From 1bf03889c5e34420001e72b5d26cc0846dcd122a Mon Sep 17 00:00:00 2001 From: Jan Wojciechowski <96974442+yanvoi@users.noreply.github.com> Date: Sun, 19 Feb 2023 23:14:01 +0100 Subject: [PATCH 001/757] Update bogo_sort.py (#8144) --- sorts/bogo_sort.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/sorts/bogo_sort.py b/sorts/bogo_sort.py index b72f2089f3d2..9c133f0d8a55 100644 --- a/sorts/bogo_sort.py +++ b/sorts/bogo_sort.py @@ -31,8 +31,6 @@ def bogo_sort(collection): """ def is_sorted(collection): - if len(collection) < 2: - return True for i in range(len(collection) - 1): if collection[i] > collection[i + 1]: return False From 67676c3b790d9631ea99c89f71dc2bf65e9aa2ca Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 21 Feb 2023 08:33:44 +0100 Subject: [PATCH 002/757] [pre-commit.ci] pre-commit autoupdate (#8149) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/tox-dev/pyproject-fmt: 0.8.0 → 0.9.1](https://github.com/tox-dev/pyproject-fmt/compare/0.8.0...0.9.1) - [github.com/pre-commit/mirrors-mypy: v1.0.0 → v1.0.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.0.0...v1.0.1) * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 4 ++-- pyproject.toml | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a1496984f950..93064949e194 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -27,7 +27,7 @@ repos: - --profile=black - repo: https://github.com/tox-dev/pyproject-fmt - rev: "0.8.0" + rev: "0.9.1" hooks: - id: pyproject-fmt @@ -62,7 +62,7 @@ repos: *flake8-plugins - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.0.0 + rev: v1.0.1 hooks: - id: mypy args: diff --git a/pyproject.toml b/pyproject.toml index 410e7655b2b5..5f9b1aa06c0e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,6 @@ addopts = [ "--showlocals", ] - [tool.coverage.report] omit = [".env/*"] sort = "Cover" From 1c15cdff70893bc27ced2b390959e1d9cc493628 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 27 Feb 2023 23:08:40 +0100 Subject: [PATCH 003/757] [pre-commit.ci] pre-commit autoupdate (#8160) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/tox-dev/pyproject-fmt: 0.9.1 → 0.9.2](https://github.com/tox-dev/pyproject-fmt/compare/0.9.1...0.9.2) * pre-commit: Add ruff --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- .pre-commit-config.yaml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 93064949e194..9f27f985bb6a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -27,7 +27,7 @@ repos: - --profile=black - repo: https://github.com/tox-dev/pyproject-fmt - rev: "0.9.1" + rev: "0.9.2" hooks: - id: pyproject-fmt @@ -43,6 +43,13 @@ repos: args: - --py311-plus + - repo: https://github.com/charliermarsh/ruff-pre-commit + rev: v0.0.253 + hooks: + - id: ruff + args: + - --ignore=E741 + - repo: https://github.com/PyCQA/flake8 rev: 6.0.0 hooks: From 64543faa980b526f79d287a073ebb7554749faf9 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Wed, 1 Mar 2023 17:23:33 +0100 Subject: [PATCH 004/757] Make some ruff fixes (#8154) * Make some ruff fixes * Undo manual fix * Undo manual fix * Updates from ruff=0.0.251 --- audio_filters/iir_filter.py | 2 +- backtracking/n_queens_math.py | 6 +++--- backtracking/sum_of_subsets.py | 2 +- ciphers/bifid.py | 2 +- ciphers/diffie_hellman.py | 16 ++++++++-------- ciphers/polybius.py | 2 +- ciphers/xor_cipher.py | 18 ++++++++---------- computer_vision/mosaic_augmentation.py | 2 +- .../binary_tree/binary_search_tree.py | 2 +- .../binary_tree/binary_tree_traversals.py | 4 ++-- .../binary_tree/inorder_tree_traversal_2022.py | 2 +- data_structures/binary_tree/red_black_tree.py | 5 ++--- .../hashing/number_theory/prime_numbers.py | 2 +- data_structures/heap/binomial_heap.py | 4 ++-- .../linked_list/doubly_linked_list_two.py | 2 +- .../linked_list/singly_linked_list.py | 1 + data_structures/linked_list/skip_list.py | 5 +---- .../queue/circular_queue_linked_list.py | 2 +- .../dilation_operation.py | 2 +- .../erosion_operation.py | 2 +- dynamic_programming/all_construct.py | 2 +- dynamic_programming/fizz_buzz.py | 2 +- .../longest_common_subsequence.py | 10 ++-------- .../longest_increasing_subsequence.py | 2 +- graphs/basic_graphs.py | 14 ++++++-------- graphs/check_cycle.py | 9 ++++----- graphs/connected_components.py | 2 +- graphs/dijkstra_algorithm.py | 2 +- .../edmonds_karp_multiple_source_and_sink.py | 5 ++--- graphs/frequent_pattern_graph_miner.py | 6 +++--- graphs/minimum_spanning_tree_boruvka.py | 1 + graphs/minimum_spanning_tree_prims.py | 5 +---- graphs/minimum_spanning_tree_prims2.py | 16 +++++++--------- hashes/hamming_code.py | 5 ++--- linear_algebra/src/lib.py | 7 ++++--- machine_learning/gradient_descent.py | 2 ++ machine_learning/k_means_clust.py | 4 ++-- .../sequential_minimum_optimization.py | 9 ++++----- maths/abs.py | 6 +++--- maths/binary_exp_mod.py | 2 +- maths/jaccard_similarity.py | 1 + maths/largest_of_very_large_numbers.py | 1 + maths/radix2_fft.py | 5 +---- .../back_propagation_neural_network.py | 1 + other/graham_scan.py | 7 +++---- other/nested_brackets.py | 9 ++++----- physics/hubble_parameter.py | 4 ++-- project_euler/problem_005/sol1.py | 1 + project_euler/problem_009/sol1.py | 5 ++--- project_euler/problem_014/sol2.py | 5 +---- project_euler/problem_018/solution.py | 10 ++-------- project_euler/problem_019/sol1.py | 2 +- project_euler/problem_033/sol1.py | 8 +++----- project_euler/problem_064/sol1.py | 5 ++--- project_euler/problem_067/sol1.py | 10 ++-------- project_euler/problem_109/sol1.py | 2 +- project_euler/problem_203/sol1.py | 4 ++-- scheduling/shortest_job_first.py | 11 +++++------ scripts/build_directory_md.py | 5 ++--- searches/binary_tree_traversal.py | 1 + sorts/circle_sort.py | 13 ++++++------- sorts/counting_sort.py | 2 +- sorts/msd_radix_sort.py | 2 +- sorts/quick_sort.py | 2 +- sorts/recursive_quick_sort.py | 10 +++++----- sorts/tim_sort.py | 4 ++-- strings/autocomplete_using_trie.py | 5 +---- strings/check_anagrams.py | 5 +---- strings/is_palindrome.py | 5 +---- strings/snake_case_to_camel_pascal_case.py | 2 +- web_programming/convert_number_to_words.py | 6 +++--- web_programming/instagram_crawler.py | 2 +- web_programming/open_google_results.py | 5 +---- 73 files changed, 151 insertions(+), 203 deletions(-) diff --git a/audio_filters/iir_filter.py b/audio_filters/iir_filter.py index aae320365012..bd448175f6f3 100644 --- a/audio_filters/iir_filter.py +++ b/audio_filters/iir_filter.py @@ -47,7 +47,7 @@ def set_coefficients(self, a_coeffs: list[float], b_coeffs: list[float]) -> None >>> filt.set_coefficients(a_coeffs, b_coeffs) """ if len(a_coeffs) < self.order: - a_coeffs = [1.0] + a_coeffs + a_coeffs = [1.0, *a_coeffs] if len(a_coeffs) != self.order + 1: raise ValueError( diff --git a/backtracking/n_queens_math.py b/backtracking/n_queens_math.py index 23bd1590618b..f3b08ab0a05f 100644 --- a/backtracking/n_queens_math.py +++ b/backtracking/n_queens_math.py @@ -129,9 +129,9 @@ def depth_first_search( # If it is False we call dfs function again and we update the inputs depth_first_search( - possible_board + [col], - diagonal_right_collisions + [row - col], - diagonal_left_collisions + [row + col], + [*possible_board, col], + [*diagonal_right_collisions, row - col], + [*diagonal_left_collisions, row + col], boards, n, ) diff --git a/backtracking/sum_of_subsets.py b/backtracking/sum_of_subsets.py index 128e290718cd..c5e23321cb0c 100644 --- a/backtracking/sum_of_subsets.py +++ b/backtracking/sum_of_subsets.py @@ -44,7 +44,7 @@ def create_state_space_tree( nums, max_sum, index + 1, - path + [nums[index]], + [*path, nums[index]], result, remaining_nums_sum - nums[index], ) diff --git a/ciphers/bifid.py b/ciphers/bifid.py index c005e051a6ba..a15b381640aa 100644 --- a/ciphers/bifid.py +++ b/ciphers/bifid.py @@ -33,7 +33,7 @@ def letter_to_numbers(self, letter: str) -> np.ndarray: >>> np.array_equal(BifidCipher().letter_to_numbers('u'), [4,5]) True """ - index1, index2 = np.where(self.SQUARE == letter) + index1, index2 = np.where(letter == self.SQUARE) indexes = np.concatenate([index1 + 1, index2 + 1]) return indexes diff --git a/ciphers/diffie_hellman.py b/ciphers/diffie_hellman.py index 072f4aaaa6da..cd40a6b9c3b3 100644 --- a/ciphers/diffie_hellman.py +++ b/ciphers/diffie_hellman.py @@ -228,10 +228,10 @@ def generate_public_key(self) -> str: def is_valid_public_key(self, key: int) -> bool: # check if the other public key is valid based on NIST SP800-56 - if 2 <= key and key <= self.prime - 2: - if pow(key, (self.prime - 1) // 2, self.prime) == 1: - return True - return False + return ( + 2 <= key <= self.prime - 2 + and pow(key, (self.prime - 1) // 2, self.prime) == 1 + ) def generate_shared_key(self, other_key_str: str) -> str: other_key = int(other_key_str, base=16) @@ -243,10 +243,10 @@ def generate_shared_key(self, other_key_str: str) -> str: @staticmethod def is_valid_public_key_static(remote_public_key_str: int, prime: int) -> bool: # check if the other public key is valid based on NIST SP800-56 - if 2 <= remote_public_key_str and remote_public_key_str <= prime - 2: - if pow(remote_public_key_str, (prime - 1) // 2, prime) == 1: - return True - return False + return ( + 2 <= remote_public_key_str <= prime - 2 + and pow(remote_public_key_str, (prime - 1) // 2, prime) == 1 + ) @staticmethod def generate_shared_key_static( diff --git a/ciphers/polybius.py b/ciphers/polybius.py index 3539ab70c303..d83badf4ac0a 100644 --- a/ciphers/polybius.py +++ b/ciphers/polybius.py @@ -31,7 +31,7 @@ def letter_to_numbers(self, letter: str) -> np.ndarray: >>> np.array_equal(PolybiusCipher().letter_to_numbers('u'), [4,5]) True """ - index1, index2 = np.where(self.SQUARE == letter) + index1, index2 = np.where(letter == self.SQUARE) indexes = np.concatenate([index1 + 1, index2 + 1]) return indexes diff --git a/ciphers/xor_cipher.py b/ciphers/xor_cipher.py index 379ef0ef7e50..0f369e38f85f 100644 --- a/ciphers/xor_cipher.py +++ b/ciphers/xor_cipher.py @@ -128,11 +128,10 @@ def encrypt_file(self, file: str, key: int = 0) -> bool: assert isinstance(file, str) and isinstance(key, int) try: - with open(file) as fin: - with open("encrypt.out", "w+") as fout: - # actual encrypt-process - for line in fin: - fout.write(self.encrypt_string(line, key)) + with open(file) as fin, open("encrypt.out", "w+") as fout: + # actual encrypt-process + for line in fin: + fout.write(self.encrypt_string(line, key)) except OSError: return False @@ -152,11 +151,10 @@ def decrypt_file(self, file: str, key: int) -> bool: assert isinstance(file, str) and isinstance(key, int) try: - with open(file) as fin: - with open("decrypt.out", "w+") as fout: - # actual encrypt-process - for line in fin: - fout.write(self.decrypt_string(line, key)) + with open(file) as fin, open("decrypt.out", "w+") as fout: + # actual encrypt-process + for line in fin: + fout.write(self.decrypt_string(line, key)) except OSError: return False diff --git a/computer_vision/mosaic_augmentation.py b/computer_vision/mosaic_augmentation.py index e2953749753f..c150126d6bfb 100644 --- a/computer_vision/mosaic_augmentation.py +++ b/computer_vision/mosaic_augmentation.py @@ -159,7 +159,7 @@ def update_image_and_anno( new_anno.append([bbox[0], xmin, ymin, xmax, ymax]) # Remove bounding box small than scale of filter - if 0 < filter_scale: + if filter_scale > 0: new_anno = [ anno for anno in new_anno diff --git a/data_structures/binary_tree/binary_search_tree.py b/data_structures/binary_tree/binary_search_tree.py index fc512944eb50..cd88cc10e697 100644 --- a/data_structures/binary_tree/binary_search_tree.py +++ b/data_structures/binary_tree/binary_search_tree.py @@ -60,7 +60,7 @@ def __insert(self, value) -> None: else: # Tree is not empty parent_node = self.root # from root if parent_node is None: - return None + return while True: # While we don't get to a leaf if value < parent_node.value: # We go left if parent_node.left is None: diff --git a/data_structures/binary_tree/binary_tree_traversals.py b/data_structures/binary_tree/binary_tree_traversals.py index 24dd1bd8cdc8..71a895e76ce4 100644 --- a/data_structures/binary_tree/binary_tree_traversals.py +++ b/data_structures/binary_tree/binary_tree_traversals.py @@ -37,7 +37,7 @@ def preorder(root: Node | None) -> list[int]: >>> preorder(make_tree()) [1, 2, 4, 5, 3] """ - return [root.data] + preorder(root.left) + preorder(root.right) if root else [] + return [root.data, *preorder(root.left), *preorder(root.right)] if root else [] def postorder(root: Node | None) -> list[int]: @@ -55,7 +55,7 @@ def inorder(root: Node | None) -> list[int]: >>> inorder(make_tree()) [4, 2, 5, 1, 3] """ - return inorder(root.left) + [root.data] + inorder(root.right) if root else [] + return [*inorder(root.left), root.data, *inorder(root.right)] if root else [] def height(root: Node | None) -> int: diff --git a/data_structures/binary_tree/inorder_tree_traversal_2022.py b/data_structures/binary_tree/inorder_tree_traversal_2022.py index e94ba7013a82..1357527d2953 100644 --- a/data_structures/binary_tree/inorder_tree_traversal_2022.py +++ b/data_structures/binary_tree/inorder_tree_traversal_2022.py @@ -50,7 +50,7 @@ def inorder(node: None | BinaryTreeNode) -> list[int]: # if node is None,return """ if node: inorder_array = inorder(node.left_child) - inorder_array = inorder_array + [node.data] + inorder_array = [*inorder_array, node.data] inorder_array = inorder_array + inorder(node.right_child) else: inorder_array = [] diff --git a/data_structures/binary_tree/red_black_tree.py b/data_structures/binary_tree/red_black_tree.py index a9dbd699c3c1..b50d75d33689 100644 --- a/data_structures/binary_tree/red_black_tree.py +++ b/data_structures/binary_tree/red_black_tree.py @@ -319,9 +319,8 @@ def check_coloring(self) -> bool: """A helper function to recursively check Property 4 of a Red-Black Tree. See check_color_properties for more info. """ - if self.color == 1: - if color(self.left) == 1 or color(self.right) == 1: - return False + if self.color == 1 and 1 in (color(self.left), color(self.right)): + return False if self.left and not self.left.check_coloring(): return False if self.right and not self.right.check_coloring(): diff --git a/data_structures/hashing/number_theory/prime_numbers.py b/data_structures/hashing/number_theory/prime_numbers.py index b88ab76ecc23..0c25896f9880 100644 --- a/data_structures/hashing/number_theory/prime_numbers.py +++ b/data_structures/hashing/number_theory/prime_numbers.py @@ -52,7 +52,7 @@ def next_prime(value, factor=1, **kwargs): first_value_val = value while not is_prime(value): - value += 1 if not ("desc" in kwargs.keys() and kwargs["desc"] is True) else -1 + value += 1 if not ("desc" in kwargs and kwargs["desc"] is True) else -1 if value == first_value_val: return next_prime(value + 1, **kwargs) diff --git a/data_structures/heap/binomial_heap.py b/data_structures/heap/binomial_heap.py index 2e05c5c80a22..099bd2871023 100644 --- a/data_structures/heap/binomial_heap.py +++ b/data_structures/heap/binomial_heap.py @@ -136,12 +136,12 @@ def merge_heaps(self, other): # Empty heaps corner cases if other.size == 0: - return + return None if self.size == 0: self.size = other.size self.bottom_root = other.bottom_root self.min_node = other.min_node - return + return None # Update size self.size = self.size + other.size diff --git a/data_structures/linked_list/doubly_linked_list_two.py b/data_structures/linked_list/doubly_linked_list_two.py index c19309c9f5a7..e993cc5a20af 100644 --- a/data_structures/linked_list/doubly_linked_list_two.py +++ b/data_structures/linked_list/doubly_linked_list_two.py @@ -128,7 +128,7 @@ def insert_at_position(self, position: int, value: int) -> None: while node: if current_position == position: self.insert_before_node(node, new_node) - return None + return current_position += 1 node = node.next self.insert_after_node(self.tail, new_node) diff --git a/data_structures/linked_list/singly_linked_list.py b/data_structures/linked_list/singly_linked_list.py index 3e52c7e43cf5..bdeb5922ac67 100644 --- a/data_structures/linked_list/singly_linked_list.py +++ b/data_structures/linked_list/singly_linked_list.py @@ -107,6 +107,7 @@ def __getitem__(self, index: int) -> Any: for i, node in enumerate(self): if i == index: return node + return None # Used to change the data of a particular node def __setitem__(self, index: int, data: Any) -> None: diff --git a/data_structures/linked_list/skip_list.py b/data_structures/linked_list/skip_list.py index 96b0db7c896b..4413c53e520e 100644 --- a/data_structures/linked_list/skip_list.py +++ b/data_structures/linked_list/skip_list.py @@ -388,10 +388,7 @@ def traverse_keys(node): def test_iter_always_yields_sorted_values(): def is_sorted(lst): - for item, next_item in zip(lst, lst[1:]): - if next_item < item: - return False - return True + return all(next_item >= item for item, next_item in zip(lst, lst[1:])) skip_list = SkipList() for i in range(10): diff --git a/data_structures/queue/circular_queue_linked_list.py b/data_structures/queue/circular_queue_linked_list.py index e8c2b8bffc06..62042c4bce96 100644 --- a/data_structures/queue/circular_queue_linked_list.py +++ b/data_structures/queue/circular_queue_linked_list.py @@ -127,7 +127,7 @@ def dequeue(self) -> Any: """ self.check_can_perform_operation() if self.rear is None or self.front is None: - return + return None if self.front == self.rear: data = self.front.data self.front.data = None diff --git a/digital_image_processing/morphological_operations/dilation_operation.py b/digital_image_processing/morphological_operations/dilation_operation.py index 274880b0a50a..c8380737d219 100644 --- a/digital_image_processing/morphological_operations/dilation_operation.py +++ b/digital_image_processing/morphological_operations/dilation_operation.py @@ -32,7 +32,7 @@ def gray2binary(gray: np.array) -> np.array: [False, True, False], [False, True, False]]) """ - return (127 < gray) & (gray <= 255) + return (gray > 127) & (gray <= 255) def dilation(image: np.array, kernel: np.array) -> np.array: diff --git a/digital_image_processing/morphological_operations/erosion_operation.py b/digital_image_processing/morphological_operations/erosion_operation.py index 4b0a5eee8c03..c2cde2ea6990 100644 --- a/digital_image_processing/morphological_operations/erosion_operation.py +++ b/digital_image_processing/morphological_operations/erosion_operation.py @@ -32,7 +32,7 @@ def gray2binary(gray: np.array) -> np.array: [False, True, False], [False, True, False]]) """ - return (127 < gray) & (gray <= 255) + return (gray > 127) & (gray <= 255) def erosion(image: np.array, kernel: np.array) -> np.array: diff --git a/dynamic_programming/all_construct.py b/dynamic_programming/all_construct.py index 3839d01e6db0..6e53a702cbb1 100644 --- a/dynamic_programming/all_construct.py +++ b/dynamic_programming/all_construct.py @@ -34,7 +34,7 @@ def all_construct(target: str, word_bank: list[str] | None = None) -> list[list[ # slice condition if target[i : i + len(word)] == word: new_combinations: list[list[str]] = [ - [word] + way for way in table[i] + [word, *way] for way in table[i] ] # adds the word to every combination the current position holds # now,push that combination to the table[i+len(word)] diff --git a/dynamic_programming/fizz_buzz.py b/dynamic_programming/fizz_buzz.py index e77ab3de7b4b..e29116437a93 100644 --- a/dynamic_programming/fizz_buzz.py +++ b/dynamic_programming/fizz_buzz.py @@ -49,7 +49,7 @@ def fizz_buzz(number: int, iterations: int) -> str: out += "Fizz" if number % 5 == 0: out += "Buzz" - if not number % 3 == 0 and not number % 5 == 0: + if 0 not in (number % 3, number % 5): out += str(number) # print(out) diff --git a/dynamic_programming/longest_common_subsequence.py b/dynamic_programming/longest_common_subsequence.py index 3468fd87da8d..178b4169b213 100644 --- a/dynamic_programming/longest_common_subsequence.py +++ b/dynamic_programming/longest_common_subsequence.py @@ -42,20 +42,14 @@ def longest_common_subsequence(x: str, y: str): for i in range(1, m + 1): for j in range(1, n + 1): - if x[i - 1] == y[j - 1]: - match = 1 - else: - match = 0 + match = 1 if x[i - 1] == y[j - 1] else 0 l[i][j] = max(l[i - 1][j], l[i][j - 1], l[i - 1][j - 1] + match) seq = "" i, j = m, n while i > 0 and j > 0: - if x[i - 1] == y[j - 1]: - match = 1 - else: - match = 0 + match = 1 if x[i - 1] == y[j - 1] else 0 if l[i][j] == l[i - 1][j - 1] + match: if match == 1: diff --git a/dynamic_programming/longest_increasing_subsequence.py b/dynamic_programming/longest_increasing_subsequence.py index 6feed23529f1..d827893763c5 100644 --- a/dynamic_programming/longest_increasing_subsequence.py +++ b/dynamic_programming/longest_increasing_subsequence.py @@ -48,7 +48,7 @@ def longest_subsequence(array: list[int]) -> list[int]: # This function is recu i += 1 temp_array = [element for element in array[1:] if element >= pivot] - temp_array = [pivot] + longest_subsequence(temp_array) + temp_array = [pivot, *longest_subsequence(temp_array)] if len(temp_array) > len(longest_subseq): return temp_array else: diff --git a/graphs/basic_graphs.py b/graphs/basic_graphs.py index 298a97bf0e17..065b6185c123 100644 --- a/graphs/basic_graphs.py +++ b/graphs/basic_graphs.py @@ -139,10 +139,9 @@ def dijk(g, s): u = i known.add(u) for v in g[u]: - if v[0] not in known: - if dist[u] + v[1] < dist.get(v[0], 100000): - dist[v[0]] = dist[u] + v[1] - path[v[0]] = u + if v[0] not in known and dist[u] + v[1] < dist.get(v[0], 100000): + dist[v[0]] = dist[u] + v[1] + path[v[0]] = u for i in dist: if i != s: print(dist[i]) @@ -243,10 +242,9 @@ def prim(g, s): u = i known.add(u) for v in g[u]: - if v[0] not in known: - if v[1] < dist.get(v[0], 100000): - dist[v[0]] = v[1] - path[v[0]] = u + if v[0] not in known and v[1] < dist.get(v[0], 100000): + dist[v[0]] = v[1] + path[v[0]] = u return dist diff --git a/graphs/check_cycle.py b/graphs/check_cycle.py index dcc864988ca5..9fd1cd80f116 100644 --- a/graphs/check_cycle.py +++ b/graphs/check_cycle.py @@ -15,11 +15,10 @@ def check_cycle(graph: dict) -> bool: visited: set[int] = set() # To detect a back edge, keep track of vertices currently in the recursion stack rec_stk: set[int] = set() - for node in graph: - if node not in visited: - if depth_first_search(graph, node, visited, rec_stk): - return True - return False + return any( + node not in visited and depth_first_search(graph, node, visited, rec_stk) + for node in graph + ) def depth_first_search(graph: dict, vertex: int, visited: set, rec_stk: set) -> bool: diff --git a/graphs/connected_components.py b/graphs/connected_components.py index 4af7803d74a7..15c7633e13e8 100644 --- a/graphs/connected_components.py +++ b/graphs/connected_components.py @@ -27,7 +27,7 @@ def dfs(graph: dict, vert: int, visited: list) -> list: if not visited[neighbour]: connected_verts += dfs(graph, neighbour, visited) - return [vert] + connected_verts + return [vert, *connected_verts] def connected_components(graph: dict) -> list: diff --git a/graphs/dijkstra_algorithm.py b/graphs/dijkstra_algorithm.py index 1845dad05db2..452138fe904b 100644 --- a/graphs/dijkstra_algorithm.py +++ b/graphs/dijkstra_algorithm.py @@ -112,7 +112,7 @@ def dijkstra(self, src): self.dist[src] = 0 q = PriorityQueue() q.insert((0, src)) # (dist from src, node) - for u in self.adjList.keys(): + for u in self.adjList: if u != src: self.dist[u] = sys.maxsize # Infinity self.par[u] = -1 diff --git a/graphs/edmonds_karp_multiple_source_and_sink.py b/graphs/edmonds_karp_multiple_source_and_sink.py index 070d758e63b6..d0610804109f 100644 --- a/graphs/edmonds_karp_multiple_source_and_sink.py +++ b/graphs/edmonds_karp_multiple_source_and_sink.py @@ -163,9 +163,8 @@ def relabel(self, vertex_index): self.graph[vertex_index][to_index] - self.preflow[vertex_index][to_index] > 0 - ): - if min_height is None or self.heights[to_index] < min_height: - min_height = self.heights[to_index] + ) and (min_height is None or self.heights[to_index] < min_height): + min_height = self.heights[to_index] if min_height is not None: self.heights[vertex_index] = min_height + 1 diff --git a/graphs/frequent_pattern_graph_miner.py b/graphs/frequent_pattern_graph_miner.py index 87d5605a0bc8..208e57f9b32f 100644 --- a/graphs/frequent_pattern_graph_miner.py +++ b/graphs/frequent_pattern_graph_miner.py @@ -130,11 +130,11 @@ def create_edge(nodes, graph, cluster, c1): """ create edge between the nodes """ - for i in cluster[c1].keys(): + for i in cluster[c1]: count = 0 c2 = c1 + 1 while c2 < max(cluster.keys()): - for j in cluster[c2].keys(): + for j in cluster[c2]: """ creates edge only if the condition satisfies """ @@ -185,7 +185,7 @@ def find_freq_subgraph_given_support(s, cluster, graph): find edges of multiple frequent subgraphs """ k = int(s / 100 * (len(cluster) - 1)) - for i in cluster[k].keys(): + for i in cluster[k]: my_dfs(graph, tuple(cluster[k][i]), (["Header"],)) diff --git a/graphs/minimum_spanning_tree_boruvka.py b/graphs/minimum_spanning_tree_boruvka.py index 663d8e26cfad..3c6888037948 100644 --- a/graphs/minimum_spanning_tree_boruvka.py +++ b/graphs/minimum_spanning_tree_boruvka.py @@ -144,6 +144,7 @@ def union(self, item1, item2): self.rank[root1] += 1 self.parent[root2] = root1 return root1 + return None @staticmethod def boruvka_mst(graph): diff --git a/graphs/minimum_spanning_tree_prims.py b/graphs/minimum_spanning_tree_prims.py index f577866f0da6..5a08ec57ff4d 100644 --- a/graphs/minimum_spanning_tree_prims.py +++ b/graphs/minimum_spanning_tree_prims.py @@ -44,10 +44,7 @@ def bottom_to_top(self, val, index, heap, position): temp = position[index] while index != 0: - if index % 2 == 0: - parent = int((index - 2) / 2) - else: - parent = int((index - 1) / 2) + parent = int((index - 2) / 2) if index % 2 == 0 else int((index - 1) / 2) if val < heap[parent]: heap[index] = heap[parent] diff --git a/graphs/minimum_spanning_tree_prims2.py b/graphs/minimum_spanning_tree_prims2.py index 707be783d087..81f30ef615fe 100644 --- a/graphs/minimum_spanning_tree_prims2.py +++ b/graphs/minimum_spanning_tree_prims2.py @@ -135,14 +135,14 @@ def _bubble_up(self, elem: T) -> None: # only] curr_pos = self.position_map[elem] if curr_pos == 0: - return + return None parent_position = get_parent_position(curr_pos) _, weight = self.heap[curr_pos] _, parent_weight = self.heap[parent_position] if parent_weight > weight: self._swap_nodes(parent_position, curr_pos) return self._bubble_up(elem) - return + return None def _bubble_down(self, elem: T) -> None: # Place a node at the proper position (downward movement) [to be used @@ -154,24 +154,22 @@ def _bubble_down(self, elem: T) -> None: if child_left_position < self.elements and child_right_position < self.elements: _, child_left_weight = self.heap[child_left_position] _, child_right_weight = self.heap[child_right_position] - if child_right_weight < child_left_weight: - if child_right_weight < weight: - self._swap_nodes(child_right_position, curr_pos) - return self._bubble_down(elem) + if child_right_weight < child_left_weight and child_right_weight < weight: + self._swap_nodes(child_right_position, curr_pos) + return self._bubble_down(elem) if child_left_position < self.elements: _, child_left_weight = self.heap[child_left_position] if child_left_weight < weight: self._swap_nodes(child_left_position, curr_pos) return self._bubble_down(elem) else: - return + return None if child_right_position < self.elements: _, child_right_weight = self.heap[child_right_position] if child_right_weight < weight: self._swap_nodes(child_right_position, curr_pos) return self._bubble_down(elem) - else: - return + return None def _swap_nodes(self, node1_pos: int, node2_pos: int) -> None: # Swap the nodes at the given positions diff --git a/hashes/hamming_code.py b/hashes/hamming_code.py index 481a6750773a..dc93032183e0 100644 --- a/hashes/hamming_code.py +++ b/hashes/hamming_code.py @@ -126,9 +126,8 @@ def emitter_converter(size_par, data): aux = (bin_pos[cont_loop])[-1 * (bp)] except IndexError: aux = "0" - if aux == "1": - if x == "1": - cont_bo += 1 + if aux == "1" and x == "1": + cont_bo += 1 cont_loop += 1 parity.append(cont_bo % 2) diff --git a/linear_algebra/src/lib.py b/linear_algebra/src/lib.py index ac0398a31a07..e3556e74c3f3 100644 --- a/linear_algebra/src/lib.py +++ b/linear_algebra/src/lib.py @@ -108,7 +108,7 @@ def __mul__(self, other: float | Vector) -> float | Vector: mul implements the scalar multiplication and the dot-product """ - if isinstance(other, float) or isinstance(other, int): + if isinstance(other, (float, int)): ans = [c * other for c in self.__components] return Vector(ans) elif isinstance(other, Vector) and len(self) == len(other): @@ -216,7 +216,7 @@ def axpy(scalar: float, x: Vector, y: Vector) -> Vector: assert ( isinstance(x, Vector) and isinstance(y, Vector) - and (isinstance(scalar, int) or isinstance(scalar, float)) + and (isinstance(scalar, (int, float))) ) return x * scalar + y @@ -337,12 +337,13 @@ def __mul__(self, other: float | Vector) -> Vector | Matrix: "vector must have the same size as the " "number of columns of the matrix!" ) - elif isinstance(other, int) or isinstance(other, float): # matrix-scalar + elif isinstance(other, (int, float)): # matrix-scalar matrix = [ [self.__matrix[i][j] * other for j in range(self.__width)] for i in range(self.__height) ] return Matrix(matrix, self.__width, self.__height) + return None def height(self) -> int: """ diff --git a/machine_learning/gradient_descent.py b/machine_learning/gradient_descent.py index 9fa460a07562..5b74dad082e7 100644 --- a/machine_learning/gradient_descent.py +++ b/machine_learning/gradient_descent.py @@ -55,6 +55,7 @@ def output(example_no, data_set): return train_data[example_no][1] elif data_set == "test": return test_data[example_no][1] + return None def calculate_hypothesis_value(example_no, data_set): @@ -68,6 +69,7 @@ def calculate_hypothesis_value(example_no, data_set): return _hypothesis_value(train_data[example_no][0]) elif data_set == "test": return _hypothesis_value(test_data[example_no][0]) + return None def summation_of_cost_derivative(index, end=m): diff --git a/machine_learning/k_means_clust.py b/machine_learning/k_means_clust.py index b6305469ed7d..7c8142aab878 100644 --- a/machine_learning/k_means_clust.py +++ b/machine_learning/k_means_clust.py @@ -229,7 +229,7 @@ def report_generator( """ # Fill missing values with given rules if fill_missing_report: - df.fillna(value=fill_missing_report, inplace=True) + df = df.fillna(value=fill_missing_report) df["dummy"] = 1 numeric_cols = df.select_dtypes(np.number).columns report = ( @@ -338,7 +338,7 @@ def report_generator( ) report.columns.name = "" report = report.reset_index() - report.drop(columns=["index"], inplace=True) + report = report.drop(columns=["index"]) return report diff --git a/machine_learning/sequential_minimum_optimization.py b/machine_learning/sequential_minimum_optimization.py index 9c45c351272f..37172c8e9bf6 100644 --- a/machine_learning/sequential_minimum_optimization.py +++ b/machine_learning/sequential_minimum_optimization.py @@ -129,7 +129,7 @@ def fit(self): # error self._unbound = [i for i in self._all_samples if self._is_unbound(i)] for s in self.unbound: - if s == i1 or s == i2: + if s in (i1, i2): continue self._error[s] += ( y1 * (a1_new - a1) * k(i1, s) @@ -225,7 +225,7 @@ def _predict(self, sample): def _choose_alphas(self): locis = yield from self._choose_a1() if not locis: - return + return None return locis def _choose_a1(self): @@ -423,9 +423,8 @@ def _rbf(self, v1, v2): return np.exp(-1 * (self.gamma * np.linalg.norm(v1 - v2) ** 2)) def _check(self): - if self._kernel == self._rbf: - if self.gamma < 0: - raise ValueError("gamma value must greater than 0") + if self._kernel == self._rbf and self.gamma < 0: + raise ValueError("gamma value must greater than 0") def _get_kernel(self, kernel_name): maps = {"linear": self._linear, "poly": self._polynomial, "rbf": self._rbf} diff --git a/maths/abs.py b/maths/abs.py index cb0ffc8a5b61..b357e98d8680 100644 --- a/maths/abs.py +++ b/maths/abs.py @@ -75,9 +75,9 @@ def test_abs_val(): """ >>> test_abs_val() """ - assert 0 == abs_val(0) - assert 34 == abs_val(34) - assert 100000000000 == abs_val(-100000000000) + assert abs_val(0) == 0 + assert abs_val(34) == 34 + assert abs_val(-100000000000) == 100000000000 a = [-3, -1, 2, -11] assert abs_max(a) == -11 diff --git a/maths/binary_exp_mod.py b/maths/binary_exp_mod.py index 67dd1e728b18..df688892d690 100644 --- a/maths/binary_exp_mod.py +++ b/maths/binary_exp_mod.py @@ -6,7 +6,7 @@ def bin_exp_mod(a, n, b): 7 """ # mod b - assert not (b == 0), "This cannot accept modulo that is == 0" + assert b != 0, "This cannot accept modulo that is == 0" if n == 0: return 1 diff --git a/maths/jaccard_similarity.py b/maths/jaccard_similarity.py index eab25188b2fd..32054414c0c2 100644 --- a/maths/jaccard_similarity.py +++ b/maths/jaccard_similarity.py @@ -71,6 +71,7 @@ def jaccard_similarity(set_a, set_b, alternative_union=False): return len(intersection) / len(union) return len(intersection) / len(union) + return None if __name__ == "__main__": diff --git a/maths/largest_of_very_large_numbers.py b/maths/largest_of_very_large_numbers.py index d2dc0af18126..7e7fea004958 100644 --- a/maths/largest_of_very_large_numbers.py +++ b/maths/largest_of_very_large_numbers.py @@ -12,6 +12,7 @@ def res(x, y): return 0 elif y == 0: return 1 # any number raised to 0 is 1 + raise AssertionError("This should never happen") if __name__ == "__main__": # Main function diff --git a/maths/radix2_fft.py b/maths/radix2_fft.py index 1def58e1f226..af98f24f9538 100644 --- a/maths/radix2_fft.py +++ b/maths/radix2_fft.py @@ -80,10 +80,7 @@ def __init__(self, poly_a=None, poly_b=None): # Discrete fourier transform of A and B def __dft(self, which): - if which == "A": - dft = [[x] for x in self.polyA] - else: - dft = [[x] for x in self.polyB] + dft = [[x] for x in self.polyA] if which == "A" else [[x] for x in self.polyB] # Corner case if len(dft) <= 1: return dft[0] diff --git a/neural_network/back_propagation_neural_network.py b/neural_network/back_propagation_neural_network.py index cb47b829010c..9dd112115f5e 100644 --- a/neural_network/back_propagation_neural_network.py +++ b/neural_network/back_propagation_neural_network.py @@ -153,6 +153,7 @@ def train(self, xdata, ydata, train_round, accuracy): if mse < self.accuracy: print("----达到精度----") return mse + return None def cal_loss(self, ydata, ydata_): self.loss = np.sum(np.power((ydata - ydata_), 2)) diff --git a/other/graham_scan.py b/other/graham_scan.py index 8e83bfcf4c49..2eadb4e56668 100644 --- a/other/graham_scan.py +++ b/other/graham_scan.py @@ -125,10 +125,9 @@ def graham_scan(points: list[tuple[int, int]]) -> list[tuple[int, int]]: miny = y minx = x minidx = i - if y == miny: - if x < minx: - minx = x - minidx = i + if y == miny and x < minx: + minx = x + minidx = i # remove the lowest and the most left point from points for preparing for sort points.pop(minidx) diff --git a/other/nested_brackets.py b/other/nested_brackets.py index 3f61a4e7006c..ea48c0a5f532 100644 --- a/other/nested_brackets.py +++ b/other/nested_brackets.py @@ -24,11 +24,10 @@ def is_balanced(s): if s[i] in open_brackets: stack.append(s[i]) - elif s[i] in closed_brackets: - if len(stack) == 0 or ( - len(stack) > 0 and open_to_closed[stack.pop()] != s[i] - ): - return False + elif s[i] in closed_brackets and ( + len(stack) == 0 or (len(stack) > 0 and open_to_closed[stack.pop()] != s[i]) + ): + return False return len(stack) == 0 diff --git a/physics/hubble_parameter.py b/physics/hubble_parameter.py index 6bc62e7131c5..f7b2d28a6716 100644 --- a/physics/hubble_parameter.py +++ b/physics/hubble_parameter.py @@ -70,10 +70,10 @@ def hubble_parameter( 68.3 """ parameters = [redshift, radiation_density, matter_density, dark_energy] - if any(0 > p for p in parameters): + if any(p < 0 for p in parameters): raise ValueError("All input parameters must be positive") - if any(1 < p for p in parameters[1:4]): + if any(p > 1 for p in parameters[1:4]): raise ValueError("Relative densities cannot be greater than one") else: curvature = 1 - (matter_density + radiation_density + dark_energy) diff --git a/project_euler/problem_005/sol1.py b/project_euler/problem_005/sol1.py index f272c102d2bb..01cbd0e15ff7 100644 --- a/project_euler/problem_005/sol1.py +++ b/project_euler/problem_005/sol1.py @@ -63,6 +63,7 @@ def solution(n: int = 20) -> int: if i == 0: i = 1 return i + return None if __name__ == "__main__": diff --git a/project_euler/problem_009/sol1.py b/project_euler/problem_009/sol1.py index 1d908402b6b1..e65c9b857990 100644 --- a/project_euler/problem_009/sol1.py +++ b/project_euler/problem_009/sol1.py @@ -32,9 +32,8 @@ def solution() -> int: for a in range(300): for b in range(a + 1, 400): for c in range(b + 1, 500): - if (a + b + c) == 1000: - if (a**2) + (b**2) == (c**2): - return a * b * c + if (a + b + c) == 1000 and (a**2) + (b**2) == (c**2): + return a * b * c return -1 diff --git a/project_euler/problem_014/sol2.py b/project_euler/problem_014/sol2.py index d2a1d9f0e468..2448e652ce5b 100644 --- a/project_euler/problem_014/sol2.py +++ b/project_euler/problem_014/sol2.py @@ -34,10 +34,7 @@ def collatz_sequence_length(n: int) -> int: """Returns the Collatz sequence length for n.""" if n in COLLATZ_SEQUENCE_LENGTHS: return COLLATZ_SEQUENCE_LENGTHS[n] - if n % 2 == 0: - next_n = n // 2 - else: - next_n = 3 * n + 1 + next_n = n // 2 if n % 2 == 0 else 3 * n + 1 sequence_length = collatz_sequence_length(next_n) + 1 COLLATZ_SEQUENCE_LENGTHS[n] = sequence_length return sequence_length diff --git a/project_euler/problem_018/solution.py b/project_euler/problem_018/solution.py index 82fc3ce3c9db..70306148bb9e 100644 --- a/project_euler/problem_018/solution.py +++ b/project_euler/problem_018/solution.py @@ -48,14 +48,8 @@ def solution(): for i in range(1, len(a)): for j in range(len(a[i])): - if j != len(a[i - 1]): - number1 = a[i - 1][j] - else: - number1 = 0 - if j > 0: - number2 = a[i - 1][j - 1] - else: - number2 = 0 + number1 = a[i - 1][j] if j != len(a[i - 1]) else 0 + number2 = a[i - 1][j - 1] if j > 0 else 0 a[i][j] += max(number1, number2) return max(a[-1]) diff --git a/project_euler/problem_019/sol1.py b/project_euler/problem_019/sol1.py index ab59365843b2..0e38137d4f01 100644 --- a/project_euler/problem_019/sol1.py +++ b/project_euler/problem_019/sol1.py @@ -39,7 +39,7 @@ def solution(): while year < 2001: day += 7 - if (year % 4 == 0 and not year % 100 == 0) or (year % 400 == 0): + if (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0): if day > days_per_month[month - 1] and month != 2: month += 1 day = day - days_per_month[month - 2] diff --git a/project_euler/problem_033/sol1.py b/project_euler/problem_033/sol1.py index e0c9a058af53..32be424b6a7b 100644 --- a/project_euler/problem_033/sol1.py +++ b/project_euler/problem_033/sol1.py @@ -20,11 +20,9 @@ def is_digit_cancelling(num: int, den: int) -> bool: - if num != den: - if num % 10 == den // 10: - if (num // 10) / (den % 10) == num / den: - return True - return False + return ( + num != den and num % 10 == den // 10 and (num // 10) / (den % 10) == num / den + ) def fraction_list(digit_len: int) -> list[str]: diff --git a/project_euler/problem_064/sol1.py b/project_euler/problem_064/sol1.py index 81ebcc7b73c3..12769decc62f 100644 --- a/project_euler/problem_064/sol1.py +++ b/project_euler/problem_064/sol1.py @@ -67,9 +67,8 @@ def solution(n: int = 10000) -> int: count_odd_periods = 0 for i in range(2, n + 1): sr = sqrt(i) - if sr - floor(sr) != 0: - if continuous_fraction_period(i) % 2 == 1: - count_odd_periods += 1 + if sr - floor(sr) != 0 and continuous_fraction_period(i) % 2 == 1: + count_odd_periods += 1 return count_odd_periods diff --git a/project_euler/problem_067/sol1.py b/project_euler/problem_067/sol1.py index f20c206cca11..2b41fedc6784 100644 --- a/project_euler/problem_067/sol1.py +++ b/project_euler/problem_067/sol1.py @@ -37,14 +37,8 @@ def solution(): for i in range(1, len(a)): for j in range(len(a[i])): - if j != len(a[i - 1]): - number1 = a[i - 1][j] - else: - number1 = 0 - if j > 0: - number2 = a[i - 1][j - 1] - else: - number2 = 0 + number1 = a[i - 1][j] if j != len(a[i - 1]) else 0 + number2 = a[i - 1][j - 1] if j > 0 else 0 a[i][j] += max(number1, number2) return max(a[-1]) diff --git a/project_euler/problem_109/sol1.py b/project_euler/problem_109/sol1.py index 852f001d38af..ef145dda590b 100644 --- a/project_euler/problem_109/sol1.py +++ b/project_euler/problem_109/sol1.py @@ -65,7 +65,7 @@ def solution(limit: int = 100) -> int: >>> solution(50) 12577 """ - singles: list[int] = list(range(1, 21)) + [25] + singles: list[int] = [*list(range(1, 21)), 25] doubles: list[int] = [2 * x for x in range(1, 21)] + [50] triples: list[int] = [3 * x for x in range(1, 21)] all_values: list[int] = singles + doubles + triples + [0] diff --git a/project_euler/problem_203/sol1.py b/project_euler/problem_203/sol1.py index 713b530b6af2..da9436246a7c 100644 --- a/project_euler/problem_203/sol1.py +++ b/project_euler/problem_203/sol1.py @@ -50,8 +50,8 @@ def get_pascal_triangle_unique_coefficients(depth: int) -> set[int]: coefficients = {1} previous_coefficients = [1] for _ in range(2, depth + 1): - coefficients_begins_one = previous_coefficients + [0] - coefficients_ends_one = [0] + previous_coefficients + coefficients_begins_one = [*previous_coefficients, 0] + coefficients_ends_one = [0, *previous_coefficients] previous_coefficients = [] for x, y in zip(coefficients_begins_one, coefficients_ends_one): coefficients.add(x + y) diff --git a/scheduling/shortest_job_first.py b/scheduling/shortest_job_first.py index b3f81bfd10e7..871de8207308 100644 --- a/scheduling/shortest_job_first.py +++ b/scheduling/shortest_job_first.py @@ -36,12 +36,11 @@ def calculate_waitingtime( # Process until all processes are completed while complete != no_of_processes: for j in range(no_of_processes): - if arrival_time[j] <= increment_time: - if remaining_time[j] > 0: - if remaining_time[j] < minm: - minm = remaining_time[j] - short = j - check = True + if arrival_time[j] <= increment_time and remaining_time[j] > 0: + if remaining_time[j] < minm: + minm = remaining_time[j] + short = j + check = True if not check: increment_time += 1 diff --git a/scripts/build_directory_md.py b/scripts/build_directory_md.py index 7572ce342720..b95be9ebc254 100755 --- a/scripts/build_directory_md.py +++ b/scripts/build_directory_md.py @@ -21,9 +21,8 @@ def md_prefix(i): def print_path(old_path: str, new_path: str) -> str: old_parts = old_path.split(os.sep) for i, new_part in enumerate(new_path.split(os.sep)): - if i + 1 > len(old_parts) or old_parts[i] != new_part: - if new_part: - print(f"{md_prefix(i)} {new_part.replace('_', ' ').title()}") + if (i + 1 > len(old_parts) or old_parts[i] != new_part) and new_part: + print(f"{md_prefix(i)} {new_part.replace('_', ' ').title()}") return new_path diff --git a/searches/binary_tree_traversal.py b/searches/binary_tree_traversal.py index 66814b47883d..76e80df25a13 100644 --- a/searches/binary_tree_traversal.py +++ b/searches/binary_tree_traversal.py @@ -37,6 +37,7 @@ def build_tree(): right_node = TreeNode(int(check)) node_found.right = right_node q.put(right_node) + return None def pre_order(node: TreeNode) -> None: diff --git a/sorts/circle_sort.py b/sorts/circle_sort.py index da3c59059516..271fa1e8d58a 100644 --- a/sorts/circle_sort.py +++ b/sorts/circle_sort.py @@ -58,14 +58,13 @@ def circle_sort_util(collection: list, low: int, high: int) -> bool: left += 1 right -= 1 - if left == right: - if collection[left] > collection[right + 1]: - collection[left], collection[right + 1] = ( - collection[right + 1], - collection[left], - ) + if left == right and collection[left] > collection[right + 1]: + collection[left], collection[right + 1] = ( + collection[right + 1], + collection[left], + ) - swapped = True + swapped = True mid = low + int((high - low) / 2) left_swap = circle_sort_util(collection, low, mid) diff --git a/sorts/counting_sort.py b/sorts/counting_sort.py index 892ec5d5f344..18c4b0323dcb 100644 --- a/sorts/counting_sort.py +++ b/sorts/counting_sort.py @@ -66,7 +66,7 @@ def counting_sort_string(string): if __name__ == "__main__": # Test string sort - assert "eghhiiinrsssttt" == counting_sort_string("thisisthestring") + assert counting_sort_string("thisisthestring") == "eghhiiinrsssttt" user_input = input("Enter numbers separated by a comma:\n").strip() unsorted = [int(item) for item in user_input.split(",")] diff --git a/sorts/msd_radix_sort.py b/sorts/msd_radix_sort.py index 74ce21762906..03f84c75b9d8 100644 --- a/sorts/msd_radix_sort.py +++ b/sorts/msd_radix_sort.py @@ -147,7 +147,7 @@ def _msd_radix_sort_inplace( list_of_ints[i], list_of_ints[j] = list_of_ints[j], list_of_ints[i] j -= 1 - if not j == i: + if j != i: i += 1 _msd_radix_sort_inplace(list_of_ints, bit_position, begin_index, i) diff --git a/sorts/quick_sort.py b/sorts/quick_sort.py index 70cd19d7afe0..b79d3eac3e48 100644 --- a/sorts/quick_sort.py +++ b/sorts/quick_sort.py @@ -39,7 +39,7 @@ def quick_sort(collection: list) -> list: for element in collection[pivot_index + 1 :]: (greater if element > pivot else lesser).append(element) - return quick_sort(lesser) + [pivot] + quick_sort(greater) + return [*quick_sort(lesser), pivot, *quick_sort(greater)] if __name__ == "__main__": diff --git a/sorts/recursive_quick_sort.py b/sorts/recursive_quick_sort.py index c28a14e37ebd..c29009aca673 100644 --- a/sorts/recursive_quick_sort.py +++ b/sorts/recursive_quick_sort.py @@ -9,11 +9,11 @@ def quick_sort(data: list) -> list: if len(data) <= 1: return data else: - return ( - quick_sort([e for e in data[1:] if e <= data[0]]) - + [data[0]] - + quick_sort([e for e in data[1:] if e > data[0]]) - ) + return [ + *quick_sort([e for e in data[1:] if e <= data[0]]), + data[0], + *quick_sort([e for e in data[1:] if e > data[0]]), + ] if __name__ == "__main__": diff --git a/sorts/tim_sort.py b/sorts/tim_sort.py index c90c7e80390b..138f11c71bcc 100644 --- a/sorts/tim_sort.py +++ b/sorts/tim_sort.py @@ -32,9 +32,9 @@ def merge(left, right): return left if left[0] < right[0]: - return [left[0]] + merge(left[1:], right) + return [left[0], *merge(left[1:], right)] - return [right[0]] + merge(left, right[1:]) + return [right[0], *merge(left, right[1:])] def tim_sort(lst): diff --git a/strings/autocomplete_using_trie.py b/strings/autocomplete_using_trie.py index 758260292a30..77a3050ab15f 100644 --- a/strings/autocomplete_using_trie.py +++ b/strings/autocomplete_using_trie.py @@ -27,10 +27,7 @@ def find_word(self, prefix: str) -> tuple | list: def _elements(self, d: dict) -> tuple: result = [] for c, v in d.items(): - if c == END: - sub_result = [" "] - else: - sub_result = [c + s for s in self._elements(v)] + sub_result = [" "] if c == END else [(c + s) for s in self._elements(v)] result.extend(sub_result) return tuple(result) diff --git a/strings/check_anagrams.py b/strings/check_anagrams.py index 0d2f8091a3f0..a364b98212ad 100644 --- a/strings/check_anagrams.py +++ b/strings/check_anagrams.py @@ -38,10 +38,7 @@ def check_anagrams(first_str: str, second_str: str) -> bool: count[first_str[i]] += 1 count[second_str[i]] -= 1 - for _count in count.values(): - if _count != 0: - return False - return True + return all(_count == 0 for _count in count.values()) if __name__ == "__main__": diff --git a/strings/is_palindrome.py b/strings/is_palindrome.py index 9bf2abd98486..406aa2e8d3c3 100644 --- a/strings/is_palindrome.py +++ b/strings/is_palindrome.py @@ -30,10 +30,7 @@ def is_palindrome(s: str) -> bool: # with the help of 1st index (i==n-i-1) # where n is length of string - for i in range(end): - if s[i] != s[n - i - 1]: - return False - return True + return all(s[i] == s[n - i - 1] for i in range(end)) if __name__ == "__main__": diff --git a/strings/snake_case_to_camel_pascal_case.py b/strings/snake_case_to_camel_pascal_case.py index eaabdcb87a0f..28a28b517a01 100644 --- a/strings/snake_case_to_camel_pascal_case.py +++ b/strings/snake_case_to_camel_pascal_case.py @@ -43,7 +43,7 @@ def snake_to_camel_case(input_str: str, use_pascal: bool = False) -> str: initial_word = "" if use_pascal else words[0] - return "".join([initial_word] + capitalized_words) + return "".join([initial_word, *capitalized_words]) if __name__ == "__main__": diff --git a/web_programming/convert_number_to_words.py b/web_programming/convert_number_to_words.py index 50612dec20dd..1e293df9660c 100644 --- a/web_programming/convert_number_to_words.py +++ b/web_programming/convert_number_to_words.py @@ -63,7 +63,7 @@ def convert(number: int) -> str: current = temp_num % 10 if counter % 2 == 0: addition = "" - if counter in placevalue.keys() and current != 0: + if counter in placevalue and current != 0: addition = placevalue[counter] if counter == 2: words = singles[current] + addition + words @@ -84,12 +84,12 @@ def convert(number: int) -> str: words = teens[number % 10] + words else: addition = "" - if counter in placevalue.keys(): + if counter in placevalue: addition = placevalue[counter] words = doubles[current] + addition + words else: addition = "" - if counter in placevalue.keys(): + if counter in placevalue: if current == 0 and ((temp_num % 100) // 10) == 0: addition = "" else: diff --git a/web_programming/instagram_crawler.py b/web_programming/instagram_crawler.py index 4536257a984e..0816cd181051 100644 --- a/web_programming/instagram_crawler.py +++ b/web_programming/instagram_crawler.py @@ -105,7 +105,7 @@ def test_instagram_user(username: str = "github") -> None: import os if os.environ.get("CI"): - return None # test failing on GitHub Actions + return # test failing on GitHub Actions instagram_user = InstagramUser(username) assert instagram_user.user_data assert isinstance(instagram_user.user_data, dict) diff --git a/web_programming/open_google_results.py b/web_programming/open_google_results.py index 2685bf62114d..f61e3666dd7e 100644 --- a/web_programming/open_google_results.py +++ b/web_programming/open_google_results.py @@ -7,10 +7,7 @@ from fake_useragent import UserAgent if __name__ == "__main__": - if len(argv) > 1: - query = "%20".join(argv[1:]) - else: - query = quote(str(input("Search: "))) + query = "%20".join(argv[1:]) if len(argv) > 1 else quote(str(input("Search: "))) print("Googling.....") From 069a14b1c55112bc4f4e08571fc3c2156bb69e5a Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Thu, 2 Mar 2023 07:55:47 +0300 Subject: [PATCH 005/757] Add Project Euler problem 082 solution 1 (#6282) Update DIRECTORY.md --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 2 + project_euler/problem_082/__init__.py | 0 project_euler/problem_082/input.txt | 80 +++++++++++++++++++++++ project_euler/problem_082/sol1.py | 65 ++++++++++++++++++ project_euler/problem_082/test_matrix.txt | 5 ++ 5 files changed, 152 insertions(+) create mode 100644 project_euler/problem_082/__init__.py create mode 100644 project_euler/problem_082/input.txt create mode 100644 project_euler/problem_082/sol1.py create mode 100644 project_euler/problem_082/test_matrix.txt diff --git a/DIRECTORY.md b/DIRECTORY.md index a8786cc2591f..3d1bc967e4b5 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -918,6 +918,8 @@ * [Sol1](project_euler/problem_080/sol1.py) * Problem 081 * [Sol1](project_euler/problem_081/sol1.py) + * Problem 082 + * [Sol1](project_euler/problem_082/sol1.py) * Problem 085 * [Sol1](project_euler/problem_085/sol1.py) * Problem 086 diff --git a/project_euler/problem_082/__init__.py b/project_euler/problem_082/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_082/input.txt b/project_euler/problem_082/input.txt new file mode 100644 index 000000000000..f65322a7e541 --- /dev/null +++ b/project_euler/problem_082/input.txt @@ -0,0 +1,80 @@ +4445,2697,5115,718,2209,2212,654,4348,3079,6821,7668,3276,8874,4190,3785,2752,9473,7817,9137,496,7338,3434,7152,4355,4552,7917,7827,2460,2350,691,3514,5880,3145,7633,7199,3783,5066,7487,3285,1084,8985,760,872,8609,8051,1134,9536,5750,9716,9371,7619,5617,275,9721,2997,2698,1887,8825,6372,3014,2113,7122,7050,6775,5948,2758,1219,3539,348,7989,2735,9862,1263,8089,6401,9462,3168,2758,3748,5870 +1096,20,1318,7586,5167,2642,1443,5741,7621,7030,5526,4244,2348,4641,9827,2448,6918,5883,3737,300,7116,6531,567,5997,3971,6623,820,6148,3287,1874,7981,8424,7672,7575,6797,6717,1078,5008,4051,8795,5820,346,1851,6463,2117,6058,3407,8211,117,4822,1317,4377,4434,5925,8341,4800,1175,4173,690,8978,7470,1295,3799,8724,3509,9849,618,3320,7068,9633,2384,7175,544,6583,1908,9983,481,4187,9353,9377 +9607,7385,521,6084,1364,8983,7623,1585,6935,8551,2574,8267,4781,3834,2764,2084,2669,4656,9343,7709,2203,9328,8004,6192,5856,3555,2260,5118,6504,1839,9227,1259,9451,1388,7909,5733,6968,8519,9973,1663,5315,7571,3035,4325,4283,2304,6438,3815,9213,9806,9536,196,5542,6907,2475,1159,5820,9075,9470,2179,9248,1828,4592,9167,3713,4640,47,3637,309,7344,6955,346,378,9044,8635,7466,5036,9515,6385,9230 +7206,3114,7760,1094,6150,5182,7358,7387,4497,955,101,1478,7777,6966,7010,8417,6453,4955,3496,107,449,8271,131,2948,6185,784,5937,8001,6104,8282,4165,3642,710,2390,575,715,3089,6964,4217,192,5949,7006,715,3328,1152,66,8044,4319,1735,146,4818,5456,6451,4113,1063,4781,6799,602,1504,6245,6550,1417,1343,2363,3785,5448,4545,9371,5420,5068,4613,4882,4241,5043,7873,8042,8434,3939,9256,2187 +3620,8024,577,9997,7377,7682,1314,1158,6282,6310,1896,2509,5436,1732,9480,706,496,101,6232,7375,2207,2306,110,6772,3433,2878,8140,5933,8688,1399,2210,7332,6172,6403,7333,4044,2291,1790,2446,7390,8698,5723,3678,7104,1825,2040,140,3982,4905,4160,2200,5041,2512,1488,2268,1175,7588,8321,8078,7312,977,5257,8465,5068,3453,3096,1651,7906,253,9250,6021,8791,8109,6651,3412,345,4778,5152,4883,7505 +1074,5438,9008,2679,5397,5429,2652,3403,770,9188,4248,2493,4361,8327,9587,707,9525,5913,93,1899,328,2876,3604,673,8576,6908,7659,2544,3359,3883,5273,6587,3065,1749,3223,604,9925,6941,2823,8767,7039,3290,3214,1787,7904,3421,7137,9560,8451,2669,9219,6332,1576,5477,6755,8348,4164,4307,2984,4012,6629,1044,2874,6541,4942,903,1404,9125,5160,8836,4345,2581,460,8438,1538,5507,668,3352,2678,6942 +4295,1176,5596,1521,3061,9868,7037,7129,8933,6659,5947,5063,3653,9447,9245,2679,767,714,116,8558,163,3927,8779,158,5093,2447,5782,3967,1716,931,7772,8164,1117,9244,5783,7776,3846,8862,6014,2330,6947,1777,3112,6008,3491,1906,5952,314,4602,8994,5919,9214,3995,5026,7688,6809,5003,3128,2509,7477,110,8971,3982,8539,2980,4689,6343,5411,2992,5270,5247,9260,2269,7474,1042,7162,5206,1232,4556,4757 +510,3556,5377,1406,5721,4946,2635,7847,4251,8293,8281,6351,4912,287,2870,3380,3948,5322,3840,4738,9563,1906,6298,3234,8959,1562,6297,8835,7861,239,6618,1322,2553,2213,5053,5446,4402,6500,5182,8585,6900,5756,9661,903,5186,7687,5998,7997,8081,8955,4835,6069,2621,1581,732,9564,1082,1853,5442,1342,520,1737,3703,5321,4793,2776,1508,1647,9101,2499,6891,4336,7012,3329,3212,1442,9993,3988,4930,7706 +9444,3401,5891,9716,1228,7107,109,3563,2700,6161,5039,4992,2242,8541,7372,2067,1294,3058,1306,320,8881,5756,9326,411,8650,8824,5495,8282,8397,2000,1228,7817,2099,6473,3571,5994,4447,1299,5991,543,7874,2297,1651,101,2093,3463,9189,6872,6118,872,1008,1779,2805,9084,4048,2123,5877,55,3075,1737,9459,4535,6453,3644,108,5982,4437,5213,1340,6967,9943,5815,669,8074,1838,6979,9132,9315,715,5048 +3327,4030,7177,6336,9933,5296,2621,4785,2755,4832,2512,2118,2244,4407,2170,499,7532,9742,5051,7687,970,6924,3527,4694,5145,1306,2165,5940,2425,8910,3513,1909,6983,346,6377,4304,9330,7203,6605,3709,3346,970,369,9737,5811,4427,9939,3693,8436,5566,1977,3728,2399,3985,8303,2492,5366,9802,9193,7296,1033,5060,9144,2766,1151,7629,5169,5995,58,7619,7565,4208,1713,6279,3209,4908,9224,7409,1325,8540 +6882,1265,1775,3648,4690,959,5837,4520,5394,1378,9485,1360,4018,578,9174,2932,9890,3696,116,1723,1178,9355,7063,1594,1918,8574,7594,7942,1547,6166,7888,354,6932,4651,1010,7759,6905,661,7689,6092,9292,3845,9605,8443,443,8275,5163,7720,7265,6356,7779,1798,1754,5225,6661,1180,8024,5666,88,9153,1840,3508,1193,4445,2648,3538,6243,6375,8107,5902,5423,2520,1122,5015,6113,8859,9370,966,8673,2442 +7338,3423,4723,6533,848,8041,7921,8277,4094,5368,7252,8852,9166,2250,2801,6125,8093,5738,4038,9808,7359,9494,601,9116,4946,2702,5573,2921,9862,1462,1269,2410,4171,2709,7508,6241,7522,615,2407,8200,4189,5492,5649,7353,2590,5203,4274,710,7329,9063,956,8371,3722,4253,4785,1194,4828,4717,4548,940,983,2575,4511,2938,1827,2027,2700,1236,841,5760,1680,6260,2373,3851,1841,4968,1172,5179,7175,3509 +4420,1327,3560,2376,6260,2988,9537,4064,4829,8872,9598,3228,1792,7118,9962,9336,4368,9189,6857,1829,9863,6287,7303,7769,2707,8257,2391,2009,3975,4993,3068,9835,3427,341,8412,2134,4034,8511,6421,3041,9012,2983,7289,100,1355,7904,9186,6920,5856,2008,6545,8331,3655,5011,839,8041,9255,6524,3862,8788,62,7455,3513,5003,8413,3918,2076,7960,6108,3638,6999,3436,1441,4858,4181,1866,8731,7745,3744,1000 +356,8296,8325,1058,1277,4743,3850,2388,6079,6462,2815,5620,8495,5378,75,4324,3441,9870,1113,165,1544,1179,2834,562,6176,2313,6836,8839,2986,9454,5199,6888,1927,5866,8760,320,1792,8296,7898,6121,7241,5886,5814,2815,8336,1576,4314,3109,2572,6011,2086,9061,9403,3947,5487,9731,7281,3159,1819,1334,3181,5844,5114,9898,4634,2531,4412,6430,4262,8482,4546,4555,6804,2607,9421,686,8649,8860,7794,6672 +9870,152,1558,4963,8750,4754,6521,6256,8818,5208,5691,9659,8377,9725,5050,5343,2539,6101,1844,9700,7750,8114,5357,3001,8830,4438,199,9545,8496,43,2078,327,9397,106,6090,8181,8646,6414,7499,5450,4850,6273,5014,4131,7639,3913,6571,8534,9703,4391,7618,445,1320,5,1894,6771,7383,9191,4708,9706,6939,7937,8726,9382,5216,3685,2247,9029,8154,1738,9984,2626,9438,4167,6351,5060,29,1218,1239,4785 +192,5213,8297,8974,4032,6966,5717,1179,6523,4679,9513,1481,3041,5355,9303,9154,1389,8702,6589,7818,6336,3539,5538,3094,6646,6702,6266,2759,4608,4452,617,9406,8064,6379,444,5602,4950,1810,8391,1536,316,8714,1178,5182,5863,5110,5372,4954,1978,2971,5680,4863,2255,4630,5723,2168,538,1692,1319,7540,440,6430,6266,7712,7385,5702,620,641,3136,7350,1478,3155,2820,9109,6261,1122,4470,14,8493,2095 +1046,4301,6082,474,4974,7822,2102,5161,5172,6946,8074,9716,6586,9962,9749,5015,2217,995,5388,4402,7652,6399,6539,1349,8101,3677,1328,9612,7922,2879,231,5887,2655,508,4357,4964,3554,5930,6236,7384,4614,280,3093,9600,2110,7863,2631,6626,6620,68,1311,7198,7561,1768,5139,1431,221,230,2940,968,5283,6517,2146,1646,869,9402,7068,8645,7058,1765,9690,4152,2926,9504,2939,7504,6074,2944,6470,7859 +4659,736,4951,9344,1927,6271,8837,8711,3241,6579,7660,5499,5616,3743,5801,4682,9748,8796,779,1833,4549,8138,4026,775,4170,2432,4174,3741,7540,8017,2833,4027,396,811,2871,1150,9809,2719,9199,8504,1224,540,2051,3519,7982,7367,2761,308,3358,6505,2050,4836,5090,7864,805,2566,2409,6876,3361,8622,5572,5895,3280,441,7893,8105,1634,2929,274,3926,7786,6123,8233,9921,2674,5340,1445,203,4585,3837 +5759,338,7444,7968,7742,3755,1591,4839,1705,650,7061,2461,9230,9391,9373,2413,1213,431,7801,4994,2380,2703,6161,6878,8331,2538,6093,1275,5065,5062,2839,582,1014,8109,3525,1544,1569,8622,7944,2905,6120,1564,1839,5570,7579,1318,2677,5257,4418,5601,7935,7656,5192,1864,5886,6083,5580,6202,8869,1636,7907,4759,9082,5854,3185,7631,6854,5872,5632,5280,1431,2077,9717,7431,4256,8261,9680,4487,4752,4286 +1571,1428,8599,1230,7772,4221,8523,9049,4042,8726,7567,6736,9033,2104,4879,4967,6334,6716,3994,1269,8995,6539,3610,7667,6560,6065,874,848,4597,1711,7161,4811,6734,5723,6356,6026,9183,2586,5636,1092,7779,7923,8747,6887,7505,9909,1792,3233,4526,3176,1508,8043,720,5212,6046,4988,709,5277,8256,3642,1391,5803,1468,2145,3970,6301,7767,2359,8487,9771,8785,7520,856,1605,8972,2402,2386,991,1383,5963 +1822,4824,5957,6511,9868,4113,301,9353,6228,2881,2966,6956,9124,9574,9233,1601,7340,973,9396,540,4747,8590,9535,3650,7333,7583,4806,3593,2738,8157,5215,8472,2284,9473,3906,6982,5505,6053,7936,6074,7179,6688,1564,1103,6860,5839,2022,8490,910,7551,7805,881,7024,1855,9448,4790,1274,3672,2810,774,7623,4223,4850,6071,9975,4935,1915,9771,6690,3846,517,463,7624,4511,614,6394,3661,7409,1395,8127 +8738,3850,9555,3695,4383,2378,87,6256,6740,7682,9546,4255,6105,2000,1851,4073,8957,9022,6547,5189,2487,303,9602,7833,1628,4163,6678,3144,8589,7096,8913,5823,4890,7679,1212,9294,5884,2972,3012,3359,7794,7428,1579,4350,7246,4301,7779,7790,3294,9547,4367,3549,1958,8237,6758,3497,3250,3456,6318,1663,708,7714,6143,6890,3428,6853,9334,7992,591,6449,9786,1412,8500,722,5468,1371,108,3939,4199,2535 +7047,4323,1934,5163,4166,461,3544,2767,6554,203,6098,2265,9078,2075,4644,6641,8412,9183,487,101,7566,5622,1975,5726,2920,5374,7779,5631,3753,3725,2672,3621,4280,1162,5812,345,8173,9785,1525,955,5603,2215,2580,5261,2765,2990,5979,389,3907,2484,1232,5933,5871,3304,1138,1616,5114,9199,5072,7442,7245,6472,4760,6359,9053,7876,2564,9404,3043,9026,2261,3374,4460,7306,2326,966,828,3274,1712,3446 +3975,4565,8131,5800,4570,2306,8838,4392,9147,11,3911,7118,9645,4994,2028,6062,5431,2279,8752,2658,7836,994,7316,5336,7185,3289,1898,9689,2331,5737,3403,1124,2679,3241,7748,16,2724,5441,6640,9368,9081,5618,858,4969,17,2103,6035,8043,7475,2181,939,415,1617,8500,8253,2155,7843,7974,7859,1746,6336,3193,2617,8736,4079,6324,6645,8891,9396,5522,6103,1857,8979,3835,2475,1310,7422,610,8345,7615 +9248,5397,5686,2988,3446,4359,6634,9141,497,9176,6773,7448,1907,8454,916,1596,2241,1626,1384,2741,3649,5362,8791,7170,2903,2475,5325,6451,924,3328,522,90,4813,9737,9557,691,2388,1383,4021,1609,9206,4707,5200,7107,8104,4333,9860,5013,1224,6959,8527,1877,4545,7772,6268,621,4915,9349,5970,706,9583,3071,4127,780,8231,3017,9114,3836,7503,2383,1977,4870,8035,2379,9704,1037,3992,3642,1016,4303 +5093,138,4639,6609,1146,5565,95,7521,9077,2272,974,4388,2465,2650,722,4998,3567,3047,921,2736,7855,173,2065,4238,1048,5,6847,9548,8632,9194,5942,4777,7910,8971,6279,7253,2516,1555,1833,3184,9453,9053,6897,7808,8629,4877,1871,8055,4881,7639,1537,7701,2508,7564,5845,5023,2304,5396,3193,2955,1088,3801,6203,1748,3737,1276,13,4120,7715,8552,3047,2921,106,7508,304,1280,7140,2567,9135,5266 +6237,4607,7527,9047,522,7371,4883,2540,5867,6366,5301,1570,421,276,3361,527,6637,4861,2401,7522,5808,9371,5298,2045,5096,5447,7755,5115,7060,8529,4078,1943,1697,1764,5453,7085,960,2405,739,2100,5800,728,9737,5704,5693,1431,8979,6428,673,7540,6,7773,5857,6823,150,5869,8486,684,5816,9626,7451,5579,8260,3397,5322,6920,1879,2127,2884,5478,4977,9016,6165,6292,3062,5671,5968,78,4619,4763 +9905,7127,9390,5185,6923,3721,9164,9705,4341,1031,1046,5127,7376,6528,3248,4941,1178,7889,3364,4486,5358,9402,9158,8600,1025,874,1839,1783,309,9030,1843,845,8398,1433,7118,70,8071,2877,3904,8866,6722,4299,10,1929,5897,4188,600,1889,3325,2485,6473,4474,7444,6992,4846,6166,4441,2283,2629,4352,7775,1101,2214,9985,215,8270,9750,2740,8361,7103,5930,8664,9690,8302,9267,344,2077,1372,1880,9550 +5825,8517,7769,2405,8204,1060,3603,7025,478,8334,1997,3692,7433,9101,7294,7498,9415,5452,3850,3508,6857,9213,6807,4412,7310,854,5384,686,4978,892,8651,3241,2743,3801,3813,8588,6701,4416,6990,6490,3197,6838,6503,114,8343,5844,8646,8694,65,791,5979,2687,2621,2019,8097,1423,3644,9764,4921,3266,3662,5561,2476,8271,8138,6147,1168,3340,1998,9874,6572,9873,6659,5609,2711,3931,9567,4143,7833,8887 +6223,2099,2700,589,4716,8333,1362,5007,2753,2848,4441,8397,7192,8191,4916,9955,6076,3370,6396,6971,3156,248,3911,2488,4930,2458,7183,5455,170,6809,6417,3390,1956,7188,577,7526,2203,968,8164,479,8699,7915,507,6393,4632,1597,7534,3604,618,3280,6061,9793,9238,8347,568,9645,2070,5198,6482,5000,9212,6655,5961,7513,1323,3872,6170,3812,4146,2736,67,3151,5548,2781,9679,7564,5043,8587,1893,4531 +5826,3690,6724,2121,9308,6986,8106,6659,2142,1642,7170,2877,5757,6494,8026,6571,8387,9961,6043,9758,9607,6450,8631,8334,7359,5256,8523,2225,7487,1977,9555,8048,5763,2414,4948,4265,2427,8978,8088,8841,9208,9601,5810,9398,8866,9138,4176,5875,7212,3272,6759,5678,7649,4922,5422,1343,8197,3154,3600,687,1028,4579,2084,9467,4492,7262,7296,6538,7657,7134,2077,1505,7332,6890,8964,4879,7603,7400,5973,739 +1861,1613,4879,1884,7334,966,2000,7489,2123,4287,1472,3263,4726,9203,1040,4103,6075,6049,330,9253,4062,4268,1635,9960,577,1320,3195,9628,1030,4092,4979,6474,6393,2799,6967,8687,7724,7392,9927,2085,3200,6466,8702,265,7646,8665,7986,7266,4574,6587,612,2724,704,3191,8323,9523,3002,704,5064,3960,8209,2027,2758,8393,4875,4641,9584,6401,7883,7014,768,443,5490,7506,1852,2005,8850,5776,4487,4269 +4052,6687,4705,7260,6645,6715,3706,5504,8672,2853,1136,8187,8203,4016,871,1809,1366,4952,9294,5339,6872,2645,6083,7874,3056,5218,7485,8796,7401,3348,2103,426,8572,4163,9171,3176,948,7654,9344,3217,1650,5580,7971,2622,76,2874,880,2034,9929,1546,2659,5811,3754,7096,7436,9694,9960,7415,2164,953,2360,4194,2397,1047,2196,6827,575,784,2675,8821,6802,7972,5996,6699,2134,7577,2887,1412,4349,4380 +4629,2234,6240,8132,7592,3181,6389,1214,266,1910,2451,8784,2790,1127,6932,1447,8986,2492,5476,397,889,3027,7641,5083,5776,4022,185,3364,5701,2442,2840,4160,9525,4828,6602,2614,7447,3711,4505,7745,8034,6514,4907,2605,7753,6958,7270,6936,3006,8968,439,2326,4652,3085,3425,9863,5049,5361,8688,297,7580,8777,7916,6687,8683,7141,306,9569,2384,1500,3346,4601,7329,9040,6097,2727,6314,4501,4974,2829 +8316,4072,2025,6884,3027,1808,5714,7624,7880,8528,4205,8686,7587,3230,1139,7273,6163,6986,3914,9309,1464,9359,4474,7095,2212,7302,2583,9462,7532,6567,1606,4436,8981,5612,6796,4385,5076,2007,6072,3678,8331,1338,3299,8845,4783,8613,4071,1232,6028,2176,3990,2148,3748,103,9453,538,6745,9110,926,3125,473,5970,8728,7072,9062,1404,1317,5139,9862,6496,6062,3338,464,1600,2532,1088,8232,7739,8274,3873 +2341,523,7096,8397,8301,6541,9844,244,4993,2280,7689,4025,4196,5522,7904,6048,2623,9258,2149,9461,6448,8087,7245,1917,8340,7127,8466,5725,6996,3421,5313,512,9164,9837,9794,8369,4185,1488,7210,1524,1016,4620,9435,2478,7765,8035,697,6677,3724,6988,5853,7662,3895,9593,1185,4727,6025,5734,7665,3070,138,8469,6748,6459,561,7935,8646,2378,462,7755,3115,9690,8877,3946,2728,8793,244,6323,8666,4271 +6430,2406,8994,56,1267,3826,9443,7079,7579,5232,6691,3435,6718,5698,4144,7028,592,2627,217,734,6194,8156,9118,58,2640,8069,4127,3285,694,3197,3377,4143,4802,3324,8134,6953,7625,3598,3584,4289,7065,3434,2106,7132,5802,7920,9060,7531,3321,1725,1067,3751,444,5503,6785,7937,6365,4803,198,6266,8177,1470,6390,1606,2904,7555,9834,8667,2033,1723,5167,1666,8546,8152,473,4475,6451,7947,3062,3281 +2810,3042,7759,1741,2275,2609,7676,8640,4117,1958,7500,8048,1757,3954,9270,1971,4796,2912,660,5511,3553,1012,5757,4525,6084,7198,8352,5775,7726,8591,7710,9589,3122,4392,6856,5016,749,2285,3356,7482,9956,7348,2599,8944,495,3462,3578,551,4543,7207,7169,7796,1247,4278,6916,8176,3742,8385,2310,1345,8692,2667,4568,1770,8319,3585,4920,3890,4928,7343,5385,9772,7947,8786,2056,9266,3454,2807,877,2660 +6206,8252,5928,5837,4177,4333,207,7934,5581,9526,8906,1498,8411,2984,5198,5134,2464,8435,8514,8674,3876,599,5327,826,2152,4084,2433,9327,9697,4800,2728,3608,3849,3861,3498,9943,1407,3991,7191,9110,5666,8434,4704,6545,5944,2357,1163,4995,9619,6754,4200,9682,6654,4862,4744,5953,6632,1054,293,9439,8286,2255,696,8709,1533,1844,6441,430,1999,6063,9431,7018,8057,2920,6266,6799,356,3597,4024,6665 +3847,6356,8541,7225,2325,2946,5199,469,5450,7508,2197,9915,8284,7983,6341,3276,3321,16,1321,7608,5015,3362,8491,6968,6818,797,156,2575,706,9516,5344,5457,9210,5051,8099,1617,9951,7663,8253,9683,2670,1261,4710,1068,8753,4799,1228,2621,3275,6188,4699,1791,9518,8701,5932,4275,6011,9877,2933,4182,6059,2930,6687,6682,9771,654,9437,3169,8596,1827,5471,8909,2352,123,4394,3208,8756,5513,6917,2056 +5458,8173,3138,3290,4570,4892,3317,4251,9699,7973,1163,1935,5477,6648,9614,5655,9592,975,9118,2194,7322,8248,8413,3462,8560,1907,7810,6650,7355,2939,4973,6894,3933,3784,3200,2419,9234,4747,2208,2207,1945,2899,1407,6145,8023,3484,5688,7686,2737,3828,3704,9004,5190,9740,8643,8650,5358,4426,1522,1707,3613,9887,6956,2447,2762,833,1449,9489,2573,1080,4167,3456,6809,2466,227,7125,2759,6250,6472,8089 +3266,7025,9756,3914,1265,9116,7723,9788,6805,5493,2092,8688,6592,9173,4431,4028,6007,7131,4446,4815,3648,6701,759,3312,8355,4485,4187,5188,8746,7759,3528,2177,5243,8379,3838,7233,4607,9187,7216,2190,6967,2920,6082,7910,5354,3609,8958,6949,7731,494,8753,8707,1523,4426,3543,7085,647,6771,9847,646,5049,824,8417,5260,2730,5702,2513,9275,4279,2767,8684,1165,9903,4518,55,9682,8963,6005,2102,6523 +1998,8731,936,1479,5259,7064,4085,91,7745,7136,3773,3810,730,8255,2705,2653,9790,6807,2342,355,9344,2668,3690,2028,9679,8102,574,4318,6481,9175,5423,8062,2867,9657,7553,3442,3920,7430,3945,7639,3714,3392,2525,4995,4850,2867,7951,9667,486,9506,9888,781,8866,1702,3795,90,356,1483,4200,2131,6969,5931,486,6880,4404,1084,5169,4910,6567,8335,4686,5043,2614,3352,2667,4513,6472,7471,5720,1616 +8878,1613,1716,868,1906,2681,564,665,5995,2474,7496,3432,9491,9087,8850,8287,669,823,347,6194,2264,2592,7871,7616,8508,4827,760,2676,4660,4881,7572,3811,9032,939,4384,929,7525,8419,5556,9063,662,8887,7026,8534,3111,1454,2082,7598,5726,6687,9647,7608,73,3014,5063,670,5461,5631,3367,9796,8475,7908,5073,1565,5008,5295,4457,1274,4788,1728,338,600,8415,8535,9351,7750,6887,5845,1741,125 +3637,6489,9634,9464,9055,2413,7824,9517,7532,3577,7050,6186,6980,9365,9782,191,870,2497,8498,2218,2757,5420,6468,586,3320,9230,1034,1393,9886,5072,9391,1178,8464,8042,6869,2075,8275,3601,7715,9470,8786,6475,8373,2159,9237,2066,3264,5000,679,355,3069,4073,494,2308,5512,4334,9438,8786,8637,9774,1169,1949,6594,6072,4270,9158,7916,5752,6794,9391,6301,5842,3285,2141,3898,8027,4310,8821,7079,1307 +8497,6681,4732,7151,7060,5204,9030,7157,833,5014,8723,3207,9796,9286,4913,119,5118,7650,9335,809,3675,2597,5144,3945,5090,8384,187,4102,1260,2445,2792,4422,8389,9290,50,1765,1521,6921,8586,4368,1565,5727,7855,2003,4834,9897,5911,8630,5070,1330,7692,7557,7980,6028,5805,9090,8265,3019,3802,698,9149,5748,1965,9658,4417,5994,5584,8226,2937,272,5743,1278,5698,8736,2595,6475,5342,6596,1149,6920 +8188,8009,9546,6310,8772,2500,9846,6592,6872,3857,1307,8125,7042,1544,6159,2330,643,4604,7899,6848,371,8067,2062,3200,7295,1857,9505,6936,384,2193,2190,301,8535,5503,1462,7380,5114,4824,8833,1763,4974,8711,9262,6698,3999,2645,6937,7747,1128,2933,3556,7943,2885,3122,9105,5447,418,2899,5148,3699,9021,9501,597,4084,175,1621,1,1079,6067,5812,4326,9914,6633,5394,4233,6728,9084,1864,5863,1225 +9935,8793,9117,1825,9542,8246,8437,3331,9128,9675,6086,7075,319,1334,7932,3583,7167,4178,1726,7720,695,8277,7887,6359,5912,1719,2780,8529,1359,2013,4498,8072,1129,9998,1147,8804,9405,6255,1619,2165,7491,1,8882,7378,3337,503,5758,4109,3577,985,3200,7615,8058,5032,1080,6410,6873,5496,1466,2412,9885,5904,4406,3605,8770,4361,6205,9193,1537,9959,214,7260,9566,1685,100,4920,7138,9819,5637,976 +3466,9854,985,1078,7222,8888,5466,5379,3578,4540,6853,8690,3728,6351,7147,3134,6921,9692,857,3307,4998,2172,5783,3931,9417,2541,6299,13,787,2099,9131,9494,896,8600,1643,8419,7248,2660,2609,8579,91,6663,5506,7675,1947,6165,4286,1972,9645,3805,1663,1456,8853,5705,9889,7489,1107,383,4044,2969,3343,152,7805,4980,9929,5033,1737,9953,7197,9158,4071,1324,473,9676,3984,9680,3606,8160,7384,5432 +1005,4512,5186,3953,2164,3372,4097,3247,8697,3022,9896,4101,3871,6791,3219,2742,4630,6967,7829,5991,6134,1197,1414,8923,8787,1394,8852,5019,7768,5147,8004,8825,5062,9625,7988,1110,3992,7984,9966,6516,6251,8270,421,3723,1432,4830,6935,8095,9059,2214,6483,6846,3120,1587,6201,6691,9096,9627,6671,4002,3495,9939,7708,7465,5879,6959,6634,3241,3401,2355,9061,2611,7830,3941,2177,2146,5089,7079,519,6351 +7280,8586,4261,2831,7217,3141,9994,9940,5462,2189,4005,6942,9848,5350,8060,6665,7519,4324,7684,657,9453,9296,2944,6843,7499,7847,1728,9681,3906,6353,5529,2822,3355,3897,7724,4257,7489,8672,4356,3983,1948,6892,7415,4153,5893,4190,621,1736,4045,9532,7701,3671,1211,1622,3176,4524,9317,7800,5638,6644,6943,5463,3531,2821,1347,5958,3436,1438,2999,994,850,4131,2616,1549,3465,5946,690,9273,6954,7991 +9517,399,3249,2596,7736,2142,1322,968,7350,1614,468,3346,3265,7222,6086,1661,5317,2582,7959,4685,2807,2917,1037,5698,1529,3972,8716,2634,3301,3412,8621,743,8001,4734,888,7744,8092,3671,8941,1487,5658,7099,2781,99,1932,4443,4756,4652,9328,1581,7855,4312,5976,7255,6480,3996,2748,1973,9731,4530,2790,9417,7186,5303,3557,351,7182,9428,1342,9020,7599,1392,8304,2070,9138,7215,2008,9937,1106,7110 +7444,769,9688,632,1571,6820,8743,4338,337,3366,3073,1946,8219,104,4210,6986,249,5061,8693,7960,6546,1004,8857,5997,9352,4338,6105,5008,2556,6518,6694,4345,3727,7956,20,3954,8652,4424,9387,2035,8358,5962,5304,5194,8650,8282,1256,1103,2138,6679,1985,3653,2770,2433,4278,615,2863,1715,242,3790,2636,6998,3088,1671,2239,957,5411,4595,6282,2881,9974,2401,875,7574,2987,4587,3147,6766,9885,2965 +3287,3016,3619,6818,9073,6120,5423,557,2900,2015,8111,3873,1314,4189,1846,4399,7041,7583,2427,2864,3525,5002,2069,748,1948,6015,2684,438,770,8367,1663,7887,7759,1885,157,7770,4520,4878,3857,1137,3525,3050,6276,5569,7649,904,4533,7843,2199,5648,7628,9075,9441,3600,7231,2388,5640,9096,958,3058,584,5899,8150,1181,9616,1098,8162,6819,8171,1519,1140,7665,8801,2632,1299,9192,707,9955,2710,7314 +1772,2963,7578,3541,3095,1488,7026,2634,6015,4633,4370,2762,1650,2174,909,8158,2922,8467,4198,4280,9092,8856,8835,5457,2790,8574,9742,5054,9547,4156,7940,8126,9824,7340,8840,6574,3547,1477,3014,6798,7134,435,9484,9859,3031,4,1502,4133,1738,1807,4825,463,6343,9701,8506,9822,9555,8688,8168,3467,3234,6318,1787,5591,419,6593,7974,8486,9861,6381,6758,194,3061,4315,2863,4665,3789,2201,1492,4416 +126,8927,6608,5682,8986,6867,1715,6076,3159,788,3140,4744,830,9253,5812,5021,7616,8534,1546,9590,1101,9012,9821,8132,7857,4086,1069,7491,2988,1579,2442,4321,2149,7642,6108,250,6086,3167,24,9528,7663,2685,1220,9196,1397,5776,1577,1730,5481,977,6115,199,6326,2183,3767,5928,5586,7561,663,8649,9688,949,5913,9160,1870,5764,9887,4477,6703,1413,4995,5494,7131,2192,8969,7138,3997,8697,646,1028 +8074,1731,8245,624,4601,8706,155,8891,309,2552,8208,8452,2954,3124,3469,4246,3352,1105,4509,8677,9901,4416,8191,9283,5625,7120,2952,8881,7693,830,4580,8228,9459,8611,4499,1179,4988,1394,550,2336,6089,6872,269,7213,1848,917,6672,4890,656,1478,6536,3165,4743,4990,1176,6211,7207,5284,9730,4738,1549,4986,4942,8645,3698,9429,1439,2175,6549,3058,6513,1574,6988,8333,3406,5245,5431,7140,7085,6407 +7845,4694,2530,8249,290,5948,5509,1588,5940,4495,5866,5021,4626,3979,3296,7589,4854,1998,5627,3926,8346,6512,9608,1918,7070,4747,4182,2858,2766,4606,6269,4107,8982,8568,9053,4244,5604,102,2756,727,5887,2566,7922,44,5986,621,1202,374,6988,4130,3627,6744,9443,4568,1398,8679,397,3928,9159,367,2917,6127,5788,3304,8129,911,2669,1463,9749,264,4478,8940,1109,7309,2462,117,4692,7724,225,2312 +4164,3637,2000,941,8903,39,3443,7172,1031,3687,4901,8082,4945,4515,7204,9310,9349,9535,9940,218,1788,9245,2237,1541,5670,6538,6047,5553,9807,8101,1925,8714,445,8332,7309,6830,5786,5736,7306,2710,3034,1838,7969,6318,7912,2584,2080,7437,6705,2254,7428,820,782,9861,7596,3842,3631,8063,5240,6666,394,4565,7865,4895,9890,6028,6117,4724,9156,4473,4552,602,470,6191,4927,5387,884,3146,1978,3000 +4258,6880,1696,3582,5793,4923,2119,1155,9056,9698,6603,3768,5514,9927,9609,6166,6566,4536,4985,4934,8076,9062,6741,6163,7399,4562,2337,5600,2919,9012,8459,1308,6072,1225,9306,8818,5886,7243,7365,8792,6007,9256,6699,7171,4230,7002,8720,7839,4533,1671,478,7774,1607,2317,5437,4705,7886,4760,6760,7271,3081,2997,3088,7675,6208,3101,6821,6840,122,9633,4900,2067,8546,4549,2091,7188,5605,8599,6758,5229 +7854,5243,9155,3556,8812,7047,2202,1541,5993,4600,4760,713,434,7911,7426,7414,8729,322,803,7960,7563,4908,6285,6291,736,3389,9339,4132,8701,7534,5287,3646,592,3065,7582,2592,8755,6068,8597,1982,5782,1894,2900,6236,4039,6569,3037,5837,7698,700,7815,2491,7272,5878,3083,6778,6639,3589,5010,8313,2581,6617,5869,8402,6808,2951,2321,5195,497,2190,6187,1342,1316,4453,7740,4154,2959,1781,1482,8256 +7178,2046,4419,744,8312,5356,6855,8839,319,2962,5662,47,6307,8662,68,4813,567,2712,9931,1678,3101,8227,6533,4933,6656,92,5846,4780,6256,6361,4323,9985,1231,2175,7178,3034,9744,6155,9165,7787,5836,9318,7860,9644,8941,6480,9443,8188,5928,161,6979,2352,5628,6991,1198,8067,5867,6620,3778,8426,2994,3122,3124,6335,3918,8897,2655,9670,634,1088,1576,8935,7255,474,8166,7417,9547,2886,5560,3842 +6957,3111,26,7530,7143,1295,1744,6057,3009,1854,8098,5405,2234,4874,9447,2620,9303,27,7410,969,40,2966,5648,7596,8637,4238,3143,3679,7187,690,9980,7085,7714,9373,5632,7526,6707,3951,9734,4216,2146,3602,5371,6029,3039,4433,4855,4151,1449,3376,8009,7240,7027,4602,2947,9081,4045,8424,9352,8742,923,2705,4266,3232,2264,6761,363,2651,3383,7770,6730,7856,7340,9679,2158,610,4471,4608,910,6241 +4417,6756,1013,8797,658,8809,5032,8703,7541,846,3357,2920,9817,1745,9980,7593,4667,3087,779,3218,6233,5568,4296,2289,2654,7898,5021,9461,5593,8214,9173,4203,2271,7980,2983,5952,9992,8399,3468,1776,3188,9314,1720,6523,2933,621,8685,5483,8986,6163,3444,9539,4320,155,3992,2828,2150,6071,524,2895,5468,8063,1210,3348,9071,4862,483,9017,4097,6186,9815,3610,5048,1644,1003,9865,9332,2145,1944,2213 +9284,3803,4920,1927,6706,4344,7383,4786,9890,2010,5228,1224,3158,6967,8580,8990,8883,5213,76,8306,2031,4980,5639,9519,7184,5645,7769,3259,8077,9130,1317,3096,9624,3818,1770,695,2454,947,6029,3474,9938,3527,5696,4760,7724,7738,2848,6442,5767,6845,8323,4131,2859,7595,2500,4815,3660,9130,8580,7016,8231,4391,8369,3444,4069,4021,556,6154,627,2778,1496,4206,6356,8434,8491,3816,8231,3190,5575,1015 +3787,7572,1788,6803,5641,6844,1961,4811,8535,9914,9999,1450,8857,738,4662,8569,6679,2225,7839,8618,286,2648,5342,2294,3205,4546,176,8705,3741,6134,8324,8021,7004,5205,7032,6637,9442,5539,5584,4819,5874,5807,8589,6871,9016,983,1758,3786,1519,6241,185,8398,495,3370,9133,3051,4549,9674,7311,9738,3316,9383,2658,2776,9481,7558,619,3943,3324,6491,4933,153,9738,4623,912,3595,7771,7939,1219,4405 +2650,3883,4154,5809,315,7756,4430,1788,4451,1631,6461,7230,6017,5751,138,588,5282,2442,9110,9035,6349,2515,1570,6122,4192,4174,3530,1933,4186,4420,4609,5739,4135,2963,6308,1161,8809,8619,2796,3819,6971,8228,4188,1492,909,8048,2328,6772,8467,7671,9068,2226,7579,6422,7056,8042,3296,2272,3006,2196,7320,3238,3490,3102,37,1293,3212,4767,5041,8773,5794,4456,6174,7279,7054,2835,7053,9088,790,6640 +3101,1057,7057,3826,6077,1025,2955,1224,1114,6729,5902,4698,6239,7203,9423,1804,4417,6686,1426,6941,8071,1029,4985,9010,6122,6597,1622,1574,3513,1684,7086,5505,3244,411,9638,4150,907,9135,829,981,1707,5359,8781,9751,5,9131,3973,7159,1340,6955,7514,7993,6964,8198,1933,2797,877,3993,4453,8020,9349,8646,2779,8679,2961,3547,3374,3510,1129,3568,2241,2625,9138,5974,8206,7669,7678,1833,8700,4480 +4865,9912,8038,8238,782,3095,8199,1127,4501,7280,2112,2487,3626,2790,9432,1475,6312,8277,4827,2218,5806,7132,8752,1468,7471,6386,739,8762,8323,8120,5169,9078,9058,3370,9560,7987,8585,8531,5347,9312,1058,4271,1159,5286,5404,6925,8606,9204,7361,2415,560,586,4002,2644,1927,2824,768,4409,2942,3345,1002,808,4941,6267,7979,5140,8643,7553,9438,7320,4938,2666,4609,2778,8158,6730,3748,3867,1866,7181 +171,3771,7134,8927,4778,2913,3326,2004,3089,7853,1378,1729,4777,2706,9578,1360,5693,3036,1851,7248,2403,2273,8536,6501,9216,613,9671,7131,7719,6425,773,717,8803,160,1114,7554,7197,753,4513,4322,8499,4533,2609,4226,8710,6627,644,9666,6260,4870,5744,7385,6542,6203,7703,6130,8944,5589,2262,6803,6381,7414,6888,5123,7320,9392,9061,6780,322,8975,7050,5089,1061,2260,3199,1150,1865,5386,9699,6501 +3744,8454,6885,8277,919,1923,4001,6864,7854,5519,2491,6057,8794,9645,1776,5714,9786,9281,7538,6916,3215,395,2501,9618,4835,8846,9708,2813,3303,1794,8309,7176,2206,1602,1838,236,4593,2245,8993,4017,10,8215,6921,5206,4023,5932,6997,7801,262,7640,3107,8275,4938,7822,2425,3223,3886,2105,8700,9526,2088,8662,8034,7004,5710,2124,7164,3574,6630,9980,4242,2901,9471,1491,2117,4562,1130,9086,4117,6698 +2810,2280,2331,1170,4554,4071,8387,1215,2274,9848,6738,1604,7281,8805,439,1298,8318,7834,9426,8603,6092,7944,1309,8828,303,3157,4638,4439,9175,1921,4695,7716,1494,1015,1772,5913,1127,1952,1950,8905,4064,9890,385,9357,7945,5035,7082,5369,4093,6546,5187,5637,2041,8946,1758,7111,6566,1027,1049,5148,7224,7248,296,6169,375,1656,7993,2816,3717,4279,4675,1609,3317,42,6201,3100,3144,163,9530,4531 +7096,6070,1009,4988,3538,5801,7149,3063,2324,2912,7911,7002,4338,7880,2481,7368,3516,2016,7556,2193,1388,3865,8125,4637,4096,8114,750,3144,1938,7002,9343,4095,1392,4220,3455,6969,9647,1321,9048,1996,1640,6626,1788,314,9578,6630,2813,6626,4981,9908,7024,4355,3201,3521,3864,3303,464,1923,595,9801,3391,8366,8084,9374,1041,8807,9085,1892,9431,8317,9016,9221,8574,9981,9240,5395,2009,6310,2854,9255 +8830,3145,2960,9615,8220,6061,3452,2918,6481,9278,2297,3385,6565,7066,7316,5682,107,7646,4466,68,1952,9603,8615,54,7191,791,6833,2560,693,9733,4168,570,9127,9537,1925,8287,5508,4297,8452,8795,6213,7994,2420,4208,524,5915,8602,8330,2651,8547,6156,1812,6271,7991,9407,9804,1553,6866,1128,2119,4691,9711,8315,5879,9935,6900,482,682,4126,1041,428,6247,3720,5882,7526,2582,4327,7725,3503,2631 +2738,9323,721,7434,1453,6294,2957,3786,5722,6019,8685,4386,3066,9057,6860,499,5315,3045,5194,7111,3137,9104,941,586,3066,755,4177,8819,7040,5309,3583,3897,4428,7788,4721,7249,6559,7324,825,7311,3760,6064,6070,9672,4882,584,1365,9739,9331,5783,2624,7889,1604,1303,1555,7125,8312,425,8936,3233,7724,1480,403,7440,1784,1754,4721,1569,652,3893,4574,5692,9730,4813,9844,8291,9199,7101,3391,8914 +6044,2928,9332,3328,8588,447,3830,1176,3523,2705,8365,6136,5442,9049,5526,8575,8869,9031,7280,706,2794,8814,5767,4241,7696,78,6570,556,5083,1426,4502,3336,9518,2292,1885,3740,3153,9348,9331,8051,2759,5407,9028,7840,9255,831,515,2612,9747,7435,8964,4971,2048,4900,5967,8271,1719,9670,2810,6777,1594,6367,6259,8316,3815,1689,6840,9437,4361,822,9619,3065,83,6344,7486,8657,8228,9635,6932,4864 +8478,4777,6334,4678,7476,4963,6735,3096,5860,1405,5127,7269,7793,4738,227,9168,2996,8928,765,733,1276,7677,6258,1528,9558,3329,302,8901,1422,8277,6340,645,9125,8869,5952,141,8141,1816,9635,4025,4184,3093,83,2344,2747,9352,7966,1206,1126,1826,218,7939,2957,2729,810,8752,5247,4174,4038,8884,7899,9567,301,5265,5752,7524,4381,1669,3106,8270,6228,6373,754,2547,4240,2313,5514,3022,1040,9738 +2265,8192,1763,1369,8469,8789,4836,52,1212,6690,5257,8918,6723,6319,378,4039,2421,8555,8184,9577,1432,7139,8078,5452,9628,7579,4161,7490,5159,8559,1011,81,478,5840,1964,1334,6875,8670,9900,739,1514,8692,522,9316,6955,1345,8132,2277,3193,9773,3923,4177,2183,1236,6747,6575,4874,6003,6409,8187,745,8776,9440,7543,9825,2582,7381,8147,7236,5185,7564,6125,218,7991,6394,391,7659,7456,5128,5294 +2132,8992,8160,5782,4420,3371,3798,5054,552,5631,7546,4716,1332,6486,7892,7441,4370,6231,4579,2121,8615,1145,9391,1524,1385,2400,9437,2454,7896,7467,2928,8400,3299,4025,7458,4703,7206,6358,792,6200,725,4275,4136,7390,5984,4502,7929,5085,8176,4600,119,3568,76,9363,6943,2248,9077,9731,6213,5817,6729,4190,3092,6910,759,2682,8380,1254,9604,3011,9291,5329,9453,9746,2739,6522,3765,5634,1113,5789 +5304,5499,564,2801,679,2653,1783,3608,7359,7797,3284,796,3222,437,7185,6135,8571,2778,7488,5746,678,6140,861,7750,803,9859,9918,2425,3734,2698,9005,4864,9818,6743,2475,132,9486,3825,5472,919,292,4411,7213,7699,6435,9019,6769,1388,802,2124,1345,8493,9487,8558,7061,8777,8833,2427,2238,5409,4957,8503,3171,7622,5779,6145,2417,5873,5563,5693,9574,9491,1937,7384,4563,6842,5432,2751,3406,7981 diff --git a/project_euler/problem_082/sol1.py b/project_euler/problem_082/sol1.py new file mode 100644 index 000000000000..7b50dc887719 --- /dev/null +++ b/project_euler/problem_082/sol1.py @@ -0,0 +1,65 @@ +""" +Project Euler Problem 82: https://projecteuler.net/problem=82 + +The minimal path sum in the 5 by 5 matrix below, by starting in any cell +in the left column and finishing in any cell in the right column, +and only moving up, down, and right, is indicated in red and bold; +the sum is equal to 994. + + 131 673 [234] [103] [18] + [201] [96] [342] 965 150 + 630 803 746 422 111 + 537 699 497 121 956 + 805 732 524 37 331 + +Find the minimal path sum from the left column to the right column in matrix.txt +(https://projecteuler.net/project/resources/p082_matrix.txt) +(right click and "Save Link/Target As..."), +a 31K text file containing an 80 by 80 matrix. +""" + +import os + + +def solution(filename: str = "input.txt") -> int: + """ + Returns the minimal path sum in the matrix from the file, by starting in any cell + in the left column and finishing in any cell in the right column, + and only moving up, down, and right + + >>> solution("test_matrix.txt") + 994 + """ + + with open(os.path.join(os.path.dirname(__file__), filename)) as input_file: + matrix = [ + [int(element) for element in line.split(",")] + for line in input_file.readlines() + ] + + rows = len(matrix) + cols = len(matrix[0]) + + minimal_path_sums = [[-1 for _ in range(cols)] for _ in range(rows)] + for i in range(rows): + minimal_path_sums[i][0] = matrix[i][0] + + for j in range(1, cols): + for i in range(rows): + minimal_path_sums[i][j] = minimal_path_sums[i][j - 1] + matrix[i][j] + + for i in range(1, rows): + minimal_path_sums[i][j] = min( + minimal_path_sums[i][j], minimal_path_sums[i - 1][j] + matrix[i][j] + ) + + for i in range(rows - 2, -1, -1): + minimal_path_sums[i][j] = min( + minimal_path_sums[i][j], minimal_path_sums[i + 1][j] + matrix[i][j] + ) + + return min(minimal_path_sums_row[-1] for minimal_path_sums_row in minimal_path_sums) + + +if __name__ == "__main__": + print(f"{solution() = }") diff --git a/project_euler/problem_082/test_matrix.txt b/project_euler/problem_082/test_matrix.txt new file mode 100644 index 000000000000..76167d9e7fc1 --- /dev/null +++ b/project_euler/problem_082/test_matrix.txt @@ -0,0 +1,5 @@ +131,673,234,103,18 +201,96,342,965,150 +630,803,746,422,111 +537,699,497,121,956 +805,732,524,37,331 From ee778128bdf8d4d6d386cfdc500f3b3173f56c06 Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Thu, 2 Mar 2023 07:57:07 +0300 Subject: [PATCH 006/757] Reduce the complexity of other/scoring_algorithm.py (#8045) * Increase the --max-complexity threshold in the file .flake8 --- other/scoring_algorithm.py | 57 ++++++++++++++++++++++++++++---------- 1 file changed, 43 insertions(+), 14 deletions(-) diff --git a/other/scoring_algorithm.py b/other/scoring_algorithm.py index 00d87cfc0b73..8e04a8f30dd7 100644 --- a/other/scoring_algorithm.py +++ b/other/scoring_algorithm.py @@ -23,29 +23,29 @@ """ -def procentual_proximity( - source_data: list[list[float]], weights: list[int] -) -> list[list[float]]: +def get_data(source_data: list[list[float]]) -> list[list[float]]: """ - weights - int list - possible values - 0 / 1 - 0 if lower values have higher weight in the data set - 1 if higher values have higher weight in the data set - - >>> procentual_proximity([[20, 60, 2012],[23, 90, 2015],[22, 50, 2011]], [0, 0, 1]) - [[20, 60, 2012, 2.0], [23, 90, 2015, 1.0], [22, 50, 2011, 1.3333333333333335]] + >>> get_data([[20, 60, 2012],[23, 90, 2015],[22, 50, 2011]]) + [[20.0, 23.0, 22.0], [60.0, 90.0, 50.0], [2012.0, 2015.0, 2011.0]] """ - - # getting data data_lists: list[list[float]] = [] for data in source_data: for i, el in enumerate(data): if len(data_lists) < i + 1: data_lists.append([]) data_lists[i].append(float(el)) + return data_lists + +def calculate_each_score( + data_lists: list[list[float]], weights: list[int] +) -> list[list[float]]: + """ + >>> calculate_each_score([[20, 23, 22], [60, 90, 50], [2012, 2015, 2011]], + ... [0, 0, 1]) + [[1.0, 0.0, 0.33333333333333337], [0.75, 0.0, 1.0], [0.25, 1.0, 0.0]] + """ score_lists: list[list[float]] = [] - # calculating each score for dlist, weight in zip(data_lists, weights): mind = min(dlist) maxd = max(dlist) @@ -72,14 +72,43 @@ def procentual_proximity( score_lists.append(score) + return score_lists + + +def generate_final_scores(score_lists: list[list[float]]) -> list[float]: + """ + >>> generate_final_scores([[1.0, 0.0, 0.33333333333333337], + ... [0.75, 0.0, 1.0], + ... [0.25, 1.0, 0.0]]) + [2.0, 1.0, 1.3333333333333335] + """ # initialize final scores final_scores: list[float] = [0 for i in range(len(score_lists[0]))] - # generate final scores for slist in score_lists: for j, ele in enumerate(slist): final_scores[j] = final_scores[j] + ele + return final_scores + + +def procentual_proximity( + source_data: list[list[float]], weights: list[int] +) -> list[list[float]]: + """ + weights - int list + possible values - 0 / 1 + 0 if lower values have higher weight in the data set + 1 if higher values have higher weight in the data set + + >>> procentual_proximity([[20, 60, 2012],[23, 90, 2015],[22, 50, 2011]], [0, 0, 1]) + [[20, 60, 2012, 2.0], [23, 90, 2015, 1.0], [22, 50, 2011, 1.3333333333333335]] + """ + + data_lists = get_data(source_data) + score_lists = calculate_each_score(data_lists, weights) + final_scores = generate_final_scores(score_lists) + # append scores to source data for i, ele in enumerate(final_scores): source_data[i].append(ele) From 9720e6a6cf52e2395e2d7ef3ef6ae91a355d318e Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Thu, 2 Mar 2023 19:51:48 +0300 Subject: [PATCH 007/757] Add Project Euler problem 117 solution 1 (#6872) Update DIRECTORY.md --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 2 + project_euler/problem_117/__init__.py | 0 project_euler/problem_117/sol1.py | 53 +++++++++++++++++++++++++++ 3 files changed, 55 insertions(+) create mode 100644 project_euler/problem_117/__init__.py create mode 100644 project_euler/problem_117/sol1.py diff --git a/DIRECTORY.md b/DIRECTORY.md index 3d1bc967e4b5..4844841040d9 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -956,6 +956,8 @@ * [Sol1](project_euler/problem_115/sol1.py) * Problem 116 * [Sol1](project_euler/problem_116/sol1.py) + * Problem 117 + * [Sol1](project_euler/problem_117/sol1.py) * Problem 119 * [Sol1](project_euler/problem_119/sol1.py) * Problem 120 diff --git a/project_euler/problem_117/__init__.py b/project_euler/problem_117/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_117/sol1.py b/project_euler/problem_117/sol1.py new file mode 100644 index 000000000000..e8214454fac5 --- /dev/null +++ b/project_euler/problem_117/sol1.py @@ -0,0 +1,53 @@ +""" +Project Euler Problem 117: https://projecteuler.net/problem=117 + +Using a combination of grey square tiles and oblong tiles chosen from: +red tiles (measuring two units), green tiles (measuring three units), +and blue tiles (measuring four units), +it is possible to tile a row measuring five units in length +in exactly fifteen different ways. + + |grey|grey|grey|grey|grey| |red,red|grey|grey|grey| + + |grey|red,red|grey|grey| |grey|grey|red,red|grey| + + |grey|grey|grey|red,red| |red,red|red,red|grey| + + |red,red|grey|red,red| |grey|red,red|red,red| + + |green,green,green|grey|grey| |grey|green,green,green|grey| + + |grey|grey|green,green,green| |red,red|green,green,green| + + |green,green,green|red,red| |blue,blue,blue,blue|grey| + + |grey|blue,blue,blue,blue| + +How many ways can a row measuring fifty units in length be tiled? + +NOTE: This is related to Problem 116 (https://projecteuler.net/problem=116). +""" + + +def solution(length: int = 50) -> int: + """ + Returns the number of ways can a row of the given length be tiled + + >>> solution(5) + 15 + """ + + ways_number = [1] * (length + 1) + + for row_length in range(length + 1): + for tile_length in range(2, 5): + for tile_start in range(row_length - tile_length + 1): + ways_number[row_length] += ways_number[ + row_length - tile_start - tile_length + ] + + return ways_number[length] + + +if __name__ == "__main__": + print(f"{solution() = }") From 41b633a841084acac5a640042d365c985e23b357 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 7 Mar 2023 00:10:39 +0100 Subject: [PATCH 008/757] [pre-commit.ci] pre-commit autoupdate (#8168) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/charliermarsh/ruff-pre-commit: v0.0.253 → v0.0.254](https://github.com/charliermarsh/ruff-pre-commit/compare/v0.0.253...v0.0.254) * Rename get_top_billionaires.py to get_top_billionaires.py.disabled * updating DIRECTORY.md --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- DIRECTORY.md | 1 - ...get_top_billionaires.py => get_top_billionaires.py.disabled} | 0 3 files changed, 1 insertion(+), 2 deletions(-) rename web_programming/{get_top_billionaires.py => get_top_billionaires.py.disabled} (100%) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9f27f985bb6a..329407265a5a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -44,7 +44,7 @@ repos: - --py311-plus - repo: https://github.com/charliermarsh/ruff-pre-commit - rev: v0.0.253 + rev: v0.0.254 hooks: - id: ruff args: diff --git a/DIRECTORY.md b/DIRECTORY.md index 4844841040d9..f25b0c6ff4e3 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -1167,7 +1167,6 @@ * [Get Amazon Product Data](web_programming/get_amazon_product_data.py) * [Get Imdb Top 250 Movies Csv](web_programming/get_imdb_top_250_movies_csv.py) * [Get Imdbtop](web_programming/get_imdbtop.py) - * [Get Top Billionaires](web_programming/get_top_billionaires.py) * [Get Top Hn Posts](web_programming/get_top_hn_posts.py) * [Get User Tweets](web_programming/get_user_tweets.py) * [Giphy](web_programming/giphy.py) diff --git a/web_programming/get_top_billionaires.py b/web_programming/get_top_billionaires.py.disabled similarity index 100% rename from web_programming/get_top_billionaires.py rename to web_programming/get_top_billionaires.py.disabled From 9e28ecca28176254c39bcc791733589c6091422e Mon Sep 17 00:00:00 2001 From: Subhendu Dash <71781104+subhendudash02@users.noreply.github.com> Date: Tue, 7 Mar 2023 21:46:25 +0530 Subject: [PATCH 009/757] Add circular convolution (#8158) * add circular convolution * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * add type hint for __init__ * rounding off final values to 2 and minor changes * add test case for unequal signals * changes in list comprehension and enumeraton --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- electronics/circular_convolution.py | 99 +++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 electronics/circular_convolution.py diff --git a/electronics/circular_convolution.py b/electronics/circular_convolution.py new file mode 100644 index 000000000000..f2e35742e944 --- /dev/null +++ b/electronics/circular_convolution.py @@ -0,0 +1,99 @@ +# https://en.wikipedia.org/wiki/Circular_convolution + +""" +Circular convolution, also known as cyclic convolution, +is a special case of periodic convolution, which is the convolution of two +periodic functions that have the same period. Periodic convolution arises, +for example, in the context of the discrete-time Fourier transform (DTFT). +In particular, the DTFT of the product of two discrete sequences is the periodic +convolution of the DTFTs of the individual sequences. And each DTFT is a periodic +summation of a continuous Fourier transform function. + +Source: https://en.wikipedia.org/wiki/Circular_convolution +""" + +import doctest +from collections import deque + +import numpy as np + + +class CircularConvolution: + """ + This class stores the first and second signal and performs the circular convolution + """ + + def __init__(self) -> None: + """ + First signal and second signal are stored as 1-D array + """ + + self.first_signal = [2, 1, 2, -1] + self.second_signal = [1, 2, 3, 4] + + def circular_convolution(self) -> list[float]: + """ + This function performs the circular convolution of the first and second signal + using matrix method + + Usage: + >>> import circular_convolution as cc + >>> convolution = cc.CircularConvolution() + >>> convolution.circular_convolution() + [10, 10, 6, 14] + + >>> convolution.first_signal = [0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6] + >>> convolution.second_signal = [0.1, 0.3, 0.5, 0.7, 0.9, 1.1, 1.3, 1.5] + >>> convolution.circular_convolution() + [5.2, 6.0, 6.48, 6.64, 6.48, 6.0, 5.2, 4.08] + + >>> convolution.first_signal = [-1, 1, 2, -2] + >>> convolution.second_signal = [0.5, 1, -1, 2, 0.75] + >>> convolution.circular_convolution() + [6.25, -3.0, 1.5, -2.0, -2.75] + + >>> convolution.first_signal = [1, -1, 2, 3, -1] + >>> convolution.second_signal = [1, 2, 3] + >>> convolution.circular_convolution() + [8, -2, 3, 4, 11] + + """ + + length_first_signal = len(self.first_signal) + length_second_signal = len(self.second_signal) + + max_length = max(length_first_signal, length_second_signal) + + # create a zero matrix of max_length x max_length + matrix = [[0] * max_length for i in range(max_length)] + + # fills the smaller signal with zeros to make both signals of same length + if length_first_signal < length_second_signal: + self.first_signal += [0] * (max_length - length_first_signal) + elif length_first_signal > length_second_signal: + self.second_signal += [0] * (max_length - length_second_signal) + + """ + Fills the matrix in the following way assuming 'x' is the signal of length 4 + [ + [x[0], x[3], x[2], x[1]], + [x[1], x[0], x[3], x[2]], + [x[2], x[1], x[0], x[3]], + [x[3], x[2], x[1], x[0]] + ] + """ + for i in range(max_length): + rotated_signal = deque(self.second_signal) + rotated_signal.rotate(i) + for j, item in enumerate(rotated_signal): + matrix[i][j] += item + + # multiply the matrix with the first signal + final_signal = np.matmul(np.transpose(matrix), np.transpose(self.first_signal)) + + # rounding-off to two decimal places + return [round(i, 2) for i in final_signal] + + +if __name__ == "__main__": + doctest.testmod() From f9cc25221c1521a0da9ee27d6a9bea1f14f4c986 Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Fri, 10 Mar 2023 12:48:05 +0300 Subject: [PATCH 010/757] Reduce the complexity of backtracking/word_search.py (#8166) * Lower the --max-complexity threshold in the file .flake8 --- backtracking/word_search.py | 112 +++++++++++++++++++----------------- 1 file changed, 60 insertions(+), 52 deletions(-) diff --git a/backtracking/word_search.py b/backtracking/word_search.py index 25d1436be36e..c9d52012b42b 100644 --- a/backtracking/word_search.py +++ b/backtracking/word_search.py @@ -33,6 +33,61 @@ """ +def get_point_key(len_board: int, len_board_column: int, row: int, column: int) -> int: + """ + Returns the hash key of matrix indexes. + + >>> get_point_key(10, 20, 1, 0) + 200 + """ + + return len_board * len_board_column * row + column + + +def exits_word( + board: list[list[str]], + word: str, + row: int, + column: int, + word_index: int, + visited_points_set: set[int], +) -> bool: + """ + Return True if it's possible to search the word suffix + starting from the word_index. + + >>> exits_word([["A"]], "B", 0, 0, 0, set()) + False + """ + + if board[row][column] != word[word_index]: + return False + + if word_index == len(word) - 1: + return True + + traverts_directions = [(0, 1), (0, -1), (-1, 0), (1, 0)] + len_board = len(board) + len_board_column = len(board[0]) + for direction in traverts_directions: + next_i = row + direction[0] + next_j = column + direction[1] + if not (0 <= next_i < len_board and 0 <= next_j < len_board_column): + continue + + key = get_point_key(len_board, len_board_column, next_i, next_j) + if key in visited_points_set: + continue + + visited_points_set.add(key) + if exits_word(board, word, next_i, next_j, word_index + 1, visited_points_set): + return True + + visited_points_set.remove(key) + + return False + + def word_exists(board: list[list[str]], word: str) -> bool: """ >>> word_exists([["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], "ABCCED") @@ -77,6 +132,8 @@ def word_exists(board: list[list[str]], word: str) -> bool: board_error_message = ( "The board should be a non empty matrix of single chars strings." ) + + len_board = len(board) if not isinstance(board, list) or len(board) == 0: raise ValueError(board_error_message) @@ -94,61 +151,12 @@ def word_exists(board: list[list[str]], word: str) -> bool: "The word parameter should be a string of length greater than 0." ) - traverts_directions = [(0, 1), (0, -1), (-1, 0), (1, 0)] - len_word = len(word) - len_board = len(board) len_board_column = len(board[0]) - - # Returns the hash key of matrix indexes. - def get_point_key(row: int, column: int) -> int: - """ - >>> len_board=10 - >>> len_board_column=20 - >>> get_point_key(0, 0) - 200 - """ - - return len_board * len_board_column * row + column - - # Return True if it's possible to search the word suffix - # starting from the word_index. - def exits_word( - row: int, column: int, word_index: int, visited_points_set: set[int] - ) -> bool: - """ - >>> board=[["A"]] - >>> word="B" - >>> exits_word(0, 0, 0, set()) - False - """ - - if board[row][column] != word[word_index]: - return False - - if word_index == len_word - 1: - return True - - for direction in traverts_directions: - next_i = row + direction[0] - next_j = column + direction[1] - if not (0 <= next_i < len_board and 0 <= next_j < len_board_column): - continue - - key = get_point_key(next_i, next_j) - if key in visited_points_set: - continue - - visited_points_set.add(key) - if exits_word(next_i, next_j, word_index + 1, visited_points_set): - return True - - visited_points_set.remove(key) - - return False - for i in range(len_board): for j in range(len_board_column): - if exits_word(i, j, 0, {get_point_key(i, j)}): + if exits_word( + board, word, i, j, 0, {get_point_key(len_board, len_board_column, i, j)} + ): return True return False From 8959211100ba7a612d42a6e7db4755303b78c5a7 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 13 Mar 2023 23:18:35 +0100 Subject: [PATCH 011/757] [pre-commit.ci] pre-commit autoupdate (#8177) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/charliermarsh/ruff-pre-commit: v0.0.254 → v0.0.255](https://github.com/charliermarsh/ruff-pre-commit/compare/v0.0.254...v0.0.255) - [github.com/pre-commit/mirrors-mypy: v1.0.1 → v1.1.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.0.1...v1.1.1) - [github.com/codespell-project/codespell: v2.2.2 → v2.2.4](https://github.com/codespell-project/codespell/compare/v2.2.2...v2.2.4) * updating DIRECTORY.md * Fixes for new version of codespell --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: Christian Clauss --- .pre-commit-config.yaml | 8 ++++---- DIRECTORY.md | 1 + machine_learning/sequential_minimum_optimization.py | 2 +- physics/lorentz_transformation_four_vector.py | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 329407265a5a..9aa965e42aec 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -44,7 +44,7 @@ repos: - --py311-plus - repo: https://github.com/charliermarsh/ruff-pre-commit - rev: v0.0.254 + rev: v0.0.255 hooks: - id: ruff args: @@ -69,7 +69,7 @@ repos: *flake8-plugins - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.0.1 + rev: v1.1.1 hooks: - id: mypy args: @@ -79,11 +79,11 @@ repos: additional_dependencies: [types-requests] - repo: https://github.com/codespell-project/codespell - rev: v2.2.2 + rev: v2.2.4 hooks: - id: codespell args: - - --ignore-words-list=ans,crate,damon,fo,followings,hist,iff,mater,secant,som,sur,tim,zar + - --ignore-words-list=3rt,ans,crate,damon,fo,followings,hist,iff,kwanza,mater,secant,som,sur,tim,zar exclude: | (?x)^( ciphers/prehistoric_men.txt | diff --git a/DIRECTORY.md b/DIRECTORY.md index f25b0c6ff4e3..b2daaaa9c47d 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -334,6 +334,7 @@ ## Electronics * [Builtin Voltage](electronics/builtin_voltage.py) * [Carrier Concentration](electronics/carrier_concentration.py) + * [Circular Convolution](electronics/circular_convolution.py) * [Coulombs Law](electronics/coulombs_law.py) * [Electric Conductivity](electronics/electric_conductivity.py) * [Electric Power](electronics/electric_power.py) diff --git a/machine_learning/sequential_minimum_optimization.py b/machine_learning/sequential_minimum_optimization.py index 37172c8e9bf6..b68bd52f4de9 100644 --- a/machine_learning/sequential_minimum_optimization.py +++ b/machine_learning/sequential_minimum_optimization.py @@ -569,7 +569,7 @@ def plot_partition_boundary( """ We can not get the optimum w of our kernel svm model which is different from linear svm. For this reason, we generate randomly distributed points with high desity and - prediced values of these points are calculated by using our tained model. Then we + prediced values of these points are calculated by using our trained model. Then we could use this prediced values to draw contour map. And this contour map can represent svm's partition boundary. """ diff --git a/physics/lorentz_transformation_four_vector.py b/physics/lorentz_transformation_four_vector.py index 64be97245f29..f4fda4dff8cd 100644 --- a/physics/lorentz_transformation_four_vector.py +++ b/physics/lorentz_transformation_four_vector.py @@ -2,7 +2,7 @@ Lorentz transformations describe the transition between two inertial reference frames F and F', each of which is moving in some direction with respect to the other. This code only calculates Lorentz transformations for movement in the x -direction with no spacial rotation (i.e., a Lorentz boost in the x direction). +direction with no spatial rotation (i.e., a Lorentz boost in the x direction). The Lorentz transformations are calculated here as linear transformations of four-vectors [ct, x, y, z] described by Minkowski space. Note that t (time) is multiplied by c (the speed of light) in the first entry of each four-vector. From b797e437aeadcac50556d6606a547dc634cf5329 Mon Sep 17 00:00:00 2001 From: Andrey Date: Tue, 14 Mar 2023 01:31:27 +0100 Subject: [PATCH 012/757] Add hashmap implementation (#7967) --- data_structures/hashing/hash_map.py | 162 ++++++++++++++++++ .../hashing/tests/test_hash_map.py | 97 +++++++++++ 2 files changed, 259 insertions(+) create mode 100644 data_structures/hashing/hash_map.py create mode 100644 data_structures/hashing/tests/test_hash_map.py diff --git a/data_structures/hashing/hash_map.py b/data_structures/hashing/hash_map.py new file mode 100644 index 000000000000..1dfcc8bbf906 --- /dev/null +++ b/data_structures/hashing/hash_map.py @@ -0,0 +1,162 @@ +""" +Hash map with open addressing. + +https://en.wikipedia.org/wiki/Hash_table + +Another hash map implementation, with a good explanation. +Modern Dictionaries by Raymond Hettinger +https://www.youtube.com/watch?v=p33CVV29OG8 +""" +from collections.abc import Iterator, MutableMapping +from dataclasses import dataclass +from typing import Generic, TypeVar + +KEY = TypeVar("KEY") +VAL = TypeVar("VAL") + + +@dataclass(frozen=True, slots=True) +class _Item(Generic[KEY, VAL]): + key: KEY + val: VAL + + +class _DeletedItem(_Item): + def __init__(self) -> None: + super().__init__(None, None) + + def __bool__(self) -> bool: + return False + + +_deleted = _DeletedItem() + + +class HashMap(MutableMapping[KEY, VAL]): + """ + Hash map with open addressing. + """ + + def __init__( + self, initial_block_size: int = 8, capacity_factor: float = 0.75 + ) -> None: + self._initial_block_size = initial_block_size + self._buckets: list[_Item | None] = [None] * initial_block_size + assert 0.0 < capacity_factor < 1.0 + self._capacity_factor = capacity_factor + self._len = 0 + + def _get_bucket_index(self, key: KEY) -> int: + return hash(key) % len(self._buckets) + + def _get_next_ind(self, ind: int) -> int: + """ + Get next index. + + Implements linear open addressing. + """ + return (ind + 1) % len(self._buckets) + + def _try_set(self, ind: int, key: KEY, val: VAL) -> bool: + """ + Try to add value to the bucket. + + If bucket is empty or key is the same, does insert and return True. + + If bucket has another key or deleted placeholder, + that means that we need to check next bucket. + """ + stored = self._buckets[ind] + if not stored: + self._buckets[ind] = _Item(key, val) + self._len += 1 + return True + elif stored.key == key: + self._buckets[ind] = _Item(key, val) + return True + else: + return False + + def _is_full(self) -> bool: + """ + Return true if we have reached safe capacity. + + So we need to increase the number of buckets to avoid collisions. + """ + limit = len(self._buckets) * self._capacity_factor + return len(self) >= int(limit) + + def _is_sparse(self) -> bool: + """Return true if we need twice fewer buckets when we have now.""" + if len(self._buckets) <= self._initial_block_size: + return False + limit = len(self._buckets) * self._capacity_factor / 2 + return len(self) < limit + + def _resize(self, new_size: int) -> None: + old_buckets = self._buckets + self._buckets = [None] * new_size + self._len = 0 + for item in old_buckets: + if item: + self._add_item(item.key, item.val) + + def _size_up(self) -> None: + self._resize(len(self._buckets) * 2) + + def _size_down(self) -> None: + self._resize(len(self._buckets) // 2) + + def _iterate_buckets(self, key: KEY) -> Iterator[int]: + ind = self._get_bucket_index(key) + for _ in range(len(self._buckets)): + yield ind + ind = self._get_next_ind(ind) + + def _add_item(self, key: KEY, val: VAL) -> None: + for ind in self._iterate_buckets(key): + if self._try_set(ind, key, val): + break + + def __setitem__(self, key: KEY, val: VAL) -> None: + if self._is_full(): + self._size_up() + + self._add_item(key, val) + + def __delitem__(self, key: KEY) -> None: + for ind in self._iterate_buckets(key): + item = self._buckets[ind] + if item is None: + raise KeyError(key) + if item is _deleted: + continue + if item.key == key: + self._buckets[ind] = _deleted + self._len -= 1 + break + if self._is_sparse(): + self._size_down() + + def __getitem__(self, key: KEY) -> VAL: + for ind in self._iterate_buckets(key): + item = self._buckets[ind] + if item is None: + break + if item is _deleted: + continue + if item.key == key: + return item.val + raise KeyError(key) + + def __len__(self) -> int: + return self._len + + def __iter__(self) -> Iterator[KEY]: + yield from (item.key for item in self._buckets if item) + + def __repr__(self) -> str: + val_string = " ,".join( + f"{item.key}: {item.val}" for item in self._buckets if item + ) + return f"HashMap({val_string})" diff --git a/data_structures/hashing/tests/test_hash_map.py b/data_structures/hashing/tests/test_hash_map.py new file mode 100644 index 000000000000..929e67311996 --- /dev/null +++ b/data_structures/hashing/tests/test_hash_map.py @@ -0,0 +1,97 @@ +from operator import delitem, getitem, setitem + +import pytest + +from data_structures.hashing.hash_map import HashMap + + +def _get(k): + return getitem, k + + +def _set(k, v): + return setitem, k, v + + +def _del(k): + return delitem, k + + +def _run_operation(obj, fun, *args): + try: + return fun(obj, *args), None + except Exception as e: + return None, e + + +_add_items = ( + _set("key_a", "val_a"), + _set("key_b", "val_b"), +) + +_overwrite_items = [ + _set("key_a", "val_a"), + _set("key_a", "val_b"), +] + +_delete_items = [ + _set("key_a", "val_a"), + _set("key_b", "val_b"), + _del("key_a"), + _del("key_b"), + _set("key_a", "val_a"), + _del("key_a"), +] + +_access_absent_items = [ + _get("key_a"), + _del("key_a"), + _set("key_a", "val_a"), + _del("key_a"), + _del("key_a"), + _get("key_a"), +] + +_add_with_resize_up = [ + *[_set(x, x) for x in range(5)], # guaranteed upsize +] + +_add_with_resize_down = [ + *[_set(x, x) for x in range(5)], # guaranteed upsize + *[_del(x) for x in range(5)], + _set("key_a", "val_b"), +] + + +@pytest.mark.parametrize( + "operations", + ( + pytest.param(_add_items, id="add items"), + pytest.param(_overwrite_items, id="overwrite items"), + pytest.param(_delete_items, id="delete items"), + pytest.param(_access_absent_items, id="access absent items"), + pytest.param(_add_with_resize_up, id="add with resize up"), + pytest.param(_add_with_resize_down, id="add with resize down"), + ), +) +def test_hash_map_is_the_same_as_dict(operations): + my = HashMap(initial_block_size=4) + py = {} + for _, (fun, *args) in enumerate(operations): + my_res, my_exc = _run_operation(my, fun, *args) + py_res, py_exc = _run_operation(py, fun, *args) + assert my_res == py_res + assert str(my_exc) == str(py_exc) + assert set(py) == set(my) + assert len(py) == len(my) + assert set(my.items()) == set(py.items()) + + +def test_no_new_methods_was_added_to_api(): + def is_public(name: str) -> bool: + return not name.startswith("_") + + dict_public_names = {name for name in dir({}) if is_public(name)} + hash_public_names = {name for name in dir(HashMap()) if is_public(name)} + + assert dict_public_names > hash_public_names From 9701e459e884e883fc720277452ec592eae305d0 Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Tue, 14 Mar 2023 08:39:36 +0300 Subject: [PATCH 013/757] Add Project Euler problem 100 solution 1 (#8175) Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- DIRECTORY.md | 2 ++ project_euler/problem_100/__init__.py | 0 project_euler/problem_100/sol1.py | 48 +++++++++++++++++++++++++++ 3 files changed, 50 insertions(+) create mode 100644 project_euler/problem_100/__init__.py create mode 100644 project_euler/problem_100/sol1.py diff --git a/DIRECTORY.md b/DIRECTORY.md index b2daaaa9c47d..e1ce44eedce1 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -937,6 +937,8 @@ * [Sol1](project_euler/problem_097/sol1.py) * Problem 099 * [Sol1](project_euler/problem_099/sol1.py) + * Problem 100 + * [Sol1](project_euler/problem_100/sol1.py) * Problem 101 * [Sol1](project_euler/problem_101/sol1.py) * Problem 102 diff --git a/project_euler/problem_100/__init__.py b/project_euler/problem_100/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_100/sol1.py b/project_euler/problem_100/sol1.py new file mode 100644 index 000000000000..367378e7ab17 --- /dev/null +++ b/project_euler/problem_100/sol1.py @@ -0,0 +1,48 @@ +""" +Project Euler Problem 100: https://projecteuler.net/problem=100 + +If a box contains twenty-one coloured discs, composed of fifteen blue discs and +six red discs, and two discs were taken at random, it can be seen that +the probability of taking two blue discs, P(BB) = (15/21) x (14/20) = 1/2. + +The next such arrangement, for which there is exactly 50% chance of taking two blue +discs at random, is a box containing eighty-five blue discs and thirty-five red discs. + +By finding the first arrangement to contain over 10^12 = 1,000,000,000,000 discs +in total, determine the number of blue discs that the box would contain. +""" + + +def solution(min_total: int = 10**12) -> int: + """ + Returns the number of blue discs for the first arrangement to contain + over min_total discs in total + + >>> solution(2) + 3 + + >>> solution(4) + 15 + + >>> solution(21) + 85 + """ + + prev_numerator = 1 + prev_denominator = 0 + + numerator = 1 + denominator = 1 + + while numerator <= 2 * min_total - 1: + prev_numerator += 2 * numerator + numerator += 2 * prev_numerator + + prev_denominator += 2 * denominator + denominator += 2 * prev_denominator + + return (denominator + 1) // 2 + + +if __name__ == "__main__": + print(f"{solution() = }") From 47b3c729826e864fb1d0a30b03cf95fa2adae591 Mon Sep 17 00:00:00 2001 From: David Leal Date: Mon, 13 Mar 2023 23:46:52 -0600 Subject: [PATCH 014/757] docs: add the other/miscellaneous form (#8163) Co-authored-by: Christian Clauss Co-authored-by: Dhruv Manilawala --- .github/ISSUE_TEMPLATE/other.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/other.yml diff --git a/.github/ISSUE_TEMPLATE/other.yml b/.github/ISSUE_TEMPLATE/other.yml new file mode 100644 index 000000000000..44d6ff541506 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/other.yml @@ -0,0 +1,19 @@ +name: Other +description: Use this for any other issues. PLEASE do not create blank issues +labels: ["awaiting triage"] +body: + - type: textarea + id: issuedescription + attributes: + label: What would you like to share? + description: Provide a clear and concise explanation of your issue. + validations: + required: true + + - type: textarea + id: extrainfo + attributes: + label: Additional information + description: Is there anything else we should know about this issue? + validations: + required: false From adc3ccdabede375df5cff62c3c8f06d8a191a803 Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Wed, 15 Mar 2023 15:56:03 +0300 Subject: [PATCH 015/757] Add Project Euler problem 131 solution 1 (#8179) Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- DIRECTORY.md | 5 +++ project_euler/problem_131/__init__.py | 0 project_euler/problem_131/sol1.py | 56 +++++++++++++++++++++++++++ 3 files changed, 61 insertions(+) create mode 100644 project_euler/problem_131/__init__.py create mode 100644 project_euler/problem_131/sol1.py diff --git a/DIRECTORY.md b/DIRECTORY.md index e1ce44eedce1..1d3177801a2c 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -196,11 +196,14 @@ * [Disjoint Set](data_structures/disjoint_set/disjoint_set.py) * Hashing * [Double Hash](data_structures/hashing/double_hash.py) + * [Hash Map](data_structures/hashing/hash_map.py) * [Hash Table](data_structures/hashing/hash_table.py) * [Hash Table With Linked List](data_structures/hashing/hash_table_with_linked_list.py) * Number Theory * [Prime Numbers](data_structures/hashing/number_theory/prime_numbers.py) * [Quadratic Probing](data_structures/hashing/quadratic_probing.py) + * Tests + * [Test Hash Map](data_structures/hashing/tests/test_hash_map.py) * Heap * [Binomial Heap](data_structures/heap/binomial_heap.py) * [Heap](data_structures/heap/heap.py) @@ -973,6 +976,8 @@ * [Sol1](project_euler/problem_125/sol1.py) * Problem 129 * [Sol1](project_euler/problem_129/sol1.py) + * Problem 131 + * [Sol1](project_euler/problem_131/sol1.py) * Problem 135 * [Sol1](project_euler/problem_135/sol1.py) * Problem 144 diff --git a/project_euler/problem_131/__init__.py b/project_euler/problem_131/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_131/sol1.py b/project_euler/problem_131/sol1.py new file mode 100644 index 000000000000..f5302aac8644 --- /dev/null +++ b/project_euler/problem_131/sol1.py @@ -0,0 +1,56 @@ +""" +Project Euler Problem 131: https://projecteuler.net/problem=131 + +There are some prime values, p, for which there exists a positive integer, n, +such that the expression n^3 + n^2p is a perfect cube. + +For example, when p = 19, 8^3 + 8^2 x 19 = 12^3. + +What is perhaps most surprising is that for each prime with this property +the value of n is unique, and there are only four such primes below one-hundred. + +How many primes below one million have this remarkable property? +""" + +from math import isqrt + + +def is_prime(number: int) -> bool: + """ + Determines whether number is prime + + >>> is_prime(3) + True + + >>> is_prime(4) + False + """ + + for divisor in range(2, isqrt(number) + 1): + if number % divisor == 0: + return False + return True + + +def solution(max_prime: int = 10**6) -> int: + """ + Returns number of primes below max_prime with the property + + >>> solution(100) + 4 + """ + + primes_count = 0 + cube_index = 1 + prime_candidate = 7 + while prime_candidate < max_prime: + primes_count += is_prime(prime_candidate) + + cube_index += 1 + prime_candidate += 6 * cube_index + + return primes_count + + +if __name__ == "__main__": + print(f"{solution() = }") From c96241b5a5052af466894ef90c7a7c749ba872eb Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Wed, 15 Mar 2023 13:58:25 +0100 Subject: [PATCH 016/757] Replace bandit, flake8, isort, and pyupgrade with ruff (#8178) * Replace bandit, flake8, isort, and pyupgrade with ruff * Comment on ruff rules * updating DIRECTORY.md --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- .flake8 | 10 --- .github/workflows/ruff.yml | 16 ++++ .pre-commit-config.yaml | 78 +++++-------------- arithmetic_analysis/newton_raphson.py | 2 +- arithmetic_analysis/newton_raphson_new.py | 2 +- data_structures/heap/heap_generic.py | 1 - dynamic_programming/min_distance_up_bottom.py | 9 +-- dynamic_programming/minimum_tickets_cost.py | 4 +- dynamic_programming/word_break.py | 4 +- hashes/sha1.py | 12 +-- machine_learning/support_vector_machines.py | 4 +- maths/eulers_totient.py | 34 ++++---- maths/fibonacci.py | 4 +- maths/pythagoras.py | 6 +- other/quine.py | 1 + project_euler/problem_075/sol1.py | 3 +- pyproject.toml | 59 ++++++++++++-- sorts/external_sort.py | 2 +- strings/check_anagrams.py | 3 +- strings/word_occurrence.py | 3 +- web_programming/currency_converter.py | 2 +- 21 files changed, 127 insertions(+), 132 deletions(-) delete mode 100644 .flake8 create mode 100644 .github/workflows/ruff.yml diff --git a/.flake8 b/.flake8 deleted file mode 100644 index b68ee8533a61..000000000000 --- a/.flake8 +++ /dev/null @@ -1,10 +0,0 @@ -[flake8] -max-line-length = 88 -# max-complexity should be 10 -max-complexity = 19 -extend-ignore = - # Formatting style for `black` - # E203 is whitespace before ':' - E203, - # W503 is line break occurred before a binary operator - W503 diff --git a/.github/workflows/ruff.yml b/.github/workflows/ruff.yml new file mode 100644 index 000000000000..ca2d5be47327 --- /dev/null +++ b/.github/workflows/ruff.yml @@ -0,0 +1,16 @@ +# https://beta.ruff.rs +name: ruff +on: + push: + branches: + - master + pull_request: + branches: + - master +jobs: + ruff: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - run: pip install --user ruff + - run: ruff --format=github . diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9aa965e42aec..82aad6c65a9b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,6 +3,7 @@ repos: rev: v4.4.0 hooks: - id: check-executables-have-shebangs + - id: check-toml - id: check-yaml - id: end-of-file-fixer types: [python] @@ -14,60 +15,41 @@ repos: hooks: - id: auto-walrus + - repo: https://github.com/charliermarsh/ruff-pre-commit + rev: v0.0.255 + hooks: + - id: ruff + - repo: https://github.com/psf/black rev: 23.1.0 hooks: - id: black - - repo: https://github.com/PyCQA/isort - rev: 5.12.0 + - repo: https://github.com/codespell-project/codespell + rev: v2.2.4 hooks: - - id: isort - args: - - --profile=black + - id: codespell + additional_dependencies: + - tomli - repo: https://github.com/tox-dev/pyproject-fmt rev: "0.9.2" hooks: - id: pyproject-fmt + - repo: local + hooks: + - id: validate-filenames + name: Validate filenames + entry: ./scripts/validate_filenames.py + language: script + pass_filenames: false + - repo: https://github.com/abravalheri/validate-pyproject rev: v0.12.1 hooks: - id: validate-pyproject - - repo: https://github.com/asottile/pyupgrade - rev: v3.3.1 - hooks: - - id: pyupgrade - args: - - --py311-plus - - - repo: https://github.com/charliermarsh/ruff-pre-commit - rev: v0.0.255 - hooks: - - id: ruff - args: - - --ignore=E741 - - - repo: https://github.com/PyCQA/flake8 - rev: 6.0.0 - hooks: - - id: flake8 # See .flake8 for args - additional_dependencies: &flake8-plugins - - flake8-bugbear - - flake8-builtins - # - flake8-broken-line - - flake8-comprehensions - - pep8-naming - - - repo: https://github.com/asottile/yesqa - rev: v1.4.0 - hooks: - - id: yesqa - additional_dependencies: - *flake8-plugins - - repo: https://github.com/pre-commit/mirrors-mypy rev: v1.1.1 hooks: @@ -77,25 +59,3 @@ repos: - --install-types # See mirrors-mypy README.md - --non-interactive additional_dependencies: [types-requests] - - - repo: https://github.com/codespell-project/codespell - rev: v2.2.4 - hooks: - - id: codespell - args: - - --ignore-words-list=3rt,ans,crate,damon,fo,followings,hist,iff,kwanza,mater,secant,som,sur,tim,zar - exclude: | - (?x)^( - ciphers/prehistoric_men.txt | - strings/dictionary.txt | - strings/words.txt | - project_euler/problem_022/p022_names.txt - )$ - - - repo: local - hooks: - - id: validate-filenames - name: Validate filenames - entry: ./scripts/validate_filenames.py - language: script - pass_filenames: false diff --git a/arithmetic_analysis/newton_raphson.py b/arithmetic_analysis/newton_raphson.py index 86ff9d350dde..aee2f07e5743 100644 --- a/arithmetic_analysis/newton_raphson.py +++ b/arithmetic_analysis/newton_raphson.py @@ -5,7 +5,7 @@ from __future__ import annotations from decimal import Decimal -from math import * # noqa: F401, F403 +from math import * # noqa: F403 from sympy import diff diff --git a/arithmetic_analysis/newton_raphson_new.py b/arithmetic_analysis/newton_raphson_new.py index 472cb5b5ac54..f61841e2eb84 100644 --- a/arithmetic_analysis/newton_raphson_new.py +++ b/arithmetic_analysis/newton_raphson_new.py @@ -8,7 +8,7 @@ # Newton's Method - https://en.wikipedia.org/wiki/Newton's_method from sympy import diff, lambdify, symbols -from sympy.functions import * # noqa: F401, F403 +from sympy.functions import * # noqa: F403 def newton_raphson( diff --git a/data_structures/heap/heap_generic.py b/data_structures/heap/heap_generic.py index b4d7019f41f9..ee92149e25a9 100644 --- a/data_structures/heap/heap_generic.py +++ b/data_structures/heap/heap_generic.py @@ -166,7 +166,6 @@ def test_heap() -> None: >>> h.get_top() [9, -40] """ - pass if __name__ == "__main__": diff --git a/dynamic_programming/min_distance_up_bottom.py b/dynamic_programming/min_distance_up_bottom.py index 49c361f24d45..4870c7ef4499 100644 --- a/dynamic_programming/min_distance_up_bottom.py +++ b/dynamic_programming/min_distance_up_bottom.py @@ -6,13 +6,13 @@ The aim is to demonstate up bottom approach for solving the task. The implementation was tested on the leetcode: https://leetcode.com/problems/edit-distance/ -""" -""" Levinstein distance Dynamic Programming: up -> down. """ +import functools + def min_distance_up_bottom(word1: str, word2: str) -> int: """ @@ -25,13 +25,10 @@ def min_distance_up_bottom(word1: str, word2: str) -> int: >>> min_distance_up_bottom("zooicoarchaeologist", "zoologist") 10 """ - - from functools import lru_cache - len_word1 = len(word1) len_word2 = len(word2) - @lru_cache(maxsize=None) + @functools.cache def min_distance(index1: int, index2: int) -> int: # if first word index is overflow - delete all from the second word if index1 >= len_word1: diff --git a/dynamic_programming/minimum_tickets_cost.py b/dynamic_programming/minimum_tickets_cost.py index d07056d9217f..6790c21f16ed 100644 --- a/dynamic_programming/minimum_tickets_cost.py +++ b/dynamic_programming/minimum_tickets_cost.py @@ -22,7 +22,7 @@ Dynamic Programming: up -> down. """ -from functools import lru_cache +import functools def mincost_tickets(days: list[int], costs: list[int]) -> int: @@ -106,7 +106,7 @@ def mincost_tickets(days: list[int], costs: list[int]) -> int: days_set = set(days) - @lru_cache(maxsize=None) + @functools.cache def dynamic_programming(index: int) -> int: if index > 365: return 0 diff --git a/dynamic_programming/word_break.py b/dynamic_programming/word_break.py index 642ea0edf40d..4d7ac869080c 100644 --- a/dynamic_programming/word_break.py +++ b/dynamic_programming/word_break.py @@ -20,7 +20,7 @@ Space: O(n) """ -from functools import lru_cache +import functools from typing import Any @@ -80,7 +80,7 @@ def word_break(string: str, words: list[str]) -> bool: len_string = len(string) # Dynamic programming method - @lru_cache(maxsize=None) + @functools.cache def is_breakable(index: int) -> bool: """ >>> string = 'a' diff --git a/hashes/sha1.py b/hashes/sha1.py index b19e0cfafea3..9f0437f208fa 100644 --- a/hashes/sha1.py +++ b/hashes/sha1.py @@ -26,7 +26,6 @@ import argparse import hashlib # hashlib is only used inside the Test class import struct -import unittest class SHA1Hash: @@ -128,14 +127,9 @@ def final_hash(self): return "%08x%08x%08x%08x%08x" % tuple(self.h) -class SHA1HashTest(unittest.TestCase): - """ - Test class for the SHA1Hash class. Inherits the TestCase class from unittest - """ - - def testMatchHashes(self): # noqa: N802 - msg = bytes("Test String", "utf-8") - self.assertEqual(SHA1Hash(msg).final_hash(), hashlib.sha1(msg).hexdigest()) +def test_sha1_hash(): + msg = b"Test String" + assert SHA1Hash(msg).final_hash() == hashlib.sha1(msg).hexdigest() # noqa: S324 def main(): diff --git a/machine_learning/support_vector_machines.py b/machine_learning/support_vector_machines.py index caec10175c50..df854cc850b1 100644 --- a/machine_learning/support_vector_machines.py +++ b/machine_learning/support_vector_machines.py @@ -56,7 +56,7 @@ def __init__( *, regularization: float = np.inf, kernel: str = "linear", - gamma: float = 0, + gamma: float = 0.0, ) -> None: self.regularization = regularization self.gamma = gamma @@ -65,7 +65,7 @@ def __init__( elif kernel == "rbf": if self.gamma == 0: raise ValueError("rbf kernel requires gamma") - if not (isinstance(self.gamma, float) or isinstance(self.gamma, int)): + if not isinstance(self.gamma, (float, int)): raise ValueError("gamma must be float or int") if not self.gamma > 0: raise ValueError("gamma must be > 0") diff --git a/maths/eulers_totient.py b/maths/eulers_totient.py index 6a35e69bde0b..a156647037b4 100644 --- a/maths/eulers_totient.py +++ b/maths/eulers_totient.py @@ -1,5 +1,20 @@ # Eulers Totient function finds the number of relative primes of a number n from 1 to n def totient(n: int) -> list: + """ + >>> n = 10 + >>> totient_calculation = totient(n) + >>> for i in range(1, n): + ... print(f"{i} has {totient_calculation[i]} relative primes.") + 1 has 0 relative primes. + 2 has 1 relative primes. + 3 has 2 relative primes. + 4 has 2 relative primes. + 5 has 4 relative primes. + 6 has 2 relative primes. + 7 has 6 relative primes. + 8 has 4 relative primes. + 9 has 6 relative primes. + """ is_prime = [True for i in range(n + 1)] totients = [i - 1 for i in range(n + 1)] primes = [] @@ -20,25 +35,6 @@ def totient(n: int) -> list: return totients -def test_totient() -> None: - """ - >>> n = 10 - >>> totient_calculation = totient(n) - >>> for i in range(1, n): - ... print(f"{i} has {totient_calculation[i]} relative primes.") - 1 has 0 relative primes. - 2 has 1 relative primes. - 3 has 2 relative primes. - 4 has 2 relative primes. - 5 has 4 relative primes. - 6 has 2 relative primes. - 7 has 6 relative primes. - 8 has 4 relative primes. - 9 has 6 relative primes. - """ - pass - - if __name__ == "__main__": import doctest diff --git a/maths/fibonacci.py b/maths/fibonacci.py index d58c9fc68c67..e810add69dc7 100644 --- a/maths/fibonacci.py +++ b/maths/fibonacci.py @@ -16,7 +16,7 @@ fib_binet runtime: 0.0174 ms """ -from functools import lru_cache +import functools from math import sqrt from time import time @@ -110,7 +110,7 @@ def fib_recursive_cached(n: int) -> list[int]: Exception: n is negative """ - @lru_cache(maxsize=None) + @functools.cache def fib_recursive_term(i: int) -> int: """ Calculates the i-th (0-indexed) Fibonacci number using recursion diff --git a/maths/pythagoras.py b/maths/pythagoras.py index 69a17731a0fd..7770e981d44d 100644 --- a/maths/pythagoras.py +++ b/maths/pythagoras.py @@ -14,17 +14,13 @@ def __repr__(self) -> str: def distance(a: Point, b: Point) -> float: - return math.sqrt(abs((b.x - a.x) ** 2 + (b.y - a.y) ** 2 + (b.z - a.z) ** 2)) - - -def test_distance() -> None: """ >>> point1 = Point(2, -1, 7) >>> point2 = Point(1, -3, 5) >>> print(f"Distance from {point1} to {point2} is {distance(point1, point2)}") Distance from Point(2, -1, 7) to Point(1, -3, 5) is 3.0 """ - pass + return math.sqrt(abs((b.x - a.x) ** 2 + (b.y - a.y) ** 2 + (b.z - a.z) ** 2)) if __name__ == "__main__": diff --git a/other/quine.py b/other/quine.py index 01e03bbb02cb..500a351d38dc 100644 --- a/other/quine.py +++ b/other/quine.py @@ -1,4 +1,5 @@ #!/bin/python3 +# ruff: noqa """ Quine: diff --git a/project_euler/problem_075/sol1.py b/project_euler/problem_075/sol1.py index b57604d76a86..0ccaf5dee7ec 100644 --- a/project_euler/problem_075/sol1.py +++ b/project_euler/problem_075/sol1.py @@ -29,7 +29,6 @@ from collections import defaultdict from math import gcd -from typing import DefaultDict def solution(limit: int = 1500000) -> int: @@ -43,7 +42,7 @@ def solution(limit: int = 1500000) -> int: >>> solution(50000) 5502 """ - frequencies: DefaultDict = defaultdict(int) + frequencies: defaultdict = defaultdict(int) euclid_m = 2 while 2 * euclid_m * (euclid_m + 1) <= limit: for euclid_n in range((euclid_m % 2) + 1, euclid_m, 2): diff --git a/pyproject.toml b/pyproject.toml index 5f9b1aa06c0e..6552101d2faa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,8 +12,57 @@ addopts = [ omit = [".env/*"] sort = "Cover" -#[report] -#sort = Cover -#omit = -# .env/* -# backtracking/* +[tool.codespell] +ignore-words-list = "3rt,ans,crate,damon,fo,followings,hist,iff,kwanza,mater,secant,som,sur,tim,zar" +skip = "./.*,*.json,ciphers/prehistoric_men.txt,project_euler/problem_022/p022_names.txt,pyproject.toml,strings/dictionary.txt,strings/words.txt" + +[tool.ruff] +ignore = [ # `ruff rule S101` for a description of that rule + "B904", # B904: Within an `except` clause, raise exceptions with `raise ... from err` + "B905", # B905: `zip()` without an explicit `strict=` parameter + "E741", # E741: Ambiguous variable name 'l' + "G004", # G004 Logging statement uses f-string + "N999", # N999: Invalid module name + "PLC1901", # PLC1901: `{}` can be simplified to `{}` as an empty string is falsey + "PLR2004", # PLR2004: Magic value used in comparison + "PLR5501", # PLR5501: Consider using `elif` instead of `else` + "PLW0120", # PLW0120: `else` clause on loop without a `break` statement + "PLW060", # PLW060: Using global for `{name}` but no assignment is done -- DO NOT FIX + "PLW2901", # PLW2901: Redefined loop variable + "RUF00", # RUF00: Ambiguous unicode character -- DO NOT FIX + "RUF100", # RUF100: Unused `noqa` directive + "S101", # S101: Use of `assert` detected -- DO NOT FIX + "S105", # S105: Possible hardcoded password: 'password' + "S113", # S113: Probable use of requests call without timeout + "UP038", # UP038: Use `X | Y` in `{}` call instead of `(X, Y)` -- DO NOT FIX +] +select = [ # https://beta.ruff.rs/docs/rules + "A", # A: builtins + "B", # B: bugbear + "C40", # C40: comprehensions + "C90", # C90: mccabe code complexity + "E", # E: pycodestyle errors + "F", # F: pyflakes + "G", # G: logging format + "I", # I: isort + "N", # N: pep8 naming + "PL", # PL: pylint + "PIE", # PIE: pie + "PYI", # PYI: type hinting stub files + "RUF", # RUF: ruff + "S", # S: bandit + "TID", # TID: tidy imports + "UP", # UP: pyupgrade + "W", # W: pycodestyle warnings + "YTT", # YTT: year 2020 +] +target-version = "py311" + +[tool.ruff.mccabe] # DO NOT INCREASE THIS VALUE +max-complexity = 20 # default: 10 + +[tool.ruff.pylint] # DO NOT INCREASE THESE VALUES +max-args = 10 # default: 5 +max-branches = 20 # default: 12 +max-returns = 8 # default: 6 +max-statements = 88 # default: 50 diff --git a/sorts/external_sort.py b/sorts/external_sort.py index 7af7dc0a609d..e6b0d47f79f5 100644 --- a/sorts/external_sort.py +++ b/sorts/external_sort.py @@ -104,7 +104,7 @@ def get_file_handles(self, filenames, buffer_size): files = {} for i in range(len(filenames)): - files[i] = open(filenames[i], "r", buffer_size) + files[i] = open(filenames[i], "r", buffer_size) # noqa: UP015 return files diff --git a/strings/check_anagrams.py b/strings/check_anagrams.py index a364b98212ad..9dcdffcfb921 100644 --- a/strings/check_anagrams.py +++ b/strings/check_anagrams.py @@ -2,7 +2,6 @@ wiki: https://en.wikipedia.org/wiki/Anagram """ from collections import defaultdict -from typing import DefaultDict def check_anagrams(first_str: str, second_str: str) -> bool: @@ -30,7 +29,7 @@ def check_anagrams(first_str: str, second_str: str) -> bool: return False # Default values for count should be 0 - count: DefaultDict[str, int] = defaultdict(int) + count: defaultdict[str, int] = defaultdict(int) # For each character in input strings, # increment count in the corresponding diff --git a/strings/word_occurrence.py b/strings/word_occurrence.py index 8260620c38a4..5a18ebf771e4 100644 --- a/strings/word_occurrence.py +++ b/strings/word_occurrence.py @@ -1,7 +1,6 @@ # Created by sarathkaul on 17/11/19 # Modified by Arkadip Bhattacharya(@darkmatter18) on 20/04/2020 from collections import defaultdict -from typing import DefaultDict def word_occurrence(sentence: str) -> dict: @@ -15,7 +14,7 @@ def word_occurrence(sentence: str) -> dict: >>> dict(word_occurrence("Two spaces")) {'Two': 1, 'spaces': 1} """ - occurrence: DefaultDict[str, int] = defaultdict(int) + occurrence: defaultdict[str, int] = defaultdict(int) # Creating a dictionary containing count of each word for word in sentence.split(): occurrence[word] += 1 diff --git a/web_programming/currency_converter.py b/web_programming/currency_converter.py index 6fcc60e8feeb..69f2a2c4d421 100644 --- a/web_programming/currency_converter.py +++ b/web_programming/currency_converter.py @@ -8,7 +8,7 @@ import requests URL_BASE = "https://www.amdoren.com/api/currency.php" -TESTING = os.getenv("CI", False) +TESTING = os.getenv("CI", "") API_KEY = os.getenv("AMDOREN_API_KEY", "") if not API_KEY and not TESTING: From 521fbca61c6bdb84746564eb58c2ef2131260187 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Thu, 16 Mar 2023 13:31:29 +0100 Subject: [PATCH 017/757] Replace flake8 with ruff (#8184) --- CONTRIBUTING.md | 6 +++--- audio_filters/equal_loudness_filter.py.broken.txt | 2 +- data_structures/binary_tree/red_black_tree.py | 4 ++-- digital_image_processing/change_contrast.py | 4 ++-- maths/is_square_free.py | 4 ++-- maths/mobius_function.py | 4 ++-- other/linear_congruential_generator.py | 8 ++++---- pyproject.toml | 1 + quantum/ripple_adder_classic.py | 6 +++--- 9 files changed, 20 insertions(+), 19 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3ce5bd1edf68..6b6e4d21bfc7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -81,11 +81,11 @@ We want your work to be readable by others; therefore, we encourage you to note black . ``` -- All submissions will need to pass the test `flake8 . --ignore=E203,W503 --max-line-length=88` before they will be accepted so if possible, try this test locally on your Python file(s) before submitting your pull request. +- All submissions will need to pass the test `ruff .` before they will be accepted so if possible, try this test locally on your Python file(s) before submitting your pull request. ```bash - python3 -m pip install flake8 # only required the first time - flake8 . --ignore=E203,W503 --max-line-length=88 --show-source + python3 -m pip install ruff # only required the first time + ruff . ``` - Original code submission require docstrings or comments to describe your work. diff --git a/audio_filters/equal_loudness_filter.py.broken.txt b/audio_filters/equal_loudness_filter.py.broken.txt index b9a3c50e1c33..88cba8533cf7 100644 --- a/audio_filters/equal_loudness_filter.py.broken.txt +++ b/audio_filters/equal_loudness_filter.py.broken.txt @@ -20,7 +20,7 @@ class EqualLoudnessFilter: samplerate, use with caution. Code based on matlab implementation at https://bit.ly/3eqh2HU - (url shortened for flake8) + (url shortened for ruff) Target curve: https://i.imgur.com/3g2VfaM.png Yulewalk response: https://i.imgur.com/J9LnJ4C.png diff --git a/data_structures/binary_tree/red_black_tree.py b/data_structures/binary_tree/red_black_tree.py index b50d75d33689..3ebc8d63939b 100644 --- a/data_structures/binary_tree/red_black_tree.py +++ b/data_structures/binary_tree/red_black_tree.py @@ -1,6 +1,6 @@ """ -python/black : true -flake8 : passed +psf/black : true +ruff : passed """ from __future__ import annotations diff --git a/digital_image_processing/change_contrast.py b/digital_image_processing/change_contrast.py index 6a150400249f..7e49694708f8 100644 --- a/digital_image_processing/change_contrast.py +++ b/digital_image_processing/change_contrast.py @@ -4,8 +4,8 @@ This algorithm is used in https://noivce.pythonanywhere.com/ Python web app. -python/black: True -flake8 : True +psf/black: True +ruff : True """ from PIL import Image diff --git a/maths/is_square_free.py b/maths/is_square_free.py index 4134398d258b..08c70dc32c38 100644 --- a/maths/is_square_free.py +++ b/maths/is_square_free.py @@ -1,7 +1,7 @@ """ References: wikipedia:square free number -python/black : True -flake8 : True +psf/black : True +ruff : True """ from __future__ import annotations diff --git a/maths/mobius_function.py b/maths/mobius_function.py index 4fcf35f21813..8abdc4cafcb4 100644 --- a/maths/mobius_function.py +++ b/maths/mobius_function.py @@ -1,8 +1,8 @@ """ References: https://en.wikipedia.org/wiki/M%C3%B6bius_function References: wikipedia:square free number -python/black : True -flake8 : True +psf/black : True +ruff : True """ from maths.is_square_free import is_square_free diff --git a/other/linear_congruential_generator.py b/other/linear_congruential_generator.py index 777ee6355b9b..c016310f9cfa 100644 --- a/other/linear_congruential_generator.py +++ b/other/linear_congruential_generator.py @@ -9,10 +9,10 @@ class LinearCongruentialGenerator: """ # The default value for **seed** is the result of a function call which is not - # normally recommended and causes flake8-bugbear to raise a B008 error. However, - # in this case, it is accptable because `LinearCongruentialGenerator.__init__()` - # will only be called once per instance and it ensures that each instance will - # generate a unique sequence of numbers. + # normally recommended and causes ruff to raise a B008 error. However, in this case, + # it is accptable because `LinearCongruentialGenerator.__init__()` will only be + # called once per instance and it ensures that each instance will generate a unique + # sequence of numbers. def __init__(self, multiplier, increment, modulo, seed=int(time())): # noqa: B008 """ diff --git a/pyproject.toml b/pyproject.toml index 6552101d2faa..169c3a71ba6c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -56,6 +56,7 @@ select = [ # https://beta.ruff.rs/docs/rules "W", # W: pycodestyle warnings "YTT", # YTT: year 2020 ] +show-source = true target-version = "py311" [tool.ruff.mccabe] # DO NOT INCREASE THIS VALUE diff --git a/quantum/ripple_adder_classic.py b/quantum/ripple_adder_classic.py index c07757af7fff..b604395bc583 100644 --- a/quantum/ripple_adder_classic.py +++ b/quantum/ripple_adder_classic.py @@ -54,9 +54,9 @@ def full_adder( # The default value for **backend** is the result of a function call which is not -# normally recommended and causes flake8-bugbear to raise a B008 error. However, -# in this case, this is acceptable because `Aer.get_backend()` is called when the -# function is defined and that same backend is then reused for all function calls. +# normally recommended and causes ruff to raise a B008 error. However, in this case, +# this is acceptable because `Aer.get_backend()` is called when the function is defined +# and that same backend is then reused for all function calls. def ripple_adder( From 3f9150c1b2dd15808a4962e03a1455f8d825512c Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 20 Mar 2023 22:16:13 +0100 Subject: [PATCH 018/757] [pre-commit.ci] pre-commit autoupdate (#8294) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/charliermarsh/ruff-pre-commit: v0.0.255 → v0.0.257](https://github.com/charliermarsh/ruff-pre-commit/compare/v0.0.255...v0.0.257) * Fix PLR1711 Useless statement at end of function --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- .pre-commit-config.yaml | 2 +- data_structures/binary_tree/avl_tree.py | 4 ---- machine_learning/polymonial_regression.py | 1 - 3 files changed, 1 insertion(+), 6 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 82aad6c65a9b..58cec4ff6ee6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/charliermarsh/ruff-pre-commit - rev: v0.0.255 + rev: v0.0.257 hooks: - id: ruff diff --git a/data_structures/binary_tree/avl_tree.py b/data_structures/binary_tree/avl_tree.py index 320e7ed0d792..4c1fb17afe86 100644 --- a/data_structures/binary_tree/avl_tree.py +++ b/data_structures/binary_tree/avl_tree.py @@ -60,19 +60,15 @@ def get_height(self) -> int: def set_data(self, data: Any) -> None: self.data = data - return def set_left(self, node: MyNode | None) -> None: self.left = node - return def set_right(self, node: MyNode | None) -> None: self.right = node - return def set_height(self, height: int) -> None: self.height = height - return def get_height(node: MyNode | None) -> int: diff --git a/machine_learning/polymonial_regression.py b/machine_learning/polymonial_regression.py index 374c35f7f905..487fb814526f 100644 --- a/machine_learning/polymonial_regression.py +++ b/machine_learning/polymonial_regression.py @@ -34,7 +34,6 @@ def viz_polymonial(): plt.xlabel("Position level") plt.ylabel("Salary") plt.show() - return if __name__ == "__main__": From 7cdb011ba440a07768179bfaea190bddefc890d8 Mon Sep 17 00:00:00 2001 From: Genesis <128913081+KaixLina@users.noreply.github.com> Date: Sun, 26 Mar 2023 20:49:18 +0530 Subject: [PATCH 019/757] New gitter link added or replaced (#8551) * New gitter link added * ruff==0.0.258 * noqa: S310 * noqa: S310 * Update ruff.yml * Add Ruff rule S311 * Ruff v0.0.259 * return ("{:08x}" * 5).format(*self.h) * pickle.load(f) # noqa: S301 --------- Co-authored-by: Christian Clauss --- .github/stale.yml | 4 ++-- .pre-commit-config.yaml | 2 +- CONTRIBUTING.md | 4 ++-- README.md | 4 ++-- hashes/sha1.py | 2 +- machine_learning/sequential_minimum_optimization.py | 2 +- neural_network/convolution_neural_network.py | 2 +- project_euler/README.md | 2 +- pyproject.toml | 1 + web_programming/download_images_from_google_query.py | 2 +- 10 files changed, 13 insertions(+), 12 deletions(-) diff --git a/.github/stale.yml b/.github/stale.yml index 36ca56266b26..813f688348d8 100644 --- a/.github/stale.yml +++ b/.github/stale.yml @@ -45,7 +45,7 @@ pulls: closeComment: > Please reopen this pull request once you commit the changes requested or make improvements on the code. If this is not the case and you need - some help, feel free to seek help from our [Gitter](https://gitter.im/TheAlgorithms) + some help, feel free to seek help from our [Gitter](https://app.gitter.im/#/room/#TheAlgorithms_community:gitter.im) or ping one of the reviewers. Thank you for your contributions! issues: @@ -59,5 +59,5 @@ issues: closeComment: > Please reopen this issue once you add more information and updates here. If this is not the case and you need some help, feel free to seek help - from our [Gitter](https://gitter.im/TheAlgorithms) or ping one of the + from our [Gitter](https://app.gitter.im/#/room/#TheAlgorithms_community:gitter.im) or ping one of the reviewers. Thank you for your contributions! diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 58cec4ff6ee6..72a878387e15 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/charliermarsh/ruff-pre-commit - rev: v0.0.257 + rev: v0.0.259 hooks: - id: ruff diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6b6e4d21bfc7..75e4fb893723 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,7 +2,7 @@ ## Before contributing -Welcome to [TheAlgorithms/Python](https://github.com/TheAlgorithms/Python)! Before sending your pull requests, make sure that you __read the whole guidelines__. If you have any doubt on the contributing guide, please feel free to [state it clearly in an issue](https://github.com/TheAlgorithms/Python/issues/new) or ask the community in [Gitter](https://gitter.im/TheAlgorithms). +Welcome to [TheAlgorithms/Python](https://github.com/TheAlgorithms/Python)! Before sending your pull requests, make sure that you __read the whole guidelines__. If you have any doubt on the contributing guide, please feel free to [state it clearly in an issue](https://github.com/TheAlgorithms/Python/issues/new) or ask the community in [Gitter](https://app.gitter.im/#/room/#TheAlgorithms_community:gitter.im). ## Contributing @@ -176,7 +176,7 @@ We want your work to be readable by others; therefore, we encourage you to note - Most importantly, - __Be consistent in the use of these guidelines when submitting.__ - - __Join__ us on [Discord](https://discord.com/invite/c7MnfGFGa6) and [Gitter](https://gitter.im/TheAlgorithms) __now!__ + - __Join__ us on [Discord](https://discord.com/invite/c7MnfGFGa6) and [Gitter](https://app.gitter.im/#/room/#TheAlgorithms_community:gitter.im) __now!__ - Happy coding! Writer [@poyea](https://github.com/poyea), Jun 2019. diff --git a/README.md b/README.md index 68a6e5e6fbce..3d2f1a110780 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Discord chat - + Gitter chat @@ -42,7 +42,7 @@ Read through our [Contribution Guidelines](CONTRIBUTING.md) before you contribut ## Community Channels -We are on [Discord](https://discord.gg/c7MnfGFGa6) and [Gitter](https://gitter.im/TheAlgorithms)! Community channels are a great way for you to ask questions and get help. Please join us! +We are on [Discord](https://discord.gg/c7MnfGFGa6) and [Gitter](https://app.gitter.im/#/room/#TheAlgorithms_community:gitter.im)! Community channels are a great way for you to ask questions and get help. Please join us! ## List of Algorithms diff --git a/hashes/sha1.py b/hashes/sha1.py index 9f0437f208fa..b325ce3e43bb 100644 --- a/hashes/sha1.py +++ b/hashes/sha1.py @@ -124,7 +124,7 @@ def final_hash(self): self.h[3] + d & 0xFFFFFFFF, self.h[4] + e & 0xFFFFFFFF, ) - return "%08x%08x%08x%08x%08x" % tuple(self.h) + return ("{:08x}" * 5).format(*self.h) def test_sha1_hash(): diff --git a/machine_learning/sequential_minimum_optimization.py b/machine_learning/sequential_minimum_optimization.py index b68bd52f4de9..b24f5669e2e8 100644 --- a/machine_learning/sequential_minimum_optimization.py +++ b/machine_learning/sequential_minimum_optimization.py @@ -458,7 +458,7 @@ def test_cancel_data(): CANCER_DATASET_URL, headers={"User-Agent": "Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)"}, ) - response = urllib.request.urlopen(request) + response = urllib.request.urlopen(request) # noqa: S310 content = response.read().decode("utf-8") with open(r"cancel_data.csv", "w") as f: f.write(content) diff --git a/neural_network/convolution_neural_network.py b/neural_network/convolution_neural_network.py index bd0550212157..f5ec156f3593 100644 --- a/neural_network/convolution_neural_network.py +++ b/neural_network/convolution_neural_network.py @@ -77,7 +77,7 @@ def save_model(self, save_path): def read_model(cls, model_path): # read saved model with open(model_path, "rb") as f: - model_dic = pickle.load(f) + model_dic = pickle.load(f) # noqa: S301 conv_get = model_dic.get("conv1") conv_get.append(model_dic.get("step_conv1")) diff --git a/project_euler/README.md b/project_euler/README.md index e3dc035eee5e..4832d0078ebf 100644 --- a/project_euler/README.md +++ b/project_euler/README.md @@ -10,7 +10,7 @@ The solutions will be checked by our [automated testing on GitHub Actions](https ## Solution Guidelines -Welcome to [TheAlgorithms/Python](https://github.com/TheAlgorithms/Python)! Before reading the solution guidelines, make sure you read the whole [Contributing Guidelines](https://github.com/TheAlgorithms/Python/blob/master/CONTRIBUTING.md) as it won't be repeated in here. If you have any doubt on the guidelines, please feel free to [state it clearly in an issue](https://github.com/TheAlgorithms/Python/issues/new) or ask the community in [Gitter](https://gitter.im/TheAlgorithms). You can use the [template](https://github.com/TheAlgorithms/Python/blob/master/project_euler/README.md#solution-template) we have provided below as your starting point but be sure to read the [Coding Style](https://github.com/TheAlgorithms/Python/blob/master/project_euler/README.md#coding-style) part first. +Welcome to [TheAlgorithms/Python](https://github.com/TheAlgorithms/Python)! Before reading the solution guidelines, make sure you read the whole [Contributing Guidelines](https://github.com/TheAlgorithms/Python/blob/master/CONTRIBUTING.md) as it won't be repeated in here. If you have any doubt on the guidelines, please feel free to [state it clearly in an issue](https://github.com/TheAlgorithms/Python/issues/new) or ask the community in [Gitter](https://app.gitter.im/#/room/#TheAlgorithms_community:gitter.im). You can use the [template](https://github.com/TheAlgorithms/Python/blob/master/project_euler/README.md#solution-template) we have provided below as your starting point but be sure to read the [Coding Style](https://github.com/TheAlgorithms/Python/blob/master/project_euler/README.md#coding-style) part first. ### Coding Style diff --git a/pyproject.toml b/pyproject.toml index 169c3a71ba6c..23fe45e97d20 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,6 +34,7 @@ ignore = [ # `ruff rule S101` for a description of that rule "S101", # S101: Use of `assert` detected -- DO NOT FIX "S105", # S105: Possible hardcoded password: 'password' "S113", # S113: Probable use of requests call without timeout + "S311", # S311: Standard pseudo-random generators are not suitable for cryptographic purposes "UP038", # UP038: Use `X | Y` in `{}` call instead of `(X, Y)` -- DO NOT FIX ] select = [ # https://beta.ruff.rs/docs/rules diff --git a/web_programming/download_images_from_google_query.py b/web_programming/download_images_from_google_query.py index 9c0c21dc804e..441347459f8e 100644 --- a/web_programming/download_images_from_google_query.py +++ b/web_programming/download_images_from_google_query.py @@ -86,7 +86,7 @@ def download_images_from_google_query(query: str = "dhaka", max_images: int = 5) path_name = f"query_{query.replace(' ', '_')}" if not os.path.exists(path_name): os.makedirs(path_name) - urllib.request.urlretrieve( + urllib.request.urlretrieve( # noqa: S310 original_size_img, f"{path_name}/original_size_img_{index}.jpg" ) return index From 86b2ab09aab359ef1b4bea58ed3c1fdf5b989500 Mon Sep 17 00:00:00 2001 From: Christian Veenhuis Date: Sun, 26 Mar 2023 18:20:47 +0200 Subject: [PATCH 020/757] Fix broken links to Gitter Community (Fixes: #8197) (#8546) Co-authored-by: Christian Clauss --- .github/stale.yml | 4 ++-- CONTRIBUTING.md | 4 ++-- README.md | 4 ++-- project_euler/README.md | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/stale.yml b/.github/stale.yml index 813f688348d8..0939e1f223ff 100644 --- a/.github/stale.yml +++ b/.github/stale.yml @@ -45,7 +45,7 @@ pulls: closeComment: > Please reopen this pull request once you commit the changes requested or make improvements on the code. If this is not the case and you need - some help, feel free to seek help from our [Gitter](https://app.gitter.im/#/room/#TheAlgorithms_community:gitter.im) + some help, feel free to seek help from our [Gitter](https://gitter.im/TheAlgorithms/community) or ping one of the reviewers. Thank you for your contributions! issues: @@ -59,5 +59,5 @@ issues: closeComment: > Please reopen this issue once you add more information and updates here. If this is not the case and you need some help, feel free to seek help - from our [Gitter](https://app.gitter.im/#/room/#TheAlgorithms_community:gitter.im) or ping one of the + from our [Gitter](https://gitter.im/TheAlgorithms/community) or ping one of the reviewers. Thank you for your contributions! diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 75e4fb893723..2bb0c2e39eee 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,7 +2,7 @@ ## Before contributing -Welcome to [TheAlgorithms/Python](https://github.com/TheAlgorithms/Python)! Before sending your pull requests, make sure that you __read the whole guidelines__. If you have any doubt on the contributing guide, please feel free to [state it clearly in an issue](https://github.com/TheAlgorithms/Python/issues/new) or ask the community in [Gitter](https://app.gitter.im/#/room/#TheAlgorithms_community:gitter.im). +Welcome to [TheAlgorithms/Python](https://github.com/TheAlgorithms/Python)! Before sending your pull requests, make sure that you __read the whole guidelines__. If you have any doubt on the contributing guide, please feel free to [state it clearly in an issue](https://github.com/TheAlgorithms/Python/issues/new) or ask the community in [Gitter](https://gitter.im/TheAlgorithms/community). ## Contributing @@ -176,7 +176,7 @@ We want your work to be readable by others; therefore, we encourage you to note - Most importantly, - __Be consistent in the use of these guidelines when submitting.__ - - __Join__ us on [Discord](https://discord.com/invite/c7MnfGFGa6) and [Gitter](https://app.gitter.im/#/room/#TheAlgorithms_community:gitter.im) __now!__ + - __Join__ us on [Discord](https://discord.com/invite/c7MnfGFGa6) and [Gitter](https://gitter.im/TheAlgorithms/community) __now!__ - Happy coding! Writer [@poyea](https://github.com/poyea), Jun 2019. diff --git a/README.md b/README.md index 3d2f1a110780..bf6e0ed3cf75 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Discord chat - + Gitter chat @@ -42,7 +42,7 @@ Read through our [Contribution Guidelines](CONTRIBUTING.md) before you contribut ## Community Channels -We are on [Discord](https://discord.gg/c7MnfGFGa6) and [Gitter](https://app.gitter.im/#/room/#TheAlgorithms_community:gitter.im)! Community channels are a great way for you to ask questions and get help. Please join us! +We are on [Discord](https://discord.gg/c7MnfGFGa6) and [Gitter](https://gitter.im/TheAlgorithms/community)! Community channels are a great way for you to ask questions and get help. Please join us! ## List of Algorithms diff --git a/project_euler/README.md b/project_euler/README.md index 4832d0078ebf..16865edf2a67 100644 --- a/project_euler/README.md +++ b/project_euler/README.md @@ -10,7 +10,7 @@ The solutions will be checked by our [automated testing on GitHub Actions](https ## Solution Guidelines -Welcome to [TheAlgorithms/Python](https://github.com/TheAlgorithms/Python)! Before reading the solution guidelines, make sure you read the whole [Contributing Guidelines](https://github.com/TheAlgorithms/Python/blob/master/CONTRIBUTING.md) as it won't be repeated in here. If you have any doubt on the guidelines, please feel free to [state it clearly in an issue](https://github.com/TheAlgorithms/Python/issues/new) or ask the community in [Gitter](https://app.gitter.im/#/room/#TheAlgorithms_community:gitter.im). You can use the [template](https://github.com/TheAlgorithms/Python/blob/master/project_euler/README.md#solution-template) we have provided below as your starting point but be sure to read the [Coding Style](https://github.com/TheAlgorithms/Python/blob/master/project_euler/README.md#coding-style) part first. +Welcome to [TheAlgorithms/Python](https://github.com/TheAlgorithms/Python)! Before reading the solution guidelines, make sure you read the whole [Contributing Guidelines](https://github.com/TheAlgorithms/Python/blob/master/CONTRIBUTING.md) as it won't be repeated in here. If you have any doubt on the guidelines, please feel free to [state it clearly in an issue](https://github.com/TheAlgorithms/Python/issues/new) or ask the community in [Gitter](https://gitter.im/TheAlgorithms/community). You can use the [template](https://github.com/TheAlgorithms/Python/blob/master/project_euler/README.md#solution-template) we have provided below as your starting point but be sure to read the [Coding Style](https://github.com/TheAlgorithms/Python/blob/master/project_euler/README.md#coding-style) part first. ### Coding Style From ac111ee463065e372ad148dbafba630045ecf94c Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Wed, 29 Mar 2023 00:41:54 +0300 Subject: [PATCH 021/757] Reduce the complexity of graphs/bi_directional_dijkstra.py (#8165) * Reduce the complexity of graphs/bi_directional_dijkstra.py * Try to lower the --max-complexity threshold in the file .flake8 * Lower the --max-complexity threshold in the file .flake8 * updating DIRECTORY.md * updating DIRECTORY.md * Try to lower max-complexity * Try to lower max-complexity * Try to lower max-complexity --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- graphs/bi_directional_dijkstra.py | 95 +++++++++++++++++-------------- pyproject.toml | 2 +- 2 files changed, 53 insertions(+), 44 deletions(-) diff --git a/graphs/bi_directional_dijkstra.py b/graphs/bi_directional_dijkstra.py index fc53e2f0d8f3..a4489026be80 100644 --- a/graphs/bi_directional_dijkstra.py +++ b/graphs/bi_directional_dijkstra.py @@ -17,6 +17,32 @@ import numpy as np +def pass_and_relaxation( + graph: dict, + v: str, + visited_forward: set, + visited_backward: set, + cst_fwd: dict, + cst_bwd: dict, + queue: PriorityQueue, + parent: dict, + shortest_distance: float | int, +) -> float | int: + for nxt, d in graph[v]: + if nxt in visited_forward: + continue + old_cost_f = cst_fwd.get(nxt, np.inf) + new_cost_f = cst_fwd[v] + d + if new_cost_f < old_cost_f: + queue.put((new_cost_f, nxt)) + cst_fwd[nxt] = new_cost_f + parent[nxt] = v + if nxt in visited_backward: + if cst_fwd[v] + d + cst_bwd[nxt] < shortest_distance: + shortest_distance = cst_fwd[v] + d + cst_bwd[nxt] + return shortest_distance + + def bidirectional_dij( source: str, destination: str, graph_forward: dict, graph_backward: dict ) -> int: @@ -51,53 +77,36 @@ def bidirectional_dij( if source == destination: return 0 - while queue_forward and queue_backward: - while not queue_forward.empty(): - _, v_fwd = queue_forward.get() - - if v_fwd not in visited_forward: - break - else: - break + while not queue_forward.empty() and not queue_backward.empty(): + _, v_fwd = queue_forward.get() visited_forward.add(v_fwd) - while not queue_backward.empty(): - _, v_bwd = queue_backward.get() - - if v_bwd not in visited_backward: - break - else: - break + _, v_bwd = queue_backward.get() visited_backward.add(v_bwd) - # forward pass and relaxation - for nxt_fwd, d_forward in graph_forward[v_fwd]: - if nxt_fwd in visited_forward: - continue - old_cost_f = cst_fwd.get(nxt_fwd, np.inf) - new_cost_f = cst_fwd[v_fwd] + d_forward - if new_cost_f < old_cost_f: - queue_forward.put((new_cost_f, nxt_fwd)) - cst_fwd[nxt_fwd] = new_cost_f - parent_forward[nxt_fwd] = v_fwd - if nxt_fwd in visited_backward: - if cst_fwd[v_fwd] + d_forward + cst_bwd[nxt_fwd] < shortest_distance: - shortest_distance = cst_fwd[v_fwd] + d_forward + cst_bwd[nxt_fwd] - - # backward pass and relaxation - for nxt_bwd, d_backward in graph_backward[v_bwd]: - if nxt_bwd in visited_backward: - continue - old_cost_b = cst_bwd.get(nxt_bwd, np.inf) - new_cost_b = cst_bwd[v_bwd] + d_backward - if new_cost_b < old_cost_b: - queue_backward.put((new_cost_b, nxt_bwd)) - cst_bwd[nxt_bwd] = new_cost_b - parent_backward[nxt_bwd] = v_bwd - - if nxt_bwd in visited_forward: - if cst_bwd[v_bwd] + d_backward + cst_fwd[nxt_bwd] < shortest_distance: - shortest_distance = cst_bwd[v_bwd] + d_backward + cst_fwd[nxt_bwd] + shortest_distance = pass_and_relaxation( + graph_forward, + v_fwd, + visited_forward, + visited_backward, + cst_fwd, + cst_bwd, + queue_forward, + parent_forward, + shortest_distance, + ) + + shortest_distance = pass_and_relaxation( + graph_backward, + v_bwd, + visited_backward, + visited_forward, + cst_bwd, + cst_fwd, + queue_backward, + parent_backward, + shortest_distance, + ) if cst_fwd[v_fwd] + cst_bwd[v_bwd] >= shortest_distance: break diff --git a/pyproject.toml b/pyproject.toml index 23fe45e97d20..48c3fbd4009d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -61,7 +61,7 @@ show-source = true target-version = "py311" [tool.ruff.mccabe] # DO NOT INCREASE THIS VALUE -max-complexity = 20 # default: 10 +max-complexity = 17 # default: 10 [tool.ruff.pylint] # DO NOT INCREASE THESE VALUES max-args = 10 # default: 5 From a71f22dae54f830dbf68b3bd5e5e8d540e338a4c Mon Sep 17 00:00:00 2001 From: Rohan Anand <96521078+rohan472000@users.noreply.github.com> Date: Thu, 30 Mar 2023 10:39:21 +0530 Subject: [PATCH 022/757] Update cnn_classification.py (#8570) --- computer_vision/cnn_classification.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/computer_vision/cnn_classification.py b/computer_vision/cnn_classification.py index 1c193fcbb50b..9b5f8c95eebf 100644 --- a/computer_vision/cnn_classification.py +++ b/computer_vision/cnn_classification.py @@ -93,7 +93,7 @@ test_image = tf.keras.preprocessing.image.img_to_array(test_image) test_image = np.expand_dims(test_image, axis=0) result = classifier.predict(test_image) - training_set.class_indices + # training_set.class_indices if result[0][0] == 0: prediction = "Normal" if result[0][0] == 1: From a00492911a949a1e59072367bbabee22cd884106 Mon Sep 17 00:00:00 2001 From: Rohan Anand <96521078+rohan472000@users.noreply.github.com> Date: Fri, 31 Mar 2023 16:47:13 +0530 Subject: [PATCH 023/757] added a problem on kadane's algo and its solution. (#8569) * added kadane's algorithm directory with one problem's solution. * added type hints * Rename kaadne_algorithm/max_product_subarray.py to dynamic_programming/max_product_subarray.py * Update dynamic_programming/max_product_subarray.py Co-authored-by: Christian Clauss * Update max_product_subarray.py * Update max_product_subarray.py * Update dynamic_programming/max_product_subarray.py Co-authored-by: Christian Clauss * Update max_product_subarray.py * Update max_product_subarray.py * Update max_product_subarray.py * Update max_product_subarray.py * Update max_product_subarray.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update max_product_subarray.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update max_product_subarray.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update max_product_subarray.py * Update max_product_subarray.py * Update dynamic_programming/max_product_subarray.py Co-authored-by: Christian Clauss * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update dynamic_programming/max_product_subarray.py Co-authored-by: Christian Clauss * Update max_product_subarray.py --------- Co-authored-by: Christian Clauss Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- dynamic_programming/max_product_subarray.py | 53 +++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 dynamic_programming/max_product_subarray.py diff --git a/dynamic_programming/max_product_subarray.py b/dynamic_programming/max_product_subarray.py new file mode 100644 index 000000000000..425859bc03e3 --- /dev/null +++ b/dynamic_programming/max_product_subarray.py @@ -0,0 +1,53 @@ +def max_product_subarray(numbers: list[int]) -> int: + """ + Returns the maximum product that can be obtained by multiplying a + contiguous subarray of the given integer list `nums`. + + Example: + >>> max_product_subarray([2, 3, -2, 4]) + 6 + >>> max_product_subarray((-2, 0, -1)) + 0 + >>> max_product_subarray([2, 3, -2, 4, -1]) + 48 + >>> max_product_subarray([-1]) + -1 + >>> max_product_subarray([0]) + 0 + >>> max_product_subarray([]) + 0 + >>> max_product_subarray("") + 0 + >>> max_product_subarray(None) + 0 + >>> max_product_subarray([2, 3, -2, 4.5, -1]) + Traceback (most recent call last): + ... + ValueError: numbers must be an iterable of integers + >>> max_product_subarray("ABC") + Traceback (most recent call last): + ... + ValueError: numbers must be an iterable of integers + """ + if not numbers: + return 0 + + if not isinstance(numbers, (list, tuple)) or not all( + isinstance(number, int) for number in numbers + ): + raise ValueError("numbers must be an iterable of integers") + + max_till_now = min_till_now = max_prod = numbers[0] + + for i in range(1, len(numbers)): + # update the maximum and minimum subarray products + number = numbers[i] + if number < 0: + max_till_now, min_till_now = min_till_now, max_till_now + max_till_now = max(number, max_till_now * number) + min_till_now = min(number, min_till_now * number) + + # update the maximum product found till now + max_prod = max(max_prod, max_till_now) + + return max_prod From 238fe8c494ab5be80c96441095d1c8958f95c04d Mon Sep 17 00:00:00 2001 From: NIKITA PANDEY <113332472+nikitapandeyy@users.noreply.github.com> Date: Fri, 31 Mar 2023 19:38:13 +0530 Subject: [PATCH 024/757] Update receive_file.py (#8541) * Update receive_file.py Here are the changes I made: Added the main() function and called it from if __name__ == "__main__" block. This makes it easier to test the code and import it into other programs. Added socket.AF_INET as the first argument to socket.socket(). This specifies the address family to be used, which is necessary when using connect(). Changed print(f"{data = }") to print("Received:", len(data), "bytes"). This makes it clearer what's happening and how much data is being received. Changed the final print statement to "Successfully received the file". This makes it more accurate and descriptive. Moved the import statement to the top of the file. This is a common convention in Python. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- file_transfer/receive_file.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/file_transfer/receive_file.py b/file_transfer/receive_file.py index 37a503036dc2..f50ad9fe1107 100644 --- a/file_transfer/receive_file.py +++ b/file_transfer/receive_file.py @@ -1,8 +1,9 @@ -if __name__ == "__main__": - import socket # Import socket module +import socket + - sock = socket.socket() # Create a socket object - host = socket.gethostname() # Get local machine name +def main(): + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + host = socket.gethostname() port = 12312 sock.connect((host, port)) @@ -13,11 +14,14 @@ print("Receiving data...") while True: data = sock.recv(1024) - print(f"{data = }") if not data: break - out_file.write(data) # Write data to a file + out_file.write(data) - print("Successfully got the file") + print("Successfully received the file") sock.close() print("Connection closed") + + +if __name__ == "__main__": + main() From 5ce63b5966b6ad9c7ce36c449fb31112c3e1d084 Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Sat, 1 Apr 2023 01:11:24 -0400 Subject: [PATCH 025/757] Fix `mypy` errors in `lu_decomposition.py` (attempt 2) (#8100) * updating DIRECTORY.md * Fix mypy errors in lu_decomposition.py * Replace for-loops with comprehensions * Add explanation of LU decomposition and extra doctests Add an explanation of LU decomposition with conditions for when an LU decomposition exists Add extra doctests to handle each of the possible conditions for when a decomposition exists/doesn't exist * updating DIRECTORY.md * updating DIRECTORY.md --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- arithmetic_analysis/lu_decomposition.py | 91 ++++++++++++++++++------- 1 file changed, 65 insertions(+), 26 deletions(-) diff --git a/arithmetic_analysis/lu_decomposition.py b/arithmetic_analysis/lu_decomposition.py index 217719cf4da1..941c1dadf556 100644 --- a/arithmetic_analysis/lu_decomposition.py +++ b/arithmetic_analysis/lu_decomposition.py @@ -1,62 +1,101 @@ -"""Lower-Upper (LU) Decomposition. +""" +Lower–upper (LU) decomposition factors a matrix as a product of a lower +triangular matrix and an upper triangular matrix. A square matrix has an LU +decomposition under the following conditions: + - If the matrix is invertible, then it has an LU decomposition if and only + if all of its leading principal minors are non-zero (see + https://en.wikipedia.org/wiki/Minor_(linear_algebra) for an explanation of + leading principal minors of a matrix). + - If the matrix is singular (i.e., not invertible) and it has a rank of k + (i.e., it has k linearly independent columns), then it has an LU + decomposition if its first k leading principal minors are non-zero. + +This algorithm will simply attempt to perform LU decomposition on any square +matrix and raise an error if no such decomposition exists. -Reference: -- https://en.wikipedia.org/wiki/LU_decomposition +Reference: https://en.wikipedia.org/wiki/LU_decomposition """ from __future__ import annotations import numpy as np -from numpy import float64 -from numpy.typing import ArrayLike - -def lower_upper_decomposition( - table: ArrayLike[float64], -) -> tuple[ArrayLike[float64], ArrayLike[float64]]: - """Lower-Upper (LU) Decomposition - - Example: +def lower_upper_decomposition(table: np.ndarray) -> tuple[np.ndarray, np.ndarray]: + """ + Perform LU decomposition on a given matrix and raises an error if the matrix + isn't square or if no such decomposition exists >>> matrix = np.array([[2, -2, 1], [0, 1, 2], [5, 3, 1]]) - >>> outcome = lower_upper_decomposition(matrix) - >>> outcome[0] + >>> lower_mat, upper_mat = lower_upper_decomposition(matrix) + >>> lower_mat array([[1. , 0. , 0. ], [0. , 1. , 0. ], [2.5, 8. , 1. ]]) - >>> outcome[1] + >>> upper_mat array([[ 2. , -2. , 1. ], [ 0. , 1. , 2. ], [ 0. , 0. , -17.5]]) + >>> matrix = np.array([[4, 3], [6, 3]]) + >>> lower_mat, upper_mat = lower_upper_decomposition(matrix) + >>> lower_mat + array([[1. , 0. ], + [1.5, 1. ]]) + >>> upper_mat + array([[ 4. , 3. ], + [ 0. , -1.5]]) + + # Matrix is not square >>> matrix = np.array([[2, -2, 1], [0, 1, 2]]) - >>> lower_upper_decomposition(matrix) + >>> lower_mat, upper_mat = lower_upper_decomposition(matrix) Traceback (most recent call last): ... ValueError: 'table' has to be of square shaped array but got a 2x3 array: [[ 2 -2 1] [ 0 1 2]] + + # Matrix is invertible, but its first leading principal minor is 0 + >>> matrix = np.array([[0, 1], [1, 0]]) + >>> lower_mat, upper_mat = lower_upper_decomposition(matrix) + Traceback (most recent call last): + ... + ArithmeticError: No LU decomposition exists + + # Matrix is singular, but its first leading principal minor is 1 + >>> matrix = np.array([[1, 0], [1, 0]]) + >>> lower_mat, upper_mat = lower_upper_decomposition(matrix) + >>> lower_mat + array([[1., 0.], + [1., 1.]]) + >>> upper_mat + array([[1., 0.], + [0., 0.]]) + + # Matrix is singular, but its first leading principal minor is 0 + >>> matrix = np.array([[0, 1], [0, 1]]) + >>> lower_mat, upper_mat = lower_upper_decomposition(matrix) + Traceback (most recent call last): + ... + ArithmeticError: No LU decomposition exists """ - # Table that contains our data - # Table has to be a square array so we need to check first + # Ensure that table is a square array rows, columns = np.shape(table) if rows != columns: raise ValueError( - f"'table' has to be of square shaped array but got a {rows}x{columns} " - + f"array:\n{table}" + f"'table' has to be of square shaped array but got a " + f"{rows}x{columns} array:\n{table}" ) + lower = np.zeros((rows, columns)) upper = np.zeros((rows, columns)) for i in range(columns): for j in range(i): - total = 0 - for k in range(j): - total += lower[i][k] * upper[k][j] + total = sum(lower[i][k] * upper[k][j] for k in range(j)) + if upper[j][j] == 0: + raise ArithmeticError("No LU decomposition exists") lower[i][j] = (table[i][j] - total) / upper[j][j] lower[i][i] = 1 for j in range(i, columns): - total = 0 - for k in range(i): - total += lower[i][k] * upper[k][j] + total = sum(lower[i][k] * upper[k][j] for k in range(j)) upper[i][j] = table[i][j] - total return lower, upper From dc4f603dad22eab31892855555999b552e97e9d8 Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Sat, 1 Apr 2023 08:47:24 +0300 Subject: [PATCH 026/757] Add Project Euler problem 187 solution 1 (#8182) Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- DIRECTORY.md | 2 + project_euler/problem_187/__init__.py | 0 project_euler/problem_187/sol1.py | 58 +++++++++++++++++++++++++++ 3 files changed, 60 insertions(+) create mode 100644 project_euler/problem_187/__init__.py create mode 100644 project_euler/problem_187/sol1.py diff --git a/DIRECTORY.md b/DIRECTORY.md index 1d3177801a2c..1a641d8ecb59 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -990,6 +990,8 @@ * [Sol1](project_euler/problem_174/sol1.py) * Problem 180 * [Sol1](project_euler/problem_180/sol1.py) + * Problem 187 + * [Sol1](project_euler/problem_187/sol1.py) * Problem 188 * [Sol1](project_euler/problem_188/sol1.py) * Problem 191 diff --git a/project_euler/problem_187/__init__.py b/project_euler/problem_187/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_187/sol1.py b/project_euler/problem_187/sol1.py new file mode 100644 index 000000000000..12f03e2a7023 --- /dev/null +++ b/project_euler/problem_187/sol1.py @@ -0,0 +1,58 @@ +""" +Project Euler Problem 187: https://projecteuler.net/problem=187 + +A composite is a number containing at least two prime factors. +For example, 15 = 3 x 5; 9 = 3 x 3; 12 = 2 x 2 x 3. + +There are ten composites below thirty containing precisely two, +not necessarily distinct, prime factors: 4, 6, 9, 10, 14, 15, 21, 22, 25, 26. + +How many composite integers, n < 10^8, have precisely two, +not necessarily distinct, prime factors? +""" + +from math import isqrt + + +def calculate_prime_numbers(max_number: int) -> list[int]: + """ + Returns prime numbers below max_number + + >>> calculate_prime_numbers(10) + [2, 3, 5, 7] + """ + + is_prime = [True] * max_number + for i in range(2, isqrt(max_number - 1) + 1): + if is_prime[i]: + for j in range(i**2, max_number, i): + is_prime[j] = False + + return [i for i in range(2, max_number) if is_prime[i]] + + +def solution(max_number: int = 10**8) -> int: + """ + Returns the number of composite integers below max_number have precisely two, + not necessarily distinct, prime factors + + >>> solution(30) + 10 + """ + + prime_numbers = calculate_prime_numbers(max_number // 2) + + semiprimes_count = 0 + left = 0 + right = len(prime_numbers) - 1 + while left <= right: + while prime_numbers[left] * prime_numbers[right] >= max_number: + right -= 1 + semiprimes_count += right - left + 1 + left += 1 + + return semiprimes_count + + +if __name__ == "__main__": + print(f"{solution() = }") From e4d90e2d5b92fdcff558f1848843dfbe20d81035 Mon Sep 17 00:00:00 2001 From: amirsoroush <114881632+amirsoroush@users.noreply.github.com> Date: Sat, 1 Apr 2023 09:26:43 +0300 Subject: [PATCH 027/757] change space complexity of linked list's __len__ from O(n) to O(1) (#8183) --- data_structures/linked_list/circular_linked_list.py | 2 +- data_structures/linked_list/doubly_linked_list.py | 2 +- data_structures/linked_list/merge_two_lists.py | 2 +- data_structures/linked_list/singly_linked_list.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/data_structures/linked_list/circular_linked_list.py b/data_structures/linked_list/circular_linked_list.py index 67a63cd55e19..9092fb29e3ff 100644 --- a/data_structures/linked_list/circular_linked_list.py +++ b/data_structures/linked_list/circular_linked_list.py @@ -24,7 +24,7 @@ def __iter__(self) -> Iterator[Any]: break def __len__(self) -> int: - return len(tuple(iter(self))) + return sum(1 for _ in self) def __repr__(self): return "->".join(str(item) for item in iter(self)) diff --git a/data_structures/linked_list/doubly_linked_list.py b/data_structures/linked_list/doubly_linked_list.py index 6c81493fff85..41d07d63e005 100644 --- a/data_structures/linked_list/doubly_linked_list.py +++ b/data_structures/linked_list/doubly_linked_list.py @@ -51,7 +51,7 @@ def __len__(self): >>> len(linked_list) == 5 True """ - return len(tuple(iter(self))) + return sum(1 for _ in self) def insert_at_head(self, data): self.insert_at_nth(0, data) diff --git a/data_structures/linked_list/merge_two_lists.py b/data_structures/linked_list/merge_two_lists.py index 61e2412aa7fd..ca0d3bb48540 100644 --- a/data_structures/linked_list/merge_two_lists.py +++ b/data_structures/linked_list/merge_two_lists.py @@ -44,7 +44,7 @@ def __len__(self) -> int: >>> len(SortedLinkedList(test_data_odd)) 8 """ - return len(tuple(iter(self))) + return sum(1 for _ in self) def __str__(self) -> str: """ diff --git a/data_structures/linked_list/singly_linked_list.py b/data_structures/linked_list/singly_linked_list.py index bdeb5922ac67..a8f9e8ebb977 100644 --- a/data_structures/linked_list/singly_linked_list.py +++ b/data_structures/linked_list/singly_linked_list.py @@ -72,7 +72,7 @@ def __len__(self) -> int: >>> len(linked_list) 0 """ - return len(tuple(iter(self))) + return sum(1 for _ in self) def __repr__(self) -> str: """ From 9e0c357a57f76abc354d704012040f3f5511a941 Mon Sep 17 00:00:00 2001 From: Dhruv Manilawala Date: Sat, 1 Apr 2023 11:59:26 +0530 Subject: [PATCH 028/757] chore: additional Project Euler solution hash (#8593) --- scripts/project_euler_answers.json | 109 ++++++++++++++++++++++++++++- 1 file changed, 108 insertions(+), 1 deletion(-) diff --git a/scripts/project_euler_answers.json b/scripts/project_euler_answers.json index 6d354363ee5f..f2b876934766 100644 --- a/scripts/project_euler_answers.json +++ b/scripts/project_euler_answers.json @@ -723,5 +723,112 @@ "722": "9687101dfe209fd65f57a10603baa38ba83c9152e43a8b802b96f1e07f568e0e", "723": "74832787e7d4e0cb7991256c8f6d02775dffec0684de234786f25f898003f2de", "724": "fa05e2b497e7eafa64574017a4c45aadef6b163d907b03d63ba3f4021096d329", - "725": "005c873563f51bbebfdb1f8dbc383259e9a98e506bc87ae8d8c9044b81fc6418" + "725": "005c873563f51bbebfdb1f8dbc383259e9a98e506bc87ae8d8c9044b81fc6418", + "726": "93e41c533136bf4b436e493090fd4e7b277234db2a69c62a871f775ff26681bf", + "727": "c366f7426ca9351dcdde2e3bea01181897cda4d9b44977678ea3828419b84851", + "728": "8de62a644511d27c7c23c7722f56112b3c1ab9b05a078a98a0891f09f92464c6", + "729": "0ae82177174eef99fc80a2ec921295f61a6ac4dfed86a1bf333a50c26d01955c", + "730": "78cd876a176c8fbf7c2155b80dccbdededdbc43c28ef17b5a6e554d649325d38", + "731": "54afb9f829be51d29f90eecbfe40e5ba91f3a3bf538de62f3e34674af15eb542", + "732": "c4dc4610dcafc806b30e5d3f5560b57f462218a04397809843a7110838f0ebac", + "733": "bdde7d98d057d6a6ae360fd2f872d8bccb7e7f2971df37a3c5f20712ea3c618f", + "734": "9a514875bd9af26fcc565337771f852d311cd77033186e4d957e7b6c7b8ce018", + "735": "8bbc5a27c0031d8c44f3f73c99622a202cd6ea9a080049d615a7ae80ce6024f9", + "736": "e0d4c78b9b3dae51940877aff28275d036eccfc641111c8e34227ff6015a0fab", + "737": "a600884bcaa01797310c83b198bad58c98530289305af29b0bf75f679af38d3a", + "738": "c85f15fdaafe7d5525acff960afef7e4b8ffded5a7ee0d1dc2b0e8d0c26b9b46", + "739": "8716e9302f0fb90153e2f522bd88a710361a897480e4ccc0542473c704793518", + "740": "6ff41ee34b263b742cda109aee3be9ad6c95eec2ce31d6a9fc5353bba1b41afd", + "741": "99ac0eb9589b895e5755895206bbad5febd6bc29b2912df1c7544c547e26bca3", + "742": "7d2761a240aa577348df4813ea248088d0d6d8d421142c712ed576cdc90d4df9", + "743": "d93c42a129c0961b4e36738efae3b7e8ffae3a4daeced20e85bb740d3d72522d", + "744": "211f76700a010461486dde6c723720be85e68c192cd8a8ed0a88860b8ae9b0f0", + "745": "2d32dc1fea2f1b8600c0ada927b057b566870ceb5362cce71ac3693dcb7136ae", + "746": "2df1c2a0181f0c25e8d13d2a1eadba55a6b06267a2b22075fcf6867fb2e10c02", + "747": "a8d8f93142e320c6f0dd386c7a3bfb011bbdc15b85291a9be8f0266b3608175e", + "748": "7de937e04c10386b240afb8bb2ff590009946df8b7850a0329ccdb59fca8955f", + "749": "1a55f5484ccf964aeb186faedefa01db05d87180891dc2280b6eb85b6efb4779", + "750": "fa4318c213179e6af1c949be7cf47210f4383e0a44d191e2bad44228d3192f14", + "751": "12fe650fcb3afc214b3d647c655070e8142cfd397441fc7636ad7e6ffcaefde2", + "752": "e416c0123bc6b82df8726b328494db31aa4781d938a0a6e2107b1e44c73c0434", + "753": "0ee3299bc89e1e4c2fc79285fb1cd84c887456358a825e56be92244b7115f5af", + "754": "1370574b16207c41d3dafb62aa898379ec101ac36843634b1633b7b509d4c35a", + "755": "78bb4b18b13f5254cfafe872c0e93791ab5206b2851960dc6aebea8f62b9580c", + "756": "6becaabbda2e9ea22373e62e989b6b70467efa24fbe2f0d124d7a99a53e93f74", + "757": "fbfee0a5c4fa57a1dd6cf0c9bb2423cf7e7bcb130e67114aa360e42234987314", + "758": "8e4dfc259cec9dfd89d4b4ac8c33c75af6e0f5f7926526ee22ad4d45f93d3c18", + "759": "40bac0ed2e4f7861a6d9a2d87191a9034e177c319aa40a43638cc1b69572e5f2", + "760": "7ab50386a211f0815593389ab05b57a1a5eb5cbf5b9a85fe4afc517dcab74e06", + "761": "1cdb0318ac16e11c8d2ae7b1d7ca7138f7b1a461e9d75bd69be0f9cdd3add0c5", + "762": "84c4662267d5809380a540dfc2881665b3019047d74d5ef0a01f86e45f4b5b59", + "763": "f0def5903139447fabe7d106db5fff660d94b45af7b8b48d789596cf65ab2514", + "764": "7b4131f4d1e13d091ca7dd4d32317a14a2a24e6e1abd214df1c14c215287b330", + "765": "7558b775727426bccd945f5aa6b3e131e6034a7b1ff8576332329ef65d6a1663", + "766": "23c309430fa9546adb617457dbfd30fb7432904595c8c000e9b67ea23f32a53b", + "767": "70aef22ac2db8a5bdfcc42ff8dafbd2901e85e268f5f3c45085aa40c590b1d42", + "768": "b69a808dfc654b037e2f47ace16f48fe3bb553b3c8eed3e2b6421942fbf521d0", + "769": "78537a30577e806c6d8d94725e54d2d52e56f7f39f89c133cd5d0a2aad7e46e4", + "770": "c9d80c19c4895d1498bf809fcc37c447fa961fb325e5667eb35d6aa992966b41", + "771": "9803ace30c0d90d422e703fdf25a10a9342d0178a277ebc20c7bd6feac4c7a15", + "772": "f5a1e391af815ea6453db58a1bd71790f433c44ed63e5e93d8f5c045dfd5a464", + "773": "e1b93fc323c4d9c383100603339548e1e56ce9c38bcdcc425024c12b862ea8cb", + "774": "3646cd098b213014fb7bbc9597871585e62ee0cf2770e141f1df771237cc09ab", + "775": "d9d7d515ce7350c9e5696d85f68bbb42daa74b9e171a601dd04c823b18bb7757", + "776": "83286074d3bc86a5b449facb5fe5eafc91eb4c8031e2fb5e716443402cd8ed0f", + "777": "e62616a387d05b619d47cee3d49d5d2db19393736bf54b6cdd20933c0531cb7e", + "778": "d4de958ba44d25353de5b380e04d06c7968794ad50dbf6231ad0049ff53e106b", + "779": "c08ce54a59afc4af62f28b80a9c9a5190822d124eed8d73fd6db3e19c81e2157", + "780": "fc7ba646c16482f0f4f5ce2b06d21183dba2bdeaf9469b36b55bc7bc2d87baf3", + "781": "8fa5733f06838fb61b55b3e9d59c5061d922147e59947fe52e566dd975b2199f", + "782": "9f757d92df401ee049bc066bb2625c6287e5e4bcd38c958396a77a578f036a24", + "783": "270ff37f60c267a673bd4b223e44941f01ae9cfbf6bbdf99ca57af89b1e9a66f", + "784": "388b17c4c7b829cef767f83b4686c903faeec1241edfe5f58ee91d2b0c7f8dfc", + "785": "77cf600204c5265e1d5d3d26bf28ba1e92e6f24def040c16977450bec8b1cb99", + "786": "fb14022b7edbc6c7bfde27f35b49f6acaa4f0fc383af27614cb9d4a1980e626b", + "787": "7516ba0ac1951665723dcc4adcc52764d9497e7b6ed30bdb9937ac9df82b7c4f", + "788": "adede1d30258bb0f353af11f559b67f8b823304c71e967f52db52d002760c24f", + "789": "0c82e744a1f9bc57fd8ae8b2f479998455bc45126de971c59b68541c254e303a", + "790": "319847122251afd20d4d650047c55981a509fa2be78abd7c9c3caa0555e60a05", + "791": "2e0bbdcd0a8460e1e33c55668d0dc9752379a78b9f3561d7a17b922a5541a3fb", + "792": "5f77834c5a509023dd95dd98411eae1dd4bafd125deca590632f409f92fd257b", + "793": "dbfd900a3b31eeec2f14b916f5151611541cb716d80b7b9a1229de12293a02ea", + "794": "d019fe415aba832c4c761140d60c466c9aaad52b504df3167c17f2d3f0b277a7", + "795": "617b259349da44c2af2664acde113673ab3bb03a85d31f1be8f01027d0ebd4d3", + "796": "cba6b30a818d073398e5802211987f0897523e4752987bb445b2bca079670e22", + "797": "61e42cac3d7858b8850111a8c64c56432a18dd058dfb6afd773f07d703703b1a", + "798": "ae8b155d6b77522af79f7e4017fefe92aaa5d45eff132c83dc4d4bcfc9686020", + "799": "a41cb14ddf8f1948a01f590fbe53d9ca4e2faf48375ce1c306f91acf7c94e005", + "800": "c6a47bc6f02cf06be16728fb308c83f2f2ae350325ef7016867f5bdaea849d71", + "801": "d14b358c76b55106613f9c0a2112393338dfd01513b0fd231b79fc8db20e41f0", + "802": "22ae33e67fb48accfaa3b36e70c5a19066b974194c3130680de0c7cdce2d0f2e", + "803": "d95b3f9bbb7054042c1fba4db02f7223a2dad94977a36f08c8aaf92f373f9e78", + "804": "b0b1cf7253593eb2334c75e66dbe22b4b4540347485f1ea24e80226b4b18171c", + "805": "41b1ff5db0e70984ad20c50d1a9ac2b5a53ccd5f42796c8e948ae8880005fbb9", + "806": "b9c813beb39671adb8e1530555cadca44c21ddc7127932274918df2091dbd9ca", + "807": "745fd9ba97970d85a29877942839e41fc192794420e86f3bde39fd26db7a8bff", + "808": "6c73b947eb603602a7e8afadc83eaaa381a46db8b82a6fb89c9c1d93cb023fce", + "809": "eebac7753da4c1230dfce0f15fc124ffff01b0e432f0b74623b60cff71bbc9a9", + "810": "42be7899672a1a0046823603ce60dbeda7250a56fcb8d0913093850c85394307", + "811": "8698cd28ae4d93db36631870c33e4a8a527d970050d994666115f54260b64138", + "812": "dc2495924f37353db8b846323b8085fae9db502e890c513ed2e64ed7281f567f", + "813": "92179dde05aa6557baca65699fda50ca024d33a77078d8e128caa3c5db84064b", + "814": "344ed8cb7684307c00b7f03d751729a7f9d2a5f4a4cb4574594113d69593c0c1", + "815": "f642cf15345af3feab60e26a02aee038f759914906a5b2b469b46fdeee50ff59", + "816": "058178444e85f2aedb2f75d824a469747381f0bd3235d8c72df4385fec86eb07", + "817": "582fdc2233298192b09ceaf1463d6be06a09894075532630aa9d9efcfcb31da4", + "818": "67f6964d6ff114a43371b8375c44db2f1362df4f110b4a7ce8d79cf1b76621a0", + "819": "c7a82513ad48dfc87f2c1e0f2915b71464b7f5a16501c71df4ae4a8741dceef3", + "820": "9b23ae0181f320aadda2637ac2179c8b41b00715630c3acb643c7aee3b81cf90", + "821": "0941e396ff15b98fd7827de8e33ef94996d48ba719a88ba8e2da7f2605df3e5c", + "822": "ed8ef7f568939b9df1b77ae58344940b91c7e154a4367fe2b179bc7b9484d4e6", + "823": "05139328571a86096032b57e3a6a02a61acad4fb0d8f8e1b5d0ffb0d063ba697", + "826": "7f40f14ca65e5c06dd9ec9bbb212adb4d97a503199cb3c30ed921a04373bbe1c", + "827": "80461f02c63654c642382a6ffb7a44d0a3554434dfcfcea00ba91537724c7106", + "828": "520c196175625a0230afb76579ea26033372de3ef4c78aceb146b84322bfa871", + "829": "ed0089e61cf5540dd4a8fef1c468b96cf57f1d2bb79968755ba856d547ddafdf", + "831": "8ec445084427419ca6da405e0ded9814a4b4e11a2be84d88a8dea421f8e49992", + "832": "cfcb9ebef9308823f64798b5e12a59bf77ff6f92b0eae3790a61c0a26f577010", + "833": "e6ff3a5b257eb53366a32bfc8ea410a00a78bafa63650c76ac2bceddfbb42ff5", + "834": "b0d2a7e7d629ef14db9e7352a9a06d6ca66f750429170bb169ca52c172b8cc96", + "835": "bdfa1b1eecbad79f5de48bc6daee4d2b07689d7fb172aa306dd6094172b396f0" } From d66e1e873288bf399559c9ca40310d4b031aec50 Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Sat, 1 Apr 2023 15:18:13 +0300 Subject: [PATCH 029/757] Add Project Euler problem 800 solution 1 (#8567) Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- DIRECTORY.md | 3 ++ project_euler/problem_800/__init__.py | 0 project_euler/problem_800/sol1.py | 65 +++++++++++++++++++++++++++ 3 files changed, 68 insertions(+) create mode 100644 project_euler/problem_800/__init__.py create mode 100644 project_euler/problem_800/sol1.py diff --git a/DIRECTORY.md b/DIRECTORY.md index 1a641d8ecb59..18c573909773 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -317,6 +317,7 @@ * [Longest Sub Array](dynamic_programming/longest_sub_array.py) * [Matrix Chain Order](dynamic_programming/matrix_chain_order.py) * [Max Non Adjacent Sum](dynamic_programming/max_non_adjacent_sum.py) + * [Max Product Subarray](dynamic_programming/max_product_subarray.py) * [Max Sub Array](dynamic_programming/max_sub_array.py) * [Max Sum Contiguous Subsequence](dynamic_programming/max_sum_contiguous_subsequence.py) * [Min Distance Up Bottom](dynamic_programming/min_distance_up_bottom.py) @@ -1016,6 +1017,8 @@ * [Sol1](project_euler/problem_587/sol1.py) * Problem 686 * [Sol1](project_euler/problem_686/sol1.py) + * Problem 800 + * [Sol1](project_euler/problem_800/sol1.py) ## Quantum * [Bb84](quantum/bb84.py) diff --git a/project_euler/problem_800/__init__.py b/project_euler/problem_800/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_800/sol1.py b/project_euler/problem_800/sol1.py new file mode 100644 index 000000000000..f887787bcbc6 --- /dev/null +++ b/project_euler/problem_800/sol1.py @@ -0,0 +1,65 @@ +""" +Project Euler Problem 800: https://projecteuler.net/problem=800 + +An integer of the form p^q q^p with prime numbers p != q is called a hybrid-integer. +For example, 800 = 2^5 5^2 is a hybrid-integer. + +We define C(n) to be the number of hybrid-integers less than or equal to n. +You are given C(800) = 2 and C(800^800) = 10790 + +Find C(800800^800800) +""" + +from math import isqrt, log2 + + +def calculate_prime_numbers(max_number: int) -> list[int]: + """ + Returns prime numbers below max_number + + >>> calculate_prime_numbers(10) + [2, 3, 5, 7] + """ + + is_prime = [True] * max_number + for i in range(2, isqrt(max_number - 1) + 1): + if is_prime[i]: + for j in range(i**2, max_number, i): + is_prime[j] = False + + return [i for i in range(2, max_number) if is_prime[i]] + + +def solution(base: int = 800800, degree: int = 800800) -> int: + """ + Returns the number of hybrid-integers less than or equal to base^degree + + >>> solution(800, 1) + 2 + + >>> solution(800, 800) + 10790 + """ + + upper_bound = degree * log2(base) + max_prime = int(upper_bound) + prime_numbers = calculate_prime_numbers(max_prime) + + hybrid_integers_count = 0 + left = 0 + right = len(prime_numbers) - 1 + while left < right: + while ( + prime_numbers[right] * log2(prime_numbers[left]) + + prime_numbers[left] * log2(prime_numbers[right]) + > upper_bound + ): + right -= 1 + hybrid_integers_count += right - left + left += 1 + + return hybrid_integers_count + + +if __name__ == "__main__": + print(f"{solution() = }") From 3d2012c4ba3a9d9ddd80e518f0b5b9ba6c52df7d Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Sat, 1 Apr 2023 15:20:08 +0300 Subject: [PATCH 030/757] Add Project Euler problem 94 solution 1 (#8599) Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- DIRECTORY.md | 2 ++ project_euler/problem_094/__init__.py | 0 project_euler/problem_094/sol1.py | 44 +++++++++++++++++++++++++++ 3 files changed, 46 insertions(+) create mode 100644 project_euler/problem_094/__init__.py create mode 100644 project_euler/problem_094/sol1.py diff --git a/DIRECTORY.md b/DIRECTORY.md index 18c573909773..c781b17bf05f 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -937,6 +937,8 @@ * [Sol1](project_euler/problem_091/sol1.py) * Problem 092 * [Sol1](project_euler/problem_092/sol1.py) + * Problem 094 + * [Sol1](project_euler/problem_094/sol1.py) * Problem 097 * [Sol1](project_euler/problem_097/sol1.py) * Problem 099 diff --git a/project_euler/problem_094/__init__.py b/project_euler/problem_094/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_094/sol1.py b/project_euler/problem_094/sol1.py new file mode 100644 index 000000000000..a41292fe26fd --- /dev/null +++ b/project_euler/problem_094/sol1.py @@ -0,0 +1,44 @@ +""" +Project Euler Problem 94: https://projecteuler.net/problem=94 + +It is easily proved that no equilateral triangle exists with integral length sides and +integral area. However, the almost equilateral triangle 5-5-6 has an area of 12 square +units. + +We shall define an almost equilateral triangle to be a triangle for which two sides are +equal and the third differs by no more than one unit. + +Find the sum of the perimeters of all almost equilateral triangles with integral side +lengths and area and whose perimeters do not exceed one billion (1,000,000,000). +""" + + +def solution(max_perimeter: int = 10**9) -> int: + """ + Returns the sum of the perimeters of all almost equilateral triangles with integral + side lengths and area and whose perimeters do not exceed max_perimeter + + >>> solution(20) + 16 + """ + + prev_value = 1 + value = 2 + + perimeters_sum = 0 + i = 0 + perimeter = 0 + while perimeter <= max_perimeter: + perimeters_sum += perimeter + + prev_value += 2 * value + value += prev_value + + perimeter = 2 * value + 2 if i % 2 == 0 else 2 * value - 2 + i += 1 + + return perimeters_sum + + +if __name__ == "__main__": + print(f"{solution() = }") From 63710883c8634772fadf0145899cea4a1eadc31d Mon Sep 17 00:00:00 2001 From: amirsoroush <114881632+amirsoroush@users.noreply.github.com> Date: Sat, 1 Apr 2023 15:23:21 +0300 Subject: [PATCH 031/757] Remove extra `len` calls in doubly-linked-list's methods (#8600) --- data_structures/linked_list/doubly_linked_list.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/data_structures/linked_list/doubly_linked_list.py b/data_structures/linked_list/doubly_linked_list.py index 41d07d63e005..69763d12da15 100644 --- a/data_structures/linked_list/doubly_linked_list.py +++ b/data_structures/linked_list/doubly_linked_list.py @@ -81,7 +81,9 @@ def insert_at_nth(self, index: int, data): .... IndexError: list index out of range """ - if not 0 <= index <= len(self): + length = len(self) + + if not 0 <= index <= length: raise IndexError("list index out of range") new_node = Node(data) if self.head is None: @@ -90,7 +92,7 @@ def insert_at_nth(self, index: int, data): self.head.previous = new_node new_node.next = self.head self.head = new_node - elif index == len(self): + elif index == length: self.tail.next = new_node new_node.previous = self.tail self.tail = new_node @@ -131,15 +133,17 @@ def delete_at_nth(self, index: int): .... IndexError: list index out of range """ - if not 0 <= index <= len(self) - 1: + length = len(self) + + if not 0 <= index <= length - 1: raise IndexError("list index out of range") delete_node = self.head # default first node - if len(self) == 1: + if length == 1: self.head = self.tail = None elif index == 0: self.head = self.head.next self.head.previous = None - elif index == len(self) - 1: + elif index == length - 1: delete_node = self.tail self.tail = self.tail.previous self.tail.next = None From 59cae167e0e6b830b7ff5c89f5f2b8c747fb84c2 Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Sat, 1 Apr 2023 19:22:33 +0300 Subject: [PATCH 032/757] Reduce the complexity of digital_image_processing/edge detection/canny.py (#8167) * Reduce the complexity of digital_image_processing/edge_detection/canny.py * Fix * updating DIRECTORY.md * updating DIRECTORY.md * updating DIRECTORY.md * Fix review issues * Rename dst to destination --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- .../edge_detection/canny.py | 129 ++++++++++-------- 1 file changed, 75 insertions(+), 54 deletions(-) diff --git a/digital_image_processing/edge_detection/canny.py b/digital_image_processing/edge_detection/canny.py index a830355267c4..f8cbeedb3874 100644 --- a/digital_image_processing/edge_detection/canny.py +++ b/digital_image_processing/edge_detection/canny.py @@ -18,105 +18,126 @@ def gen_gaussian_kernel(k_size, sigma): return g -def canny(image, threshold_low=15, threshold_high=30, weak=128, strong=255): - image_row, image_col = image.shape[0], image.shape[1] - # gaussian_filter - gaussian_out = img_convolve(image, gen_gaussian_kernel(9, sigma=1.4)) - # get the gradient and degree by sobel_filter - sobel_grad, sobel_theta = sobel_filter(gaussian_out) - gradient_direction = np.rad2deg(sobel_theta) - gradient_direction += PI - - dst = np.zeros((image_row, image_col)) - +def suppress_non_maximum(image_shape, gradient_direction, sobel_grad): """ Non-maximum suppression. If the edge strength of the current pixel is the largest compared to the other pixels in the mask with the same direction, the value will be preserved. Otherwise, the value will be suppressed. """ - for row in range(1, image_row - 1): - for col in range(1, image_col - 1): + destination = np.zeros(image_shape) + + for row in range(1, image_shape[0] - 1): + for col in range(1, image_shape[1] - 1): direction = gradient_direction[row, col] if ( - 0 <= direction < 22.5 + 0 <= direction < PI / 8 or 15 * PI / 8 <= direction <= 2 * PI or 7 * PI / 8 <= direction <= 9 * PI / 8 ): w = sobel_grad[row, col - 1] e = sobel_grad[row, col + 1] if sobel_grad[row, col] >= w and sobel_grad[row, col] >= e: - dst[row, col] = sobel_grad[row, col] + destination[row, col] = sobel_grad[row, col] - elif (PI / 8 <= direction < 3 * PI / 8) or ( - 9 * PI / 8 <= direction < 11 * PI / 8 + elif ( + PI / 8 <= direction < 3 * PI / 8 + or 9 * PI / 8 <= direction < 11 * PI / 8 ): sw = sobel_grad[row + 1, col - 1] ne = sobel_grad[row - 1, col + 1] if sobel_grad[row, col] >= sw and sobel_grad[row, col] >= ne: - dst[row, col] = sobel_grad[row, col] + destination[row, col] = sobel_grad[row, col] - elif (3 * PI / 8 <= direction < 5 * PI / 8) or ( - 11 * PI / 8 <= direction < 13 * PI / 8 + elif ( + 3 * PI / 8 <= direction < 5 * PI / 8 + or 11 * PI / 8 <= direction < 13 * PI / 8 ): n = sobel_grad[row - 1, col] s = sobel_grad[row + 1, col] if sobel_grad[row, col] >= n and sobel_grad[row, col] >= s: - dst[row, col] = sobel_grad[row, col] + destination[row, col] = sobel_grad[row, col] - elif (5 * PI / 8 <= direction < 7 * PI / 8) or ( - 13 * PI / 8 <= direction < 15 * PI / 8 + elif ( + 5 * PI / 8 <= direction < 7 * PI / 8 + or 13 * PI / 8 <= direction < 15 * PI / 8 ): nw = sobel_grad[row - 1, col - 1] se = sobel_grad[row + 1, col + 1] if sobel_grad[row, col] >= nw and sobel_grad[row, col] >= se: - dst[row, col] = sobel_grad[row, col] - - """ - High-Low threshold detection. If an edge pixel’s gradient value is higher - than the high threshold value, it is marked as a strong edge pixel. If an - edge pixel’s gradient value is smaller than the high threshold value and - larger than the low threshold value, it is marked as a weak edge pixel. If - an edge pixel's value is smaller than the low threshold value, it will be - suppressed. - """ - if dst[row, col] >= threshold_high: - dst[row, col] = strong - elif dst[row, col] <= threshold_low: - dst[row, col] = 0 + destination[row, col] = sobel_grad[row, col] + + return destination + + +def detect_high_low_threshold( + image_shape, destination, threshold_low, threshold_high, weak, strong +): + """ + High-Low threshold detection. If an edge pixel’s gradient value is higher + than the high threshold value, it is marked as a strong edge pixel. If an + edge pixel’s gradient value is smaller than the high threshold value and + larger than the low threshold value, it is marked as a weak edge pixel. If + an edge pixel's value is smaller than the low threshold value, it will be + suppressed. + """ + for row in range(1, image_shape[0] - 1): + for col in range(1, image_shape[1] - 1): + if destination[row, col] >= threshold_high: + destination[row, col] = strong + elif destination[row, col] <= threshold_low: + destination[row, col] = 0 else: - dst[row, col] = weak + destination[row, col] = weak + +def track_edge(image_shape, destination, weak, strong): """ Edge tracking. Usually a weak edge pixel caused from true edges will be connected to a strong edge pixel while noise responses are unconnected. As long as there is one strong edge pixel that is involved in its 8-connected neighborhood, that weak edge point can be identified as one that should be preserved. """ - for row in range(1, image_row): - for col in range(1, image_col): - if dst[row, col] == weak: + for row in range(1, image_shape[0]): + for col in range(1, image_shape[1]): + if destination[row, col] == weak: if 255 in ( - dst[row, col + 1], - dst[row, col - 1], - dst[row - 1, col], - dst[row + 1, col], - dst[row - 1, col - 1], - dst[row + 1, col - 1], - dst[row - 1, col + 1], - dst[row + 1, col + 1], + destination[row, col + 1], + destination[row, col - 1], + destination[row - 1, col], + destination[row + 1, col], + destination[row - 1, col - 1], + destination[row + 1, col - 1], + destination[row - 1, col + 1], + destination[row + 1, col + 1], ): - dst[row, col] = strong + destination[row, col] = strong else: - dst[row, col] = 0 + destination[row, col] = 0 + + +def canny(image, threshold_low=15, threshold_high=30, weak=128, strong=255): + # gaussian_filter + gaussian_out = img_convolve(image, gen_gaussian_kernel(9, sigma=1.4)) + # get the gradient and degree by sobel_filter + sobel_grad, sobel_theta = sobel_filter(gaussian_out) + gradient_direction = PI + np.rad2deg(sobel_theta) + + destination = suppress_non_maximum(image.shape, gradient_direction, sobel_grad) + + detect_high_low_threshold( + image.shape, destination, threshold_low, threshold_high, weak, strong + ) + + track_edge(image.shape, destination, weak, strong) - return dst + return destination if __name__ == "__main__": # read original image in gray mode lena = cv2.imread(r"../image_data/lena.jpg", 0) # canny edge detection - canny_dst = canny(lena) - cv2.imshow("canny", canny_dst) + canny_destination = canny(lena) + cv2.imshow("canny", canny_destination) cv2.waitKey(0) From a213cea5f5a74e0a6b19240526779a3b0b1f270d Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Sat, 1 Apr 2023 12:39:22 -0400 Subject: [PATCH 033/757] Fix `mypy` errors in `dilation_operation.py` (#8595) * updating DIRECTORY.md * Fix mypy errors in dilation_operation.py * Rename functions to use snake case * updating DIRECTORY.md * updating DIRECTORY.md * Replace raw file string with pathlib Path * Update digital_image_processing/morphological_operations/dilation_operation.py Co-authored-by: Christian Clauss --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: Christian Clauss --- .../dilation_operation.py | 35 ++++++++++--------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/digital_image_processing/morphological_operations/dilation_operation.py b/digital_image_processing/morphological_operations/dilation_operation.py index c8380737d219..e49b955c1480 100644 --- a/digital_image_processing/morphological_operations/dilation_operation.py +++ b/digital_image_processing/morphological_operations/dilation_operation.py @@ -1,33 +1,35 @@ +from pathlib import Path + import numpy as np from PIL import Image -def rgb2gray(rgb: np.array) -> np.array: +def rgb_to_gray(rgb: np.ndarray) -> np.ndarray: """ Return gray image from rgb image - >>> rgb2gray(np.array([[[127, 255, 0]]])) + >>> rgb_to_gray(np.array([[[127, 255, 0]]])) array([[187.6453]]) - >>> rgb2gray(np.array([[[0, 0, 0]]])) + >>> rgb_to_gray(np.array([[[0, 0, 0]]])) array([[0.]]) - >>> rgb2gray(np.array([[[2, 4, 1]]])) + >>> rgb_to_gray(np.array([[[2, 4, 1]]])) array([[3.0598]]) - >>> rgb2gray(np.array([[[26, 255, 14], [5, 147, 20], [1, 200, 0]]])) + >>> rgb_to_gray(np.array([[[26, 255, 14], [5, 147, 20], [1, 200, 0]]])) array([[159.0524, 90.0635, 117.6989]]) """ r, g, b = rgb[:, :, 0], rgb[:, :, 1], rgb[:, :, 2] return 0.2989 * r + 0.5870 * g + 0.1140 * b -def gray2binary(gray: np.array) -> np.array: +def gray_to_binary(gray: np.ndarray) -> np.ndarray: """ Return binary image from gray image - >>> gray2binary(np.array([[127, 255, 0]])) + >>> gray_to_binary(np.array([[127, 255, 0]])) array([[False, True, False]]) - >>> gray2binary(np.array([[0]])) + >>> gray_to_binary(np.array([[0]])) array([[False]]) - >>> gray2binary(np.array([[26.2409, 4.9315, 1.4729]])) + >>> gray_to_binary(np.array([[26.2409, 4.9315, 1.4729]])) array([[False, False, False]]) - >>> gray2binary(np.array([[26, 255, 14], [5, 147, 20], [1, 200, 0]])) + >>> gray_to_binary(np.array([[26, 255, 14], [5, 147, 20], [1, 200, 0]])) array([[False, True, False], [False, True, False], [False, True, False]]) @@ -35,7 +37,7 @@ def gray2binary(gray: np.array) -> np.array: return (gray > 127) & (gray <= 255) -def dilation(image: np.array, kernel: np.array) -> np.array: +def dilation(image: np.ndarray, kernel: np.ndarray) -> np.ndarray: """ Return dilated image >>> dilation(np.array([[True, False, True]]), np.array([[0, 1, 0]])) @@ -61,14 +63,13 @@ def dilation(image: np.array, kernel: np.array) -> np.array: return output -# kernel to be applied -structuring_element = np.array([[0, 1, 0], [1, 1, 1], [0, 1, 0]]) - - if __name__ == "__main__": # read original image - image = np.array(Image.open(r"..\image_data\lena.jpg")) - output = dilation(gray2binary(rgb2gray(image)), structuring_element) + lena_path = Path(__file__).resolve().parent / "image_data" / "lena.jpg" + lena = np.array(Image.open(lena_path)) + # kernel to be applied + structuring_element = np.array([[0, 1, 0], [1, 1, 1], [0, 1, 0]]) + output = dilation(gray_to_binary(rgb_to_gray(lena)), structuring_element) # Save the output image pil_img = Image.fromarray(output).convert("RGB") pil_img.save("result_dilation.png") From 84b6852de80bb51c185c30942bff47f9c451c74d Mon Sep 17 00:00:00 2001 From: Blake Reimer Date: Sat, 1 Apr 2023 10:43:07 -0600 Subject: [PATCH 034/757] Graham's Law (#8162) * grahams law * doctest and type hints * doctest formatting * peer review updates --- physics/grahams_law.py | 208 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 208 insertions(+) create mode 100644 physics/grahams_law.py diff --git a/physics/grahams_law.py b/physics/grahams_law.py new file mode 100644 index 000000000000..6e5d75127e83 --- /dev/null +++ b/physics/grahams_law.py @@ -0,0 +1,208 @@ +""" +Title: Graham's Law of Effusion + +Description: Graham's law of effusion states that the rate of effusion of a gas is +inversely proportional to the square root of the molar mass of its particles: + +r1/r2 = sqrt(m2/m1) + +r1 = Rate of effusion for the first gas. +r2 = Rate of effusion for the second gas. +m1 = Molar mass of the first gas. +m2 = Molar mass of the second gas. + +(Description adapted from https://en.wikipedia.org/wiki/Graham%27s_law) +""" + +from math import pow, sqrt + + +def validate(*values: float) -> bool: + """ + Input Parameters: + ----------------- + effusion_rate_1: Effustion rate of first gas (m^2/s, mm^2/s, etc.) + effusion_rate_2: Effustion rate of second gas (m^2/s, mm^2/s, etc.) + molar_mass_1: Molar mass of the first gas (g/mol, kg/kmol, etc.) + molar_mass_2: Molar mass of the second gas (g/mol, kg/kmol, etc.) + + Returns: + -------- + >>> validate(2.016, 4.002) + True + >>> validate(-2.016, 4.002) + False + >>> validate() + False + """ + result = len(values) > 0 and all(value > 0.0 for value in values) + return result + + +def effusion_ratio(molar_mass_1: float, molar_mass_2: float) -> float | ValueError: + """ + Input Parameters: + ----------------- + molar_mass_1: Molar mass of the first gas (g/mol, kg/kmol, etc.) + molar_mass_2: Molar mass of the second gas (g/mol, kg/kmol, etc.) + + Returns: + -------- + >>> effusion_ratio(2.016, 4.002) + 1.408943 + >>> effusion_ratio(-2.016, 4.002) + ValueError('Input Error: Molar mass values must greater than 0.') + >>> effusion_ratio(2.016) + Traceback (most recent call last): + ... + TypeError: effusion_ratio() missing 1 required positional argument: 'molar_mass_2' + """ + return ( + round(sqrt(molar_mass_2 / molar_mass_1), 6) + if validate(molar_mass_1, molar_mass_2) + else ValueError("Input Error: Molar mass values must greater than 0.") + ) + + +def first_effusion_rate( + effusion_rate: float, molar_mass_1: float, molar_mass_2: float +) -> float | ValueError: + """ + Input Parameters: + ----------------- + effusion_rate: Effustion rate of second gas (m^2/s, mm^2/s, etc.) + molar_mass_1: Molar mass of the first gas (g/mol, kg/kmol, etc.) + molar_mass_2: Molar mass of the second gas (g/mol, kg/kmol, etc.) + + Returns: + -------- + >>> first_effusion_rate(1, 2.016, 4.002) + 1.408943 + >>> first_effusion_rate(-1, 2.016, 4.002) + ValueError('Input Error: Molar mass and effusion rate values must greater than 0.') + >>> first_effusion_rate(1) + Traceback (most recent call last): + ... + TypeError: first_effusion_rate() missing 2 required positional arguments: \ +'molar_mass_1' and 'molar_mass_2' + >>> first_effusion_rate(1, 2.016) + Traceback (most recent call last): + ... + TypeError: first_effusion_rate() missing 1 required positional argument: \ +'molar_mass_2' + """ + return ( + round(effusion_rate * sqrt(molar_mass_2 / molar_mass_1), 6) + if validate(effusion_rate, molar_mass_1, molar_mass_2) + else ValueError( + "Input Error: Molar mass and effusion rate values must greater than 0." + ) + ) + + +def second_effusion_rate( + effusion_rate: float, molar_mass_1: float, molar_mass_2: float +) -> float | ValueError: + """ + Input Parameters: + ----------------- + effusion_rate: Effustion rate of second gas (m^2/s, mm^2/s, etc.) + molar_mass_1: Molar mass of the first gas (g/mol, kg/kmol, etc.) + molar_mass_2: Molar mass of the second gas (g/mol, kg/kmol, etc.) + + Returns: + -------- + >>> second_effusion_rate(1, 2.016, 4.002) + 0.709752 + >>> second_effusion_rate(-1, 2.016, 4.002) + ValueError('Input Error: Molar mass and effusion rate values must greater than 0.') + >>> second_effusion_rate(1) + Traceback (most recent call last): + ... + TypeError: second_effusion_rate() missing 2 required positional arguments: \ +'molar_mass_1' and 'molar_mass_2' + >>> second_effusion_rate(1, 2.016) + Traceback (most recent call last): + ... + TypeError: second_effusion_rate() missing 1 required positional argument: \ +'molar_mass_2' + """ + return ( + round(effusion_rate / sqrt(molar_mass_2 / molar_mass_1), 6) + if validate(effusion_rate, molar_mass_1, molar_mass_2) + else ValueError( + "Input Error: Molar mass and effusion rate values must greater than 0." + ) + ) + + +def first_molar_mass( + molar_mass: float, effusion_rate_1: float, effusion_rate_2: float +) -> float | ValueError: + """ + Input Parameters: + ----------------- + molar_mass: Molar mass of the first gas (g/mol, kg/kmol, etc.) + effusion_rate_1: Effustion rate of first gas (m^2/s, mm^2/s, etc.) + effusion_rate_2: Effustion rate of second gas (m^2/s, mm^2/s, etc.) + + Returns: + -------- + >>> first_molar_mass(2, 1.408943, 0.709752) + 0.507524 + >>> first_molar_mass(-1, 2.016, 4.002) + ValueError('Input Error: Molar mass and effusion rate values must greater than 0.') + >>> first_molar_mass(1) + Traceback (most recent call last): + ... + TypeError: first_molar_mass() missing 2 required positional arguments: \ +'effusion_rate_1' and 'effusion_rate_2' + >>> first_molar_mass(1, 2.016) + Traceback (most recent call last): + ... + TypeError: first_molar_mass() missing 1 required positional argument: \ +'effusion_rate_2' + """ + return ( + round(molar_mass / pow(effusion_rate_1 / effusion_rate_2, 2), 6) + if validate(molar_mass, effusion_rate_1, effusion_rate_2) + else ValueError( + "Input Error: Molar mass and effusion rate values must greater than 0." + ) + ) + + +def second_molar_mass( + molar_mass: float, effusion_rate_1: float, effusion_rate_2: float +) -> float | ValueError: + """ + Input Parameters: + ----------------- + molar_mass: Molar mass of the first gas (g/mol, kg/kmol, etc.) + effusion_rate_1: Effustion rate of first gas (m^2/s, mm^2/s, etc.) + effusion_rate_2: Effustion rate of second gas (m^2/s, mm^2/s, etc.) + + Returns: + -------- + >>> second_molar_mass(2, 1.408943, 0.709752) + 1.970351 + >>> second_molar_mass(-2, 1.408943, 0.709752) + ValueError('Input Error: Molar mass and effusion rate values must greater than 0.') + >>> second_molar_mass(1) + Traceback (most recent call last): + ... + TypeError: second_molar_mass() missing 2 required positional arguments: \ +'effusion_rate_1' and 'effusion_rate_2' + >>> second_molar_mass(1, 2.016) + Traceback (most recent call last): + ... + TypeError: second_molar_mass() missing 1 required positional argument: \ +'effusion_rate_2' + """ + return ( + round(pow(effusion_rate_1 / effusion_rate_2, 2) / molar_mass, 6) + if validate(molar_mass, effusion_rate_1, effusion_rate_2) + else ValueError( + "Input Error: Molar mass and effusion rate values must greater than 0." + ) + ) From 56a40eb3ee9aa151defd97597f4e67acf294089f Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Sat, 1 Apr 2023 20:43:11 +0300 Subject: [PATCH 035/757] Reenable files when TensorFlow supports the current Python (#8602) * Remove python_version < "3.11" for tensorflow * Reenable neural_network/input_data.py_tf * updating DIRECTORY.md * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Try to fix ruff * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Try to fix ruff * Try to fix ruff * Try to fix ruff * Try to fix pre-commit * Try to fix * Fix * Fix * Reenable dynamic_programming/k_means_clustering_tensorflow.py_tf * updating DIRECTORY.md * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Try to fix ruff --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- DIRECTORY.md | 2 + ...py_tf => k_means_clustering_tensorflow.py} | 9 +- .../{input_data.py_tf => input_data.py} | 98 +++++++++---------- requirements.txt | 2 +- 4 files changed, 55 insertions(+), 56 deletions(-) rename dynamic_programming/{k_means_clustering_tensorflow.py_tf => k_means_clustering_tensorflow.py} (98%) rename neural_network/{input_data.py_tf => input_data.py} (83%) diff --git a/DIRECTORY.md b/DIRECTORY.md index c781b17bf05f..34967082b359 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -309,6 +309,7 @@ * [Floyd Warshall](dynamic_programming/floyd_warshall.py) * [Integer Partition](dynamic_programming/integer_partition.py) * [Iterating Through Submasks](dynamic_programming/iterating_through_submasks.py) + * [K Means Clustering Tensorflow](dynamic_programming/k_means_clustering_tensorflow.py) * [Knapsack](dynamic_programming/knapsack.py) * [Longest Common Subsequence](dynamic_programming/longest_common_subsequence.py) * [Longest Common Substring](dynamic_programming/longest_common_substring.py) @@ -685,6 +686,7 @@ * [2 Hidden Layers Neural Network](neural_network/2_hidden_layers_neural_network.py) * [Back Propagation Neural Network](neural_network/back_propagation_neural_network.py) * [Convolution Neural Network](neural_network/convolution_neural_network.py) + * [Input Data](neural_network/input_data.py) * [Perceptron](neural_network/perceptron.py) * [Simple Neural Network](neural_network/simple_neural_network.py) diff --git a/dynamic_programming/k_means_clustering_tensorflow.py_tf b/dynamic_programming/k_means_clustering_tensorflow.py similarity index 98% rename from dynamic_programming/k_means_clustering_tensorflow.py_tf rename to dynamic_programming/k_means_clustering_tensorflow.py index 4fbcedeaa0dc..8d3f6f0dfbcb 100644 --- a/dynamic_programming/k_means_clustering_tensorflow.py_tf +++ b/dynamic_programming/k_means_clustering_tensorflow.py @@ -1,9 +1,10 @@ -import tensorflow as tf from random import shuffle + +import tensorflow as tf from numpy import array -def TFKMeansCluster(vectors, noofclusters): +def tf_k_means_cluster(vectors, noofclusters): """ K-Means Clustering using TensorFlow. 'vectors' should be a n*k 2-D NumPy array, where n is the number @@ -30,7 +31,6 @@ def TFKMeansCluster(vectors, noofclusters): graph = tf.Graph() with graph.as_default(): - # SESSION OF COMPUTATION sess = tf.Session() @@ -95,8 +95,7 @@ def TFKMeansCluster(vectors, noofclusters): # iterations. To keep things simple, we will only do a set number of # iterations, instead of using a Stopping Criterion. noofiterations = 100 - for iteration_n in range(noofiterations): - + for _ in range(noofiterations): ##EXPECTATION STEP ##Based on the centroid locations till last iteration, compute ##the _expected_ centroid assignments. diff --git a/neural_network/input_data.py_tf b/neural_network/input_data.py similarity index 83% rename from neural_network/input_data.py_tf rename to neural_network/input_data.py index 0e22ac0bcda5..2a32f0b82c37 100644 --- a/neural_network/input_data.py_tf +++ b/neural_network/input_data.py @@ -21,13 +21,10 @@ import collections import gzip import os +import urllib import numpy -from six.moves import urllib -from six.moves import xrange # pylint: disable=redefined-builtin - -from tensorflow.python.framework import dtypes -from tensorflow.python.framework import random_seed +from tensorflow.python.framework import dtypes, random_seed from tensorflow.python.platform import gfile from tensorflow.python.util.deprecation import deprecated @@ -46,16 +43,16 @@ def _read32(bytestream): def _extract_images(f): """Extract the images into a 4D uint8 numpy array [index, y, x, depth]. - Args: - f: A file object that can be passed into a gzip reader. + Args: + f: A file object that can be passed into a gzip reader. - Returns: - data: A 4D uint8 numpy array [index, y, x, depth]. + Returns: + data: A 4D uint8 numpy array [index, y, x, depth]. - Raises: - ValueError: If the bytestream does not start with 2051. + Raises: + ValueError: If the bytestream does not start with 2051. - """ + """ print("Extracting", f.name) with gzip.GzipFile(fileobj=f) as bytestream: magic = _read32(bytestream) @@ -86,17 +83,17 @@ def _dense_to_one_hot(labels_dense, num_classes): def _extract_labels(f, one_hot=False, num_classes=10): """Extract the labels into a 1D uint8 numpy array [index]. - Args: - f: A file object that can be passed into a gzip reader. - one_hot: Does one hot encoding for the result. - num_classes: Number of classes for the one hot encoding. + Args: + f: A file object that can be passed into a gzip reader. + one_hot: Does one hot encoding for the result. + num_classes: Number of classes for the one hot encoding. - Returns: - labels: a 1D uint8 numpy array. + Returns: + labels: a 1D uint8 numpy array. - Raises: - ValueError: If the bystream doesn't start with 2049. - """ + Raises: + ValueError: If the bystream doesn't start with 2049. + """ print("Extracting", f.name) with gzip.GzipFile(fileobj=f) as bytestream: magic = _read32(bytestream) @@ -115,8 +112,8 @@ def _extract_labels(f, one_hot=False, num_classes=10): class _DataSet: """Container class for a _DataSet (deprecated). - THIS CLASS IS DEPRECATED. - """ + THIS CLASS IS DEPRECATED. + """ @deprecated( None, @@ -135,21 +132,21 @@ def __init__( ): """Construct a _DataSet. - one_hot arg is used only if fake_data is true. `dtype` can be either - `uint8` to leave the input as `[0, 255]`, or `float32` to rescale into - `[0, 1]`. Seed arg provides for convenient deterministic testing. - - Args: - images: The images - labels: The labels - fake_data: Ignore inages and labels, use fake data. - one_hot: Bool, return the labels as one hot vectors (if True) or ints (if - False). - dtype: Output image dtype. One of [uint8, float32]. `uint8` output has - range [0,255]. float32 output has range [0,1]. - reshape: Bool. If True returned images are returned flattened to vectors. - seed: The random seed to use. - """ + one_hot arg is used only if fake_data is true. `dtype` can be either + `uint8` to leave the input as `[0, 255]`, or `float32` to rescale into + `[0, 1]`. Seed arg provides for convenient deterministic testing. + + Args: + images: The images + labels: The labels + fake_data: Ignore inages and labels, use fake data. + one_hot: Bool, return the labels as one hot vectors (if True) or ints (if + False). + dtype: Output image dtype. One of [uint8, float32]. `uint8` output has + range [0,255]. float32 output has range [0,1]. + reshape: Bool. If True returned images are returned flattened to vectors. + seed: The random seed to use. + """ seed1, seed2 = random_seed.get_seed(seed) # If op level seed is not set, use whatever graph level seed is returned numpy.random.seed(seed1 if seed is None else seed2) @@ -206,8 +203,8 @@ def next_batch(self, batch_size, fake_data=False, shuffle=True): else: fake_label = 0 return ( - [fake_image for _ in xrange(batch_size)], - [fake_label for _ in xrange(batch_size)], + [fake_image for _ in range(batch_size)], + [fake_label for _ in range(batch_size)], ) start = self._index_in_epoch # Shuffle for the first epoch @@ -250,19 +247,19 @@ def next_batch(self, batch_size, fake_data=False, shuffle=True): def _maybe_download(filename, work_directory, source_url): """Download the data from source url, unless it's already here. - Args: - filename: string, name of the file in the directory. - work_directory: string, path to working directory. - source_url: url to download from if file doesn't exist. + Args: + filename: string, name of the file in the directory. + work_directory: string, path to working directory. + source_url: url to download from if file doesn't exist. - Returns: - Path to resulting file. - """ + Returns: + Path to resulting file. + """ if not gfile.Exists(work_directory): gfile.MakeDirs(work_directory) filepath = os.path.join(work_directory, filename) if not gfile.Exists(filepath): - urllib.request.urlretrieve(source_url, filepath) + urllib.request.urlretrieve(source_url, filepath) # noqa: S310 with gfile.GFile(filepath) as f: size = f.size() print("Successfully downloaded", filename, size, "bytes.") @@ -328,7 +325,8 @@ def fake(): if not 0 <= validation_size <= len(train_images): raise ValueError( - f"Validation size should be between 0 and {len(train_images)}. Received: {validation_size}." + f"Validation size should be between 0 and {len(train_images)}. " + f"Received: {validation_size}." ) validation_images = train_images[:validation_size] @@ -336,7 +334,7 @@ def fake(): train_images = train_images[validation_size:] train_labels = train_labels[validation_size:] - options = dict(dtype=dtype, reshape=reshape, seed=seed) + options = {"dtype": dtype, "reshape": reshape, "seed": seed} train = _DataSet(train_images, train_labels, **options) validation = _DataSet(validation_images, validation_labels, **options) diff --git a/requirements.txt b/requirements.txt index a1d607df07e1..acfbc823e77f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,7 +15,7 @@ scikit-fuzzy scikit-learn statsmodels sympy -tensorflow; python_version < "3.11" +tensorflow texttable tweepy xgboost From 33114f0272bcc1fafa6ce0f40d92ded908747ce3 Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Sat, 1 Apr 2023 16:05:01 -0400 Subject: [PATCH 036/757] Revamp `md5.py` (#8065) * Add type hints to md5.py * Rename some vars to snake case * Specify functions imported from math * Rename vars and functions to be more descriptive * Make tests from test function into doctests * Clarify more var names * Refactor some MD5 code into preprocess function * Simplify loop indices in get_block_words * Add more detailed comments, docs, and doctests * updating DIRECTORY.md * updating DIRECTORY.md * updating DIRECTORY.md * updating DIRECTORY.md * updating DIRECTORY.md * Add type hints to md5.py * Rename some vars to snake case * Specify functions imported from math * Rename vars and functions to be more descriptive * Make tests from test function into doctests * Clarify more var names * Refactor some MD5 code into preprocess function * Simplify loop indices in get_block_words * Add more detailed comments, docs, and doctests * updating DIRECTORY.md * updating DIRECTORY.md * updating DIRECTORY.md * updating DIRECTORY.md * Convert str types to bytes * Add tests comparing md5_me to hashlib's md5 * Replace line-break backslashes with parentheses --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 1 + hashes/md5.py | 372 +++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 290 insertions(+), 83 deletions(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index 34967082b359..b1adc23f6e61 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -717,6 +717,7 @@ * [Archimedes Principle](physics/archimedes_principle.py) * [Casimir Effect](physics/casimir_effect.py) * [Centripetal Force](physics/centripetal_force.py) + * [Grahams Law](physics/grahams_law.py) * [Horizontal Projectile Motion](physics/horizontal_projectile_motion.py) * [Hubble Parameter](physics/hubble_parameter.py) * [Ideal Gas Law](physics/ideal_gas_law.py) diff --git a/hashes/md5.py b/hashes/md5.py index 2020bf2e53bf..2187006ec8a9 100644 --- a/hashes/md5.py +++ b/hashes/md5.py @@ -1,91 +1,223 @@ -import math +""" +The MD5 algorithm is a hash function that's commonly used as a checksum to +detect data corruption. The algorithm works by processing a given message in +blocks of 512 bits, padding the message as needed. It uses the blocks to operate +a 128-bit state and performs a total of 64 such operations. Note that all values +are little-endian, so inputs are converted as needed. +Although MD5 was used as a cryptographic hash function in the past, it's since +been cracked, so it shouldn't be used for security purposes. -def rearrange(bit_string_32): - """[summary] - Regroups the given binary string. +For more info, see https://en.wikipedia.org/wiki/MD5 +""" + +from collections.abc import Generator +from math import sin + + +def to_little_endian(string_32: bytes) -> bytes: + """ + Converts the given string to little-endian in groups of 8 chars. Arguments: - bitString32 {[string]} -- [32 bit binary] + string_32 {[string]} -- [32-char string] Raises: - ValueError -- [if the given string not are 32 bit binary string] + ValueError -- [input is not 32 char] Returns: - [string] -- [32 bit binary string] - >>> rearrange('1234567890abcdfghijklmnopqrstuvw') - 'pqrstuvwhijklmno90abcdfg12345678' + 32-char little-endian string + >>> to_little_endian(b'1234567890abcdfghijklmnopqrstuvw') + b'pqrstuvwhijklmno90abcdfg12345678' + >>> to_little_endian(b'1234567890') + Traceback (most recent call last): + ... + ValueError: Input must be of length 32 """ + if len(string_32) != 32: + raise ValueError("Input must be of length 32") - if len(bit_string_32) != 32: - raise ValueError("Need length 32") - new_string = "" + little_endian = b"" for i in [3, 2, 1, 0]: - new_string += bit_string_32[8 * i : 8 * i + 8] - return new_string + little_endian += string_32[8 * i : 8 * i + 8] + return little_endian + + +def reformat_hex(i: int) -> bytes: + """ + Converts the given non-negative integer to hex string. + Example: Suppose the input is the following: + i = 1234 -def reformat_hex(i): - """[summary] - Converts the given integer into 8-digit hex number. + The input is 0x000004d2 in hex, so the little-endian hex string is + "d2040000". Arguments: - i {[int]} -- [integer] + i {[int]} -- [integer] + + Raises: + ValueError -- [input is negative] + + Returns: + 8-char little-endian hex string + + >>> reformat_hex(1234) + b'd2040000' >>> reformat_hex(666) - '9a020000' + b'9a020000' + >>> reformat_hex(0) + b'00000000' + >>> reformat_hex(1234567890) + b'd2029649' + >>> reformat_hex(1234567890987654321) + b'b11c6cb1' + >>> reformat_hex(-1) + Traceback (most recent call last): + ... + ValueError: Input must be non-negative """ + if i < 0: + raise ValueError("Input must be non-negative") - hexrep = format(i, "08x") - thing = "" + hex_rep = format(i, "08x")[-8:] + little_endian_hex = b"" for i in [3, 2, 1, 0]: - thing += hexrep[2 * i : 2 * i + 2] - return thing + little_endian_hex += hex_rep[2 * i : 2 * i + 2].encode("utf-8") + return little_endian_hex -def pad(bit_string): - """[summary] - Fills up the binary string to a 512 bit binary string +def preprocess(message: bytes) -> bytes: + """ + Preprocesses the message string: + - Convert message to bit string + - Pad bit string to a multiple of 512 chars: + - Append a 1 + - Append 0's until length = 448 (mod 512) + - Append length of original message (64 chars) + + Example: Suppose the input is the following: + message = "a" + + The message bit string is "01100001", which is 8 bits long. Thus, the + bit string needs 439 bits of padding so that + (bit_string + "1" + padding) = 448 (mod 512). + The message length is "000010000...0" in 64-bit little-endian binary. + The combined bit string is then 512 bits long. Arguments: - bitString {[string]} -- [binary string] + message {[string]} -- [message string] Returns: - [string] -- [binary string] + processed bit string padded to a multiple of 512 chars + + >>> preprocess(b"a") == (b"01100001" + b"1" + + ... (b"0" * 439) + b"00001000" + (b"0" * 56)) + True + >>> preprocess(b"") == b"1" + (b"0" * 447) + (b"0" * 64) + True """ - start_length = len(bit_string) - bit_string += "1" + bit_string = b"" + for char in message: + bit_string += format(char, "08b").encode("utf-8") + start_len = format(len(bit_string), "064b").encode("utf-8") + + # Pad bit_string to a multiple of 512 chars + bit_string += b"1" while len(bit_string) % 512 != 448: - bit_string += "0" - last_part = format(start_length, "064b") - bit_string += rearrange(last_part[32:]) + rearrange(last_part[:32]) + bit_string += b"0" + bit_string += to_little_endian(start_len[32:]) + to_little_endian(start_len[:32]) + return bit_string -def get_block(bit_string): - """[summary] - Iterator: - Returns by each call a list of length 16 with the 32 bit - integer blocks. +def get_block_words(bit_string: bytes) -> Generator[list[int], None, None]: + """ + Splits bit string into blocks of 512 chars and yields each block as a list + of 32-bit words + + Example: Suppose the input is the following: + bit_string = + "000000000...0" + # 0x00 (32 bits, padded to the right) + "000000010...0" + # 0x01 (32 bits, padded to the right) + "000000100...0" + # 0x02 (32 bits, padded to the right) + "000000110...0" + # 0x03 (32 bits, padded to the right) + ... + "000011110...0" # 0x0a (32 bits, padded to the right) + + Then len(bit_string) == 512, so there'll be 1 block. The block is split + into 32-bit words, and each word is converted to little endian. The + first word is interpreted as 0 in decimal, the second word is + interpreted as 1 in decimal, etc. + + Thus, block_words == [[0, 1, 2, 3, ..., 15]]. Arguments: - bit_string {[string]} -- [binary string >= 512] + bit_string {[string]} -- [bit string with multiple of 512 as length] + + Raises: + ValueError -- [length of bit string isn't multiple of 512] + + Yields: + a list of 16 32-bit words + + >>> test_string = ("".join(format(n << 24, "032b") for n in range(16)) + ... .encode("utf-8")) + >>> list(get_block_words(test_string)) + [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]] + >>> list(get_block_words(test_string * 4)) == [list(range(16))] * 4 + True + >>> list(get_block_words(b"1" * 512)) == [[4294967295] * 16] + True + >>> list(get_block_words(b"")) + [] + >>> list(get_block_words(b"1111")) + Traceback (most recent call last): + ... + ValueError: Input must have length that's a multiple of 512 """ + if len(bit_string) % 512 != 0: + raise ValueError("Input must have length that's a multiple of 512") - curr_pos = 0 - while curr_pos < len(bit_string): - curr_part = bit_string[curr_pos : curr_pos + 512] - my_splits = [] - for i in range(16): - my_splits.append(int(rearrange(curr_part[32 * i : 32 * i + 32]), 2)) - yield my_splits - curr_pos += 512 + for pos in range(0, len(bit_string), 512): + block = bit_string[pos : pos + 512] + block_words = [] + for i in range(0, 512, 32): + block_words.append(int(to_little_endian(block[i : i + 32]), 2)) + yield block_words -def not32(i): +def not_32(i: int) -> int: """ - >>> not32(34) + Perform bitwise NOT on given int. + + Arguments: + i {[int]} -- [given int] + + Raises: + ValueError -- [input is negative] + + Returns: + Result of bitwise NOT on i + + >>> not_32(34) 4294967261 + >>> not_32(1234) + 4294966061 + >>> not_32(4294966061) + 1234 + >>> not_32(0) + 4294967295 + >>> not_32(1) + 4294967294 + >>> not_32(-1) + Traceback (most recent call last): + ... + ValueError: Input must be non-negative """ + if i < 0: + raise ValueError("Input must be non-negative") + i_str = format(i, "032b") new_str = "" for c in i_str: @@ -93,35 +225,114 @@ def not32(i): return int(new_str, 2) -def sum32(a, b): +def sum_32(a: int, b: int) -> int: + """ + Add two numbers as 32-bit ints. + + Arguments: + a {[int]} -- [first given int] + b {[int]} -- [second given int] + + Returns: + (a + b) as an unsigned 32-bit int + + >>> sum_32(1, 1) + 2 + >>> sum_32(2, 3) + 5 + >>> sum_32(0, 0) + 0 + >>> sum_32(-1, -1) + 4294967294 + >>> sum_32(4294967295, 1) + 0 + """ return (a + b) % 2**32 -def leftrot32(i, s): - return (i << s) ^ (i >> (32 - s)) +def left_rotate_32(i: int, shift: int) -> int: + """ + Rotate the bits of a given int left by a given amount. + + Arguments: + i {[int]} -- [given int] + shift {[int]} -- [shift amount] + + Raises: + ValueError -- [either given int or shift is negative] + Returns: + `i` rotated to the left by `shift` bits + + >>> left_rotate_32(1234, 1) + 2468 + >>> left_rotate_32(1111, 4) + 17776 + >>> left_rotate_32(2147483648, 1) + 1 + >>> left_rotate_32(2147483648, 3) + 4 + >>> left_rotate_32(4294967295, 4) + 4294967295 + >>> left_rotate_32(1234, 0) + 1234 + >>> left_rotate_32(0, 0) + 0 + >>> left_rotate_32(-1, 0) + Traceback (most recent call last): + ... + ValueError: Input must be non-negative + >>> left_rotate_32(0, -1) + Traceback (most recent call last): + ... + ValueError: Shift must be non-negative + """ + if i < 0: + raise ValueError("Input must be non-negative") + if shift < 0: + raise ValueError("Shift must be non-negative") + return ((i << shift) ^ (i >> (32 - shift))) % 2**32 + + +def md5_me(message: bytes) -> bytes: + """ + Returns the 32-char MD5 hash of a given message. -def md5me(test_string): - """[summary] - Returns a 32-bit hash code of the string 'testString' + Reference: https://en.wikipedia.org/wiki/MD5#Algorithm Arguments: - testString {[string]} -- [message] + message {[string]} -- [message] + + Returns: + 32-char MD5 hash string + + >>> md5_me(b"") + b'd41d8cd98f00b204e9800998ecf8427e' + >>> md5_me(b"The quick brown fox jumps over the lazy dog") + b'9e107d9d372bb6826bd81d3542a419d6' + >>> md5_me(b"The quick brown fox jumps over the lazy dog.") + b'e4d909c290d0fb1ca068ffaddf22cbd0' + + >>> import hashlib + >>> from string import ascii_letters + >>> msgs = [b"", ascii_letters.encode("utf-8"), "Üñîçø∂é".encode("utf-8"), + ... b"The quick brown fox jumps over the lazy dog."] + >>> all(md5_me(msg) == hashlib.md5(msg).hexdigest().encode("utf-8") for msg in msgs) + True """ - bs = "" - for i in test_string: - bs += format(ord(i), "08b") - bs = pad(bs) + # Convert to bit string, add padding and append message length + bit_string = preprocess(message) - tvals = [int(2**32 * abs(math.sin(i + 1))) for i in range(64)] + added_consts = [int(2**32 * abs(sin(i + 1))) for i in range(64)] + # Starting states a0 = 0x67452301 b0 = 0xEFCDAB89 c0 = 0x98BADCFE d0 = 0x10325476 - s = [ + shift_amounts = [ 7, 12, 17, @@ -188,51 +399,46 @@ def md5me(test_string): 21, ] - for m in get_block(bs): + # Process bit string in chunks, each with 16 32-char words + for block_words in get_block_words(bit_string): a = a0 b = b0 c = c0 d = d0 + + # Hash current chunk for i in range(64): if i <= 15: - # f = (B & C) | (not32(B) & D) + # f = (b & c) | (not_32(b) & d) # Alternate definition for f f = d ^ (b & (c ^ d)) g = i elif i <= 31: - # f = (D & B) | (not32(D) & C) + # f = (d & b) | (not_32(d) & c) # Alternate definition for f f = c ^ (d & (b ^ c)) g = (5 * i + 1) % 16 elif i <= 47: f = b ^ c ^ d g = (3 * i + 5) % 16 else: - f = c ^ (b | not32(d)) + f = c ^ (b | not_32(d)) g = (7 * i) % 16 - dtemp = d + f = (f + a + added_consts[i] + block_words[g]) % 2**32 + a = d d = c c = b - b = sum32(b, leftrot32((a + f + tvals[i] + m[g]) % 2**32, s[i])) - a = dtemp - a0 = sum32(a0, a) - b0 = sum32(b0, b) - c0 = sum32(c0, c) - d0 = sum32(d0, d) + b = sum_32(b, left_rotate_32(f, shift_amounts[i])) + + # Add hashed chunk to running total + a0 = sum_32(a0, a) + b0 = sum_32(b0, b) + c0 = sum_32(c0, c) + d0 = sum_32(d0, d) digest = reformat_hex(a0) + reformat_hex(b0) + reformat_hex(c0) + reformat_hex(d0) return digest -def test(): - assert md5me("") == "d41d8cd98f00b204e9800998ecf8427e" - assert ( - md5me("The quick brown fox jumps over the lazy dog") - == "9e107d9d372bb6826bd81d3542a419d6" - ) - print("Success.") - - if __name__ == "__main__": - test() import doctest doctest.testmod() From 5ca71895630719cc41f8171aba8be461fb8cc9d2 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Sun, 2 Apr 2023 06:48:19 +0200 Subject: [PATCH 037/757] Rename quantum_random.py.DISABLED.txt to quantum_random.py (#8601) Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 1 + quantum/{quantum_random.py.DISABLED.txt => quantum_random.py} | 0 2 files changed, 1 insertion(+) rename quantum/{quantum_random.py.DISABLED.txt => quantum_random.py} (100%) diff --git a/DIRECTORY.md b/DIRECTORY.md index b1adc23f6e61..8dd3fb5d9af1 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -1033,6 +1033,7 @@ * [Q Fourier Transform](quantum/q_fourier_transform.py) * [Q Full Adder](quantum/q_full_adder.py) * [Quantum Entanglement](quantum/quantum_entanglement.py) + * [Quantum Random](quantum/quantum_random.py) * [Quantum Teleportation](quantum/quantum_teleportation.py) * [Ripple Adder Classic](quantum/ripple_adder_classic.py) * [Single Qubit Measure](quantum/single_qubit_measure.py) diff --git a/quantum/quantum_random.py.DISABLED.txt b/quantum/quantum_random.py similarity index 100% rename from quantum/quantum_random.py.DISABLED.txt rename to quantum/quantum_random.py From ebc2d5d79f837931e80f7d5e7e1dece9ef48f760 Mon Sep 17 00:00:00 2001 From: Ishab Date: Sun, 2 Apr 2023 13:04:11 +0100 Subject: [PATCH 038/757] Add Project Euler problem 79 solution 1 (#8607) Co-authored-by: Dhruv Manilawala --- project_euler/problem_079/__init__.py | 0 project_euler/problem_079/keylog.txt | 50 ++++++++++++++++ project_euler/problem_079/keylog_test.txt | 16 ++++++ project_euler/problem_079/sol1.py | 69 +++++++++++++++++++++++ 4 files changed, 135 insertions(+) create mode 100644 project_euler/problem_079/__init__.py create mode 100644 project_euler/problem_079/keylog.txt create mode 100644 project_euler/problem_079/keylog_test.txt create mode 100644 project_euler/problem_079/sol1.py diff --git a/project_euler/problem_079/__init__.py b/project_euler/problem_079/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_079/keylog.txt b/project_euler/problem_079/keylog.txt new file mode 100644 index 000000000000..41f15673248d --- /dev/null +++ b/project_euler/problem_079/keylog.txt @@ -0,0 +1,50 @@ +319 +680 +180 +690 +129 +620 +762 +689 +762 +318 +368 +710 +720 +710 +629 +168 +160 +689 +716 +731 +736 +729 +316 +729 +729 +710 +769 +290 +719 +680 +318 +389 +162 +289 +162 +718 +729 +319 +790 +680 +890 +362 +319 +760 +316 +729 +380 +319 +728 +716 diff --git a/project_euler/problem_079/keylog_test.txt b/project_euler/problem_079/keylog_test.txt new file mode 100644 index 000000000000..2c7024bde948 --- /dev/null +++ b/project_euler/problem_079/keylog_test.txt @@ -0,0 +1,16 @@ +319 +680 +180 +690 +129 +620 +698 +318 +328 +310 +320 +610 +629 +198 +190 +631 diff --git a/project_euler/problem_079/sol1.py b/project_euler/problem_079/sol1.py new file mode 100644 index 000000000000..d34adcd243b0 --- /dev/null +++ b/project_euler/problem_079/sol1.py @@ -0,0 +1,69 @@ +""" +Project Euler Problem 79: https://projecteuler.net/problem=79 + +Passcode derivation + +A common security method used for online banking is to ask the user for three +random characters from a passcode. For example, if the passcode was 531278, +they may ask for the 2nd, 3rd, and 5th characters; the expected reply would +be: 317. + +The text file, keylog.txt, contains fifty successful login attempts. + +Given that the three characters are always asked for in order, analyse the file +so as to determine the shortest possible secret passcode of unknown length. +""" +import itertools +from pathlib import Path + + +def find_secret_passcode(logins: list[str]) -> int: + """ + Returns the shortest possible secret passcode of unknown length. + + >>> find_secret_passcode(["135", "259", "235", "189", "690", "168", "120", + ... "136", "289", "589", "160", "165", "580", "369", "250", "280"]) + 12365890 + + >>> find_secret_passcode(["426", "281", "061", "819" "268", "406", "420", + ... "428", "209", "689", "019", "421", "469", "261", "681", "201"]) + 4206819 + """ + + # Split each login by character e.g. '319' -> ('3', '1', '9') + split_logins = [tuple(login) for login in logins] + + unique_chars = {char for login in split_logins for char in login} + + for permutation in itertools.permutations(unique_chars): + satisfied = True + for login in logins: + if not ( + permutation.index(login[0]) + < permutation.index(login[1]) + < permutation.index(login[2]) + ): + satisfied = False + break + + if satisfied: + return int("".join(permutation)) + + raise Exception("Unable to find the secret passcode") + + +def solution(input_file: str = "keylog.txt") -> int: + """ + Returns the shortest possible secret passcode of unknown length + for successful login attempts given by `input_file` text file. + + >>> solution("keylog_test.txt") + 6312980 + """ + logins = Path(__file__).parent.joinpath(input_file).read_text().splitlines() + + return find_secret_passcode(logins) + + +if __name__ == "__main__": + print(f"{solution() = }") From 740ecfb121009612310ab9e1bc9d6ffe22b62ae4 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 4 Apr 2023 07:00:31 +0530 Subject: [PATCH 039/757] [pre-commit.ci] pre-commit autoupdate (#8611) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/charliermarsh/ruff-pre-commit: v0.0.259 → v0.0.260](https://github.com/charliermarsh/ruff-pre-commit/compare/v0.0.259...v0.0.260) - [github.com/psf/black: 23.1.0 → 23.3.0](https://github.com/psf/black/compare/23.1.0...23.3.0) - [github.com/abravalheri/validate-pyproject: v0.12.1 → v0.12.2](https://github.com/abravalheri/validate-pyproject/compare/v0.12.1...v0.12.2) --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- .pre-commit-config.yaml | 6 +++--- DIRECTORY.md | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 72a878387e15..d54ce5adddce 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,12 +16,12 @@ repos: - id: auto-walrus - repo: https://github.com/charliermarsh/ruff-pre-commit - rev: v0.0.259 + rev: v0.0.260 hooks: - id: ruff - repo: https://github.com/psf/black - rev: 23.1.0 + rev: 23.3.0 hooks: - id: black @@ -46,7 +46,7 @@ repos: pass_filenames: false - repo: https://github.com/abravalheri/validate-pyproject - rev: v0.12.1 + rev: v0.12.2 hooks: - id: validate-pyproject diff --git a/DIRECTORY.md b/DIRECTORY.md index 8dd3fb5d9af1..3764c471ce70 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -922,6 +922,8 @@ * [Sol1](project_euler/problem_077/sol1.py) * Problem 078 * [Sol1](project_euler/problem_078/sol1.py) + * Problem 079 + * [Sol1](project_euler/problem_079/sol1.py) * Problem 080 * [Sol1](project_euler/problem_080/sol1.py) * Problem 081 From b2b8585e63664a0c7aa18b95528e345c2738c4ae Mon Sep 17 00:00:00 2001 From: Ishan Dutta Date: Fri, 7 Apr 2023 21:21:25 +0530 Subject: [PATCH 040/757] Add LeNet Implementation in PyTorch (#7070) * add torch to requirements * add lenet architecture in pytorch * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * add type hints * remove file * add type hints * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * update variable name * add fail test * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * add newline * reformatting --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- computer_vision/lenet_pytorch.py | 82 ++++++++++++++++++++++++++++++++ requirements.txt | 1 + 2 files changed, 83 insertions(+) create mode 100644 computer_vision/lenet_pytorch.py diff --git a/computer_vision/lenet_pytorch.py b/computer_vision/lenet_pytorch.py new file mode 100644 index 000000000000..177a5ebfcdb4 --- /dev/null +++ b/computer_vision/lenet_pytorch.py @@ -0,0 +1,82 @@ +""" +LeNet Network + +Paper: http://vision.stanford.edu/cs598_spring07/papers/Lecun98.pdf +""" + +import numpy +import torch +import torch.nn as nn + + +class LeNet(nn.Module): + def __init__(self) -> None: + super().__init__() + + self.tanh = nn.Tanh() + self.avgpool = nn.AvgPool2d(kernel_size=2, stride=2) + + self.conv1 = nn.Conv2d( + in_channels=1, + out_channels=6, + kernel_size=(5, 5), + stride=(1, 1), + padding=(0, 0), + ) + self.conv2 = nn.Conv2d( + in_channels=6, + out_channels=16, + kernel_size=(5, 5), + stride=(1, 1), + padding=(0, 0), + ) + self.conv3 = nn.Conv2d( + in_channels=16, + out_channels=120, + kernel_size=(5, 5), + stride=(1, 1), + padding=(0, 0), + ) + + self.linear1 = nn.Linear(120, 84) + self.linear2 = nn.Linear(84, 10) + + def forward(self, image_array: numpy.ndarray) -> numpy.ndarray: + image_array = self.tanh(self.conv1(image_array)) + image_array = self.avgpool(image_array) + image_array = self.tanh(self.conv2(image_array)) + image_array = self.avgpool(image_array) + image_array = self.tanh(self.conv3(image_array)) + + image_array = image_array.reshape(image_array.shape[0], -1) + image_array = self.tanh(self.linear1(image_array)) + image_array = self.linear2(image_array) + return image_array + + +def test_model(image_tensor: torch.tensor) -> bool: + """ + Test the model on an input batch of 64 images + + Args: + image_tensor (torch.tensor): Batch of Images for the model + + >>> test_model(torch.randn(64, 1, 32, 32)) + True + + """ + try: + model = LeNet() + output = model(image_tensor) + except RuntimeError: + return False + + return output.shape == torch.zeros([64, 10]).shape + + +if __name__ == "__main__": + random_image_1 = torch.randn(64, 1, 32, 32) + random_image_2 = torch.randn(1, 32, 32) + + print(f"random_image_1 Model Passed: {test_model(random_image_1)}") + print(f"\nrandom_image_2 Model Passed: {test_model(random_image_2)}") diff --git a/requirements.txt b/requirements.txt index acfbc823e77f..e159fe010dc4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -17,6 +17,7 @@ statsmodels sympy tensorflow texttable +torch tweepy xgboost yulewalker From 179298e3a291470ef30e850f23d98c2fb9055202 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Sat, 8 Apr 2023 02:52:26 +0200 Subject: [PATCH 041/757] Revert "Add LeNet Implementation in PyTorch (#7070)" (#8621) This reverts commit b2b8585e63664a0c7aa18b95528e345c2738c4ae. --- computer_vision/lenet_pytorch.py | 82 -------------------------------- requirements.txt | 1 - 2 files changed, 83 deletions(-) delete mode 100644 computer_vision/lenet_pytorch.py diff --git a/computer_vision/lenet_pytorch.py b/computer_vision/lenet_pytorch.py deleted file mode 100644 index 177a5ebfcdb4..000000000000 --- a/computer_vision/lenet_pytorch.py +++ /dev/null @@ -1,82 +0,0 @@ -""" -LeNet Network - -Paper: http://vision.stanford.edu/cs598_spring07/papers/Lecun98.pdf -""" - -import numpy -import torch -import torch.nn as nn - - -class LeNet(nn.Module): - def __init__(self) -> None: - super().__init__() - - self.tanh = nn.Tanh() - self.avgpool = nn.AvgPool2d(kernel_size=2, stride=2) - - self.conv1 = nn.Conv2d( - in_channels=1, - out_channels=6, - kernel_size=(5, 5), - stride=(1, 1), - padding=(0, 0), - ) - self.conv2 = nn.Conv2d( - in_channels=6, - out_channels=16, - kernel_size=(5, 5), - stride=(1, 1), - padding=(0, 0), - ) - self.conv3 = nn.Conv2d( - in_channels=16, - out_channels=120, - kernel_size=(5, 5), - stride=(1, 1), - padding=(0, 0), - ) - - self.linear1 = nn.Linear(120, 84) - self.linear2 = nn.Linear(84, 10) - - def forward(self, image_array: numpy.ndarray) -> numpy.ndarray: - image_array = self.tanh(self.conv1(image_array)) - image_array = self.avgpool(image_array) - image_array = self.tanh(self.conv2(image_array)) - image_array = self.avgpool(image_array) - image_array = self.tanh(self.conv3(image_array)) - - image_array = image_array.reshape(image_array.shape[0], -1) - image_array = self.tanh(self.linear1(image_array)) - image_array = self.linear2(image_array) - return image_array - - -def test_model(image_tensor: torch.tensor) -> bool: - """ - Test the model on an input batch of 64 images - - Args: - image_tensor (torch.tensor): Batch of Images for the model - - >>> test_model(torch.randn(64, 1, 32, 32)) - True - - """ - try: - model = LeNet() - output = model(image_tensor) - except RuntimeError: - return False - - return output.shape == torch.zeros([64, 10]).shape - - -if __name__ == "__main__": - random_image_1 = torch.randn(64, 1, 32, 32) - random_image_2 = torch.randn(1, 32, 32) - - print(f"random_image_1 Model Passed: {test_model(random_image_1)}") - print(f"\nrandom_image_2 Model Passed: {test_model(random_image_2)}") diff --git a/requirements.txt b/requirements.txt index e159fe010dc4..acfbc823e77f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -17,7 +17,6 @@ statsmodels sympy tensorflow texttable -torch tweepy xgboost yulewalker From 5cb0a000c47398c6d8af1ac43e2f83ae018f7182 Mon Sep 17 00:00:00 2001 From: amirsoroush <114881632+amirsoroush@users.noreply.github.com> Date: Sat, 8 Apr 2023 14:41:08 +0300 Subject: [PATCH 042/757] Queue implementation using two Stacks (#8617) * Queue implementation using two Stacks * fix typo in queue/queue_on_two_stacks.py * add 'iterable' to queue_on_two_stacks initializer * make queue_on_two_stacks.py generic class * fix ruff-UP007 in queue_on_two_stacks.py * enhance readability in queue_on_two_stacks.py * Create queue_by_two_stacks.py --------- Co-authored-by: Christian Clauss --- data_structures/queue/queue_by_two_stacks.py | 115 ++++++++++++++++ data_structures/queue/queue_on_two_stacks.py | 137 +++++++++++++++++++ 2 files changed, 252 insertions(+) create mode 100644 data_structures/queue/queue_by_two_stacks.py create mode 100644 data_structures/queue/queue_on_two_stacks.py diff --git a/data_structures/queue/queue_by_two_stacks.py b/data_structures/queue/queue_by_two_stacks.py new file mode 100644 index 000000000000..cd62f155a63b --- /dev/null +++ b/data_structures/queue/queue_by_two_stacks.py @@ -0,0 +1,115 @@ +"""Queue implementation using two stacks""" + +from collections.abc import Iterable +from typing import Generic, TypeVar + +_T = TypeVar("_T") + + +class QueueByTwoStacks(Generic[_T]): + def __init__(self, iterable: Iterable[_T] | None = None) -> None: + """ + >>> QueueByTwoStacks() + Queue(()) + >>> QueueByTwoStacks([10, 20, 30]) + Queue((10, 20, 30)) + >>> QueueByTwoStacks((i**2 for i in range(1, 4))) + Queue((1, 4, 9)) + """ + self._stack1: list[_T] = list(iterable or []) + self._stack2: list[_T] = [] + + def __len__(self) -> int: + """ + >>> len(QueueByTwoStacks()) + 0 + >>> from string import ascii_lowercase + >>> len(QueueByTwoStacks(ascii_lowercase)) + 26 + >>> queue = QueueByTwoStacks() + >>> for i in range(1, 11): + ... queue.put(i) + ... + >>> len(queue) + 10 + >>> for i in range(2): + ... queue.get() + 1 + 2 + >>> len(queue) + 8 + """ + + return len(self._stack1) + len(self._stack2) + + def __repr__(self) -> str: + """ + >>> queue = QueueByTwoStacks() + >>> queue + Queue(()) + >>> str(queue) + 'Queue(())' + >>> queue.put(10) + >>> queue + Queue((10,)) + >>> queue.put(20) + >>> queue.put(30) + >>> queue + Queue((10, 20, 30)) + """ + return f"Queue({tuple(self._stack2[::-1] + self._stack1)})" + + def put(self, item: _T) -> None: + """ + Put `item` into the Queue + + >>> queue = QueueByTwoStacks() + >>> queue.put(10) + >>> queue.put(20) + >>> len(queue) + 2 + >>> queue + Queue((10, 20)) + """ + + self._stack1.append(item) + + def get(self) -> _T: + """ + Get `item` from the Queue + + >>> queue = QueueByTwoStacks((10, 20, 30)) + >>> queue.get() + 10 + >>> queue.put(40) + >>> queue.get() + 20 + >>> queue.get() + 30 + >>> len(queue) + 1 + >>> queue.get() + 40 + >>> queue.get() + Traceback (most recent call last): + ... + IndexError: Queue is empty + """ + + # To reduce number of attribute look-ups in `while` loop. + stack1_pop = self._stack1.pop + stack2_append = self._stack2.append + + if not self._stack2: + while self._stack1: + stack2_append(stack1_pop()) + + if not self._stack2: + raise IndexError("Queue is empty") + return self._stack2.pop() + + +if __name__ == "__main__": + from doctest import testmod + + testmod() diff --git a/data_structures/queue/queue_on_two_stacks.py b/data_structures/queue/queue_on_two_stacks.py new file mode 100644 index 000000000000..61db2b512136 --- /dev/null +++ b/data_structures/queue/queue_on_two_stacks.py @@ -0,0 +1,137 @@ +"""Queue implementation using two stacks""" + +from collections.abc import Iterable +from typing import Generic, TypeVar + +_T = TypeVar("_T") + + +class QueueByTwoStacks(Generic[_T]): + def __init__(self, iterable: Iterable[_T] | None = None) -> None: + """ + >>> queue1 = QueueByTwoStacks() + >>> str(queue1) + 'Queue([])' + >>> queue2 = QueueByTwoStacks([10, 20, 30]) + >>> str(queue2) + 'Queue([10, 20, 30])' + >>> queue3 = QueueByTwoStacks((i**2 for i in range(1, 4))) + >>> str(queue3) + 'Queue([1, 4, 9])' + """ + + self._stack1: list[_T] = [] if iterable is None else list(iterable) + self._stack2: list[_T] = [] + + def __len__(self) -> int: + """ + >>> queue = QueueByTwoStacks() + >>> for i in range(1, 11): + ... queue.put(i) + ... + >>> len(queue) == 10 + True + >>> for i in range(2): + ... queue.get() + 1 + 2 + >>> len(queue) == 8 + True + """ + + return len(self._stack1) + len(self._stack2) + + def __repr__(self) -> str: + """ + >>> queue = QueueByTwoStacks() + >>> queue + Queue([]) + >>> str(queue) + 'Queue([])' + >>> queue.put(10) + >>> queue + Queue([10]) + >>> queue.put(20) + >>> queue.put(30) + >>> queue + Queue([10, 20, 30]) + """ + + items = self._stack2[::-1] + self._stack1 + return f"Queue({items})" + + def put(self, item: _T) -> None: + """ + Put `item` into the Queue + + >>> queue = QueueByTwoStacks() + >>> queue.put(10) + >>> queue.put(20) + >>> len(queue) == 2 + True + >>> str(queue) + 'Queue([10, 20])' + """ + + self._stack1.append(item) + + def get(self) -> _T: + """ + Get `item` from the Queue + + >>> queue = QueueByTwoStacks() + >>> for i in (10, 20, 30): + ... queue.put(i) + >>> queue.get() + 10 + >>> queue.put(40) + >>> queue.get() + 20 + >>> queue.get() + 30 + >>> len(queue) == 1 + True + >>> queue.get() + 40 + >>> queue.get() + Traceback (most recent call last): + ... + IndexError: Queue is empty + """ + + # To reduce number of attribute look-ups in `while` loop. + stack1_pop = self._stack1.pop + stack2_append = self._stack2.append + + if not self._stack2: + while self._stack1: + stack2_append(stack1_pop()) + + if not self._stack2: + raise IndexError("Queue is empty") + return self._stack2.pop() + + def size(self) -> int: + """ + Returns the length of the Queue + + >>> queue = QueueByTwoStacks() + >>> queue.size() + 0 + >>> queue.put(10) + >>> queue.put(20) + >>> queue.size() + 2 + >>> queue.get() + 10 + >>> queue.size() == 1 + True + """ + + return len(self) + + +if __name__ == "__main__": + from doctest import testmod + + testmod() From 2f9b03393c75f3ab14b491becae4ac5caf26de17 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Sat, 8 Apr 2023 14:16:19 +0200 Subject: [PATCH 043/757] Delete queue_on_two_stacks.py which duplicates queue_by_two_stacks.py (#8624) * Delete queue_on_two_stacks.py which duplicates queue_by_two_stacks.py * updating DIRECTORY.md --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 1 + data_structures/queue/queue_on_two_stacks.py | 137 ------------------- 2 files changed, 1 insertion(+), 137 deletions(-) delete mode 100644 data_structures/queue/queue_on_two_stacks.py diff --git a/DIRECTORY.md b/DIRECTORY.md index 3764c471ce70..e3e0748ecf75 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -232,6 +232,7 @@ * [Double Ended Queue](data_structures/queue/double_ended_queue.py) * [Linked Queue](data_structures/queue/linked_queue.py) * [Priority Queue Using List](data_structures/queue/priority_queue_using_list.py) + * [Queue By Two Stacks](data_structures/queue/queue_by_two_stacks.py) * [Queue On List](data_structures/queue/queue_on_list.py) * [Queue On Pseudo Stack](data_structures/queue/queue_on_pseudo_stack.py) * Stacks diff --git a/data_structures/queue/queue_on_two_stacks.py b/data_structures/queue/queue_on_two_stacks.py deleted file mode 100644 index 61db2b512136..000000000000 --- a/data_structures/queue/queue_on_two_stacks.py +++ /dev/null @@ -1,137 +0,0 @@ -"""Queue implementation using two stacks""" - -from collections.abc import Iterable -from typing import Generic, TypeVar - -_T = TypeVar("_T") - - -class QueueByTwoStacks(Generic[_T]): - def __init__(self, iterable: Iterable[_T] | None = None) -> None: - """ - >>> queue1 = QueueByTwoStacks() - >>> str(queue1) - 'Queue([])' - >>> queue2 = QueueByTwoStacks([10, 20, 30]) - >>> str(queue2) - 'Queue([10, 20, 30])' - >>> queue3 = QueueByTwoStacks((i**2 for i in range(1, 4))) - >>> str(queue3) - 'Queue([1, 4, 9])' - """ - - self._stack1: list[_T] = [] if iterable is None else list(iterable) - self._stack2: list[_T] = [] - - def __len__(self) -> int: - """ - >>> queue = QueueByTwoStacks() - >>> for i in range(1, 11): - ... queue.put(i) - ... - >>> len(queue) == 10 - True - >>> for i in range(2): - ... queue.get() - 1 - 2 - >>> len(queue) == 8 - True - """ - - return len(self._stack1) + len(self._stack2) - - def __repr__(self) -> str: - """ - >>> queue = QueueByTwoStacks() - >>> queue - Queue([]) - >>> str(queue) - 'Queue([])' - >>> queue.put(10) - >>> queue - Queue([10]) - >>> queue.put(20) - >>> queue.put(30) - >>> queue - Queue([10, 20, 30]) - """ - - items = self._stack2[::-1] + self._stack1 - return f"Queue({items})" - - def put(self, item: _T) -> None: - """ - Put `item` into the Queue - - >>> queue = QueueByTwoStacks() - >>> queue.put(10) - >>> queue.put(20) - >>> len(queue) == 2 - True - >>> str(queue) - 'Queue([10, 20])' - """ - - self._stack1.append(item) - - def get(self) -> _T: - """ - Get `item` from the Queue - - >>> queue = QueueByTwoStacks() - >>> for i in (10, 20, 30): - ... queue.put(i) - >>> queue.get() - 10 - >>> queue.put(40) - >>> queue.get() - 20 - >>> queue.get() - 30 - >>> len(queue) == 1 - True - >>> queue.get() - 40 - >>> queue.get() - Traceback (most recent call last): - ... - IndexError: Queue is empty - """ - - # To reduce number of attribute look-ups in `while` loop. - stack1_pop = self._stack1.pop - stack2_append = self._stack2.append - - if not self._stack2: - while self._stack1: - stack2_append(stack1_pop()) - - if not self._stack2: - raise IndexError("Queue is empty") - return self._stack2.pop() - - def size(self) -> int: - """ - Returns the length of the Queue - - >>> queue = QueueByTwoStacks() - >>> queue.size() - 0 - >>> queue.put(10) - >>> queue.put(20) - >>> queue.size() - 2 - >>> queue.get() - 10 - >>> queue.size() == 1 - True - """ - - return len(self) - - -if __name__ == "__main__": - from doctest import testmod - - testmod() From 14bdd174bba7828ac2bf476f3697aa13fa179492 Mon Sep 17 00:00:00 2001 From: isidroas Date: Sat, 8 Apr 2023 19:39:24 +0200 Subject: [PATCH 044/757] Bloom Filter (#8615) * Bloom filter with tests * has functions constant * fix type * isort * passing ruff * type hints * type hints * from fail to erro * captital leter * type hints requested by boot * descriptive name for m * more descriptibe arguments II * moved movies_test to doctest * commented doctest * removed test_probability * estimated error * added types * again hash_ * Update data_structures/hashing/bloom_filter.py Co-authored-by: Christian Clauss * from b to bloom * Update data_structures/hashing/bloom_filter.py Co-authored-by: Christian Clauss * Update data_structures/hashing/bloom_filter.py Co-authored-by: Christian Clauss * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * syntax error in dict comprehension * from goodfather to godfather * removed Interestellar * forgot the last Godfather * Revert "removed Interestellar" This reverts commit 35fa5f5c4bf101d073aad43c37b0a423d8975071. * pretty dict * Apply suggestions from code review * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update bloom_filter.py --------- Co-authored-by: Christian Clauss Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- data_structures/hashing/bloom_filter.py | 105 ++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 data_structures/hashing/bloom_filter.py diff --git a/data_structures/hashing/bloom_filter.py b/data_structures/hashing/bloom_filter.py new file mode 100644 index 000000000000..7fd0985bdc33 --- /dev/null +++ b/data_structures/hashing/bloom_filter.py @@ -0,0 +1,105 @@ +""" +See https://en.wikipedia.org/wiki/Bloom_filter + +The use of this data structure is to test membership in a set. +Compared to Python's built-in set() it is more space-efficient. +In the following example, only 8 bits of memory will be used: +>>> bloom = Bloom(size=8) + +Initially, the filter contains all zeros: +>>> bloom.bitstring +'00000000' + +When an element is added, two bits are set to 1 +since there are 2 hash functions in this implementation: +>>> "Titanic" in bloom +False +>>> bloom.add("Titanic") +>>> bloom.bitstring +'01100000' +>>> "Titanic" in bloom +True + +However, sometimes only one bit is added +because both hash functions return the same value +>>> bloom.add("Avatar") +>>> "Avatar" in bloom +True +>>> bloom.format_hash("Avatar") +'00000100' +>>> bloom.bitstring +'01100100' + +Not added elements should return False ... +>>> not_present_films = ("The Godfather", "Interstellar", "Parasite", "Pulp Fiction") +>>> { +... film: bloom.format_hash(film) for film in not_present_films +... } # doctest: +NORMALIZE_WHITESPACE +{'The Godfather': '00000101', + 'Interstellar': '00000011', + 'Parasite': '00010010', + 'Pulp Fiction': '10000100'} +>>> any(film in bloom for film in not_present_films) +False + +but sometimes there are false positives: +>>> "Ratatouille" in bloom +True +>>> bloom.format_hash("Ratatouille") +'01100000' + +The probability increases with the number of elements added. +The probability decreases with the number of bits in the bitarray. +>>> bloom.estimated_error_rate +0.140625 +>>> bloom.add("The Godfather") +>>> bloom.estimated_error_rate +0.25 +>>> bloom.bitstring +'01100101' +""" +from hashlib import md5, sha256 + +HASH_FUNCTIONS = (sha256, md5) + + +class Bloom: + def __init__(self, size: int = 8) -> None: + self.bitarray = 0b0 + self.size = size + + def add(self, value: str) -> None: + h = self.hash_(value) + self.bitarray |= h + + def exists(self, value: str) -> bool: + h = self.hash_(value) + return (h & self.bitarray) == h + + def __contains__(self, other: str) -> bool: + return self.exists(other) + + def format_bin(self, bitarray: int) -> str: + res = bin(bitarray)[2:] + return res.zfill(self.size) + + @property + def bitstring(self) -> str: + return self.format_bin(self.bitarray) + + def hash_(self, value: str) -> int: + res = 0b0 + for func in HASH_FUNCTIONS: + position = ( + int.from_bytes(func(value.encode()).digest(), "little") % self.size + ) + res |= 2**position + return res + + def format_hash(self, value: str) -> str: + return self.format_bin(self.hash_(value)) + + @property + def estimated_error_rate(self) -> float: + n_ones = bin(self.bitarray).count("1") + return (n_ones / self.size) ** len(HASH_FUNCTIONS) From d182f95646aa7c515afe0912a34e8c2a11a34ca3 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 10 Apr 2023 23:43:17 +0200 Subject: [PATCH 045/757] [pre-commit.ci] pre-commit autoupdate (#8634) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/charliermarsh/ruff-pre-commit: v0.0.260 → v0.0.261](https://github.com/charliermarsh/ruff-pre-commit/compare/v0.0.260...v0.0.261) - [github.com/pre-commit/mirrors-mypy: v1.1.1 → v1.2.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.1.1...v1.2.0) * updating DIRECTORY.md --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- .pre-commit-config.yaml | 4 ++-- DIRECTORY.md | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d54ce5adddce..55345a574ce9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/charliermarsh/ruff-pre-commit - rev: v0.0.260 + rev: v0.0.261 hooks: - id: ruff @@ -51,7 +51,7 @@ repos: - id: validate-pyproject - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.1.1 + rev: v1.2.0 hooks: - id: mypy args: diff --git a/DIRECTORY.md b/DIRECTORY.md index e3e0748ecf75..36f5a752c48b 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -195,6 +195,7 @@ * [Alternate Disjoint Set](data_structures/disjoint_set/alternate_disjoint_set.py) * [Disjoint Set](data_structures/disjoint_set/disjoint_set.py) * Hashing + * [Bloom Filter](data_structures/hashing/bloom_filter.py) * [Double Hash](data_structures/hashing/double_hash.py) * [Hash Map](data_structures/hashing/hash_map.py) * [Hash Table](data_structures/hashing/hash_table.py) From 54dedf844a30d39bd42c66ebf9cd67ec186f47bb Mon Sep 17 00:00:00 2001 From: Diego Gasco <62801631+Diegomangasco@users.noreply.github.com> Date: Mon, 17 Apr 2023 00:34:22 +0200 Subject: [PATCH 046/757] Dimensionality reduction (#8590) --- machine_learning/dimensionality_reduction.py | 198 +++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100644 machine_learning/dimensionality_reduction.py diff --git a/machine_learning/dimensionality_reduction.py b/machine_learning/dimensionality_reduction.py new file mode 100644 index 000000000000..d2046f81af04 --- /dev/null +++ b/machine_learning/dimensionality_reduction.py @@ -0,0 +1,198 @@ +# Copyright (c) 2023 Diego Gasco (diego.gasco99@gmail.com), Diegomangasco on GitHub + +""" +Requirements: + - numpy version 1.21 + - scipy version 1.3.3 +Notes: + - Each column of the features matrix corresponds to a class item +""" + +import logging + +import numpy as np +import pytest +from scipy.linalg import eigh + +logging.basicConfig(level=logging.INFO, format="%(message)s") + + +def column_reshape(input_array: np.ndarray) -> np.ndarray: + """Function to reshape a row Numpy array into a column Numpy array + >>> input_array = np.array([1, 2, 3]) + >>> column_reshape(input_array) + array([[1], + [2], + [3]]) + """ + + return input_array.reshape((input_array.size, 1)) + + +def covariance_within_classes( + features: np.ndarray, labels: np.ndarray, classes: int +) -> np.ndarray: + """Function to compute the covariance matrix inside each class. + >>> features = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) + >>> labels = np.array([0, 1, 0]) + >>> covariance_within_classes(features, labels, 2) + array([[0.66666667, 0.66666667, 0.66666667], + [0.66666667, 0.66666667, 0.66666667], + [0.66666667, 0.66666667, 0.66666667]]) + """ + + covariance_sum = np.nan + for i in range(classes): + data = features[:, labels == i] + data_mean = data.mean(1) + # Centralize the data of class i + centered_data = data - column_reshape(data_mean) + if i > 0: + # If covariance_sum is not None + covariance_sum += np.dot(centered_data, centered_data.T) + else: + # If covariance_sum is np.nan (i.e. first loop) + covariance_sum = np.dot(centered_data, centered_data.T) + + return covariance_sum / features.shape[1] + + +def covariance_between_classes( + features: np.ndarray, labels: np.ndarray, classes: int +) -> np.ndarray: + """Function to compute the covariance matrix between multiple classes + >>> features = np.array([[9, 2, 3], [4, 3, 6], [1, 8, 9]]) + >>> labels = np.array([0, 1, 0]) + >>> covariance_between_classes(features, labels, 2) + array([[ 3.55555556, 1.77777778, -2.66666667], + [ 1.77777778, 0.88888889, -1.33333333], + [-2.66666667, -1.33333333, 2. ]]) + """ + + general_data_mean = features.mean(1) + covariance_sum = np.nan + for i in range(classes): + data = features[:, labels == i] + device_data = data.shape[1] + data_mean = data.mean(1) + if i > 0: + # If covariance_sum is not None + covariance_sum += device_data * np.dot( + column_reshape(data_mean) - column_reshape(general_data_mean), + (column_reshape(data_mean) - column_reshape(general_data_mean)).T, + ) + else: + # If covariance_sum is np.nan (i.e. first loop) + covariance_sum = device_data * np.dot( + column_reshape(data_mean) - column_reshape(general_data_mean), + (column_reshape(data_mean) - column_reshape(general_data_mean)).T, + ) + + return covariance_sum / features.shape[1] + + +def principal_component_analysis(features: np.ndarray, dimensions: int) -> np.ndarray: + """ + Principal Component Analysis. + + For more details, see: https://en.wikipedia.org/wiki/Principal_component_analysis. + Parameters: + * features: the features extracted from the dataset + * dimensions: to filter the projected data for the desired dimension + + >>> test_principal_component_analysis() + """ + + # Check if the features have been loaded + if features.any(): + data_mean = features.mean(1) + # Center the dataset + centered_data = features - np.reshape(data_mean, (data_mean.size, 1)) + covariance_matrix = np.dot(centered_data, centered_data.T) / features.shape[1] + _, eigenvectors = np.linalg.eigh(covariance_matrix) + # Take all the columns in the reverse order (-1), and then takes only the first + filtered_eigenvectors = eigenvectors[:, ::-1][:, 0:dimensions] + # Project the database on the new space + projected_data = np.dot(filtered_eigenvectors.T, features) + logging.info("Principal Component Analysis computed") + + return projected_data + else: + logging.basicConfig(level=logging.ERROR, format="%(message)s", force=True) + logging.error("Dataset empty") + raise AssertionError + + +def linear_discriminant_analysis( + features: np.ndarray, labels: np.ndarray, classes: int, dimensions: int +) -> np.ndarray: + """ + Linear Discriminant Analysis. + + For more details, see: https://en.wikipedia.org/wiki/Linear_discriminant_analysis. + Parameters: + * features: the features extracted from the dataset + * labels: the class labels of the features + * classes: the number of classes present in the dataset + * dimensions: to filter the projected data for the desired dimension + + >>> test_linear_discriminant_analysis() + """ + + # Check if the dimension desired is less than the number of classes + assert classes > dimensions + + # Check if features have been already loaded + if features.any: + _, eigenvectors = eigh( + covariance_between_classes(features, labels, classes), + covariance_within_classes(features, labels, classes), + ) + filtered_eigenvectors = eigenvectors[:, ::-1][:, :dimensions] + svd_matrix, _, _ = np.linalg.svd(filtered_eigenvectors) + filtered_svd_matrix = svd_matrix[:, 0:dimensions] + projected_data = np.dot(filtered_svd_matrix.T, features) + logging.info("Linear Discriminant Analysis computed") + + return projected_data + else: + logging.basicConfig(level=logging.ERROR, format="%(message)s", force=True) + logging.error("Dataset empty") + raise AssertionError + + +def test_linear_discriminant_analysis() -> None: + # Create dummy dataset with 2 classes and 3 features + features = np.array([[1, 2, 3, 4, 5], [2, 3, 4, 5, 6], [3, 4, 5, 6, 7]]) + labels = np.array([0, 0, 0, 1, 1]) + classes = 2 + dimensions = 2 + + # Assert that the function raises an AssertionError if dimensions > classes + with pytest.raises(AssertionError) as error_info: + projected_data = linear_discriminant_analysis( + features, labels, classes, dimensions + ) + if isinstance(projected_data, np.ndarray): + raise AssertionError( + "Did not raise AssertionError for dimensions > classes" + ) + assert error_info.type is AssertionError + + +def test_principal_component_analysis() -> None: + features = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) + dimensions = 2 + expected_output = np.array([[6.92820323, 8.66025404, 10.39230485], [3.0, 3.0, 3.0]]) + + with pytest.raises(AssertionError) as error_info: + output = principal_component_analysis(features, dimensions) + if not np.allclose(expected_output, output): + raise AssertionError + assert error_info.type is AssertionError + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 2b051a2de4adf711857f5453286dff47d1d87636 Mon Sep 17 00:00:00 2001 From: Rohan Anand <96521078+rohan472000@users.noreply.github.com> Date: Tue, 18 Apr 2023 03:47:48 +0530 Subject: [PATCH 047/757] Create real_and_reactive_power.py (#8665) --- electronics/real_and_reactive_power.py | 49 ++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 electronics/real_and_reactive_power.py diff --git a/electronics/real_and_reactive_power.py b/electronics/real_and_reactive_power.py new file mode 100644 index 000000000000..81dcba800e82 --- /dev/null +++ b/electronics/real_and_reactive_power.py @@ -0,0 +1,49 @@ +import math + + +def real_power(apparent_power: float, power_factor: float) -> float: + """ + Calculate real power from apparent power and power factor. + + Examples: + >>> real_power(100, 0.9) + 90.0 + >>> real_power(0, 0.8) + 0.0 + >>> real_power(100, -0.9) + -90.0 + """ + if ( + not isinstance(power_factor, (int, float)) + or power_factor < -1 + or power_factor > 1 + ): + raise ValueError("power_factor must be a valid float value between -1 and 1.") + return apparent_power * power_factor + + +def reactive_power(apparent_power: float, power_factor: float) -> float: + """ + Calculate reactive power from apparent power and power factor. + + Examples: + >>> reactive_power(100, 0.9) + 43.58898943540673 + >>> reactive_power(0, 0.8) + 0.0 + >>> reactive_power(100, -0.9) + 43.58898943540673 + """ + if ( + not isinstance(power_factor, (int, float)) + or power_factor < -1 + or power_factor > 1 + ): + raise ValueError("power_factor must be a valid float value between -1 and 1.") + return apparent_power * math.sqrt(1 - power_factor**2) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From b5047cfa114c6343b92370419772b9cf0f13e634 Mon Sep 17 00:00:00 2001 From: Rohan Anand <96521078+rohan472000@users.noreply.github.com> Date: Tue, 18 Apr 2023 13:00:01 +0530 Subject: [PATCH 048/757] Create apparent_power.py (#8664) * Create apparent_power.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update apparent_power.py * Update apparent_power.py * Update apparent_power.py * Update electronics/apparent_power.py Co-authored-by: Christian Clauss * Update electronics/apparent_power.py Co-authored-by: Christian Clauss * Update apparent_power.py * Update electronics/apparent_power.py Co-authored-by: Christian Clauss * Update apparent_power.py * Update apparent_power.py * Update apparent_power.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update apparent_power.py * Update apparent_power.py * Update apparent_power.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- electronics/apparent_power.py | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 electronics/apparent_power.py diff --git a/electronics/apparent_power.py b/electronics/apparent_power.py new file mode 100644 index 000000000000..a6f1a50822f7 --- /dev/null +++ b/electronics/apparent_power.py @@ -0,0 +1,35 @@ +import cmath +import math + + +def apparent_power( + voltage: float, current: float, voltage_angle: float, current_angle: float +) -> complex: + """ + Calculate the apparent power in a single-phase AC circuit. + + >>> apparent_power(100, 5, 0, 0) + (500+0j) + >>> apparent_power(100, 5, 90, 0) + (3.061616997868383e-14+500j) + >>> apparent_power(100, 5, -45, -60) + (-129.40952255126027-482.9629131445341j) + >>> apparent_power(200, 10, -30, -90) + (-999.9999999999998-1732.0508075688776j) + """ + # Convert angles from degrees to radians + voltage_angle_rad = math.radians(voltage_angle) + current_angle_rad = math.radians(current_angle) + + # Convert voltage and current to rectangular form + voltage_rect = cmath.rect(voltage, voltage_angle_rad) + current_rect = cmath.rect(current, current_angle_rad) + + # Calculate apparent power + return voltage_rect * current_rect + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 93ce8cb75da2740089df8db23fa493ce104a011b Mon Sep 17 00:00:00 2001 From: Rohan Anand <96521078+rohan472000@users.noreply.github.com> Date: Tue, 18 Apr 2023 13:14:06 +0530 Subject: [PATCH 049/757] added reference link. (#8667) * added reference link. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- electronics/apparent_power.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/electronics/apparent_power.py b/electronics/apparent_power.py index a6f1a50822f7..0ce1c2aa95b9 100644 --- a/electronics/apparent_power.py +++ b/electronics/apparent_power.py @@ -8,6 +8,8 @@ def apparent_power( """ Calculate the apparent power in a single-phase AC circuit. + Reference: https://en.wikipedia.org/wiki/AC_power#Apparent_power + >>> apparent_power(100, 5, 0, 0) (500+0j) >>> apparent_power(100, 5, 90, 0) From 458debc237d41752c6c4223264a4bb23efb2ecec Mon Sep 17 00:00:00 2001 From: Rohan Anand <96521078+rohan472000@users.noreply.github.com> Date: Tue, 18 Apr 2023 13:32:20 +0530 Subject: [PATCH 050/757] added a problem with solution on sliding window. (#8566) * added a problem with solution on sliding window. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * added hint for return type and parameter * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update minimum_size_subarray_sum.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update minimum_size_subarray_sum.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update minimum_size_subarray_sum.py * Update minimum_size_subarray_sum.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update minimum_size_subarray_sum.py * removed un-necessary docs and added 2 test cases * Rename sliding_window/minimum_size_subarray_sum.py to dynamic_programming/minimum_size_subarray_sum.py * Update minimum_size_subarray_sum.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update minimum_size_subarray_sum.py * Update minimum_size_subarray_sum.py * Update minimum_size_subarray_sum.py * Update minimum_size_subarray_sum.py * Update minimum_size_subarray_sum.py * Update minimum_size_subarray_sum.py * Update dynamic_programming/minimum_size_subarray_sum.py Co-authored-by: Christian Clauss * Update dynamic_programming/minimum_size_subarray_sum.py Co-authored-by: Christian Clauss * Update dynamic_programming/minimum_size_subarray_sum.py Co-authored-by: Christian Clauss * Update dynamic_programming/minimum_size_subarray_sum.py Co-authored-by: Christian Clauss * Update dynamic_programming/minimum_size_subarray_sum.py Co-authored-by: Christian Clauss * Update dynamic_programming/minimum_size_subarray_sum.py Co-authored-by: Christian Clauss * Update dynamic_programming/minimum_size_subarray_sum.py Co-authored-by: Christian Clauss * Update minimum_size_subarray_sum.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update minimum_size_subarray_sum.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update minimum_size_subarray_sum.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update minimum_size_subarray_sum.py * Update minimum_size_subarray_sum.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- .../minimum_size_subarray_sum.py | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 dynamic_programming/minimum_size_subarray_sum.py diff --git a/dynamic_programming/minimum_size_subarray_sum.py b/dynamic_programming/minimum_size_subarray_sum.py new file mode 100644 index 000000000000..3868d73535fb --- /dev/null +++ b/dynamic_programming/minimum_size_subarray_sum.py @@ -0,0 +1,62 @@ +import sys + + +def minimum_subarray_sum(target: int, numbers: list[int]) -> int: + """ + Return the length of the shortest contiguous subarray in a list of numbers whose sum + is at least target. Reference: https://stackoverflow.com/questions/8269916 + + >>> minimum_subarray_sum(7, [2, 3, 1, 2, 4, 3]) + 2 + >>> minimum_subarray_sum(7, [2, 3, -1, 2, 4, -3]) + 4 + >>> minimum_subarray_sum(11, [1, 1, 1, 1, 1, 1, 1, 1]) + 0 + >>> minimum_subarray_sum(10, [1, 2, 3, 4, 5, 6, 7]) + 2 + >>> minimum_subarray_sum(5, [1, 1, 1, 1, 1, 5]) + 1 + >>> minimum_subarray_sum(0, []) + 0 + >>> minimum_subarray_sum(0, [1, 2, 3]) + 1 + >>> minimum_subarray_sum(10, [10, 20, 30]) + 1 + >>> minimum_subarray_sum(7, [1, 1, 1, 1, 1, 1, 10]) + 1 + >>> minimum_subarray_sum(6, []) + 0 + >>> minimum_subarray_sum(2, [1, 2, 3]) + 1 + >>> minimum_subarray_sum(-6, []) + 0 + >>> minimum_subarray_sum(-6, [3, 4, 5]) + 1 + >>> minimum_subarray_sum(8, None) + 0 + >>> minimum_subarray_sum(2, "ABC") + Traceback (most recent call last): + ... + ValueError: numbers must be an iterable of integers + """ + if not numbers: + return 0 + if target == 0 and target in numbers: + return 0 + if not isinstance(numbers, (list, tuple)) or not all( + isinstance(number, int) for number in numbers + ): + raise ValueError("numbers must be an iterable of integers") + + left = right = curr_sum = 0 + min_len = sys.maxsize + + while right < len(numbers): + curr_sum += numbers[right] + while curr_sum >= target and left <= right: + min_len = min(min_len, right - left + 1) + curr_sum -= numbers[left] + left += 1 + right += 1 + + return 0 if min_len == sys.maxsize else min_len From 11582943a555ae3b6a22938df6d3645b0327562e Mon Sep 17 00:00:00 2001 From: JulianStiebler <68881884+JulianStiebler@users.noreply.github.com> Date: Tue, 18 Apr 2023 11:57:48 +0200 Subject: [PATCH 051/757] Create maths/pi_generator.py (#8666) * Create pi_generator.py * Update pi_generator.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update pi_generator.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update pi_generator.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update pi_generator.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update pi_generator.py * Update pi_generator.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Updated commentary on line 28, added math.pi comparison & math.isclose() test * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Removed # noqa: E501 * printf() added as recommended by cclaus --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- maths/pi_generator.py | 94 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 maths/pi_generator.py diff --git a/maths/pi_generator.py b/maths/pi_generator.py new file mode 100644 index 000000000000..dcd218aae309 --- /dev/null +++ b/maths/pi_generator.py @@ -0,0 +1,94 @@ +def calculate_pi(limit: int) -> str: + """ + https://en.wikipedia.org/wiki/Leibniz_formula_for_%CF%80 + Leibniz Formula for Pi + + The Leibniz formula is the special case arctan 1 = 1/4 Pi . + Leibniz's formula converges extremely slowly: it exhibits sublinear convergence. + + Convergence (https://en.wikipedia.org/wiki/Leibniz_formula_for_%CF%80#Convergence) + + We cannot try to prove against an interrupted, uncompleted generation. + https://en.wikipedia.org/wiki/Leibniz_formula_for_%CF%80#Unusual_behaviour + The errors can in fact be predicted; + but those calculations also approach infinity for accuracy. + + Our output will always be a string since we can defintely store all digits in there. + For simplicity' sake, let's just compare against known values and since our outpit + is a string, we need to convert to float. + + >>> import math + >>> float(calculate_pi(15)) == math.pi + True + + Since we cannot predict errors or interrupt any infinite alternating + series generation since they approach infinity, + or interrupt any alternating series, we are going to need math.isclose() + + >>> math.isclose(float(calculate_pi(50)), math.pi) + True + + >>> math.isclose(float(calculate_pi(100)), math.pi) + True + + Since math.pi-constant contains only 16 digits, here some test with preknown values: + + >>> calculate_pi(50) + '3.14159265358979323846264338327950288419716939937510' + >>> calculate_pi(80) + '3.14159265358979323846264338327950288419716939937510582097494459230781640628620899' + + To apply the Leibniz formula for calculating pi, + the variables q, r, t, k, n, and l are used for the iteration process. + """ + q = 1 + r = 0 + t = 1 + k = 1 + n = 3 + l = 3 + decimal = limit + counter = 0 + + result = "" + + """ + We will avoid using yield since we otherwise get a Generator-Object, + which we can't just compare against anything. We would have to make a list out of it + after the generation, so we will just stick to plain return logic: + """ + while counter != decimal + 1: + if 4 * q + r - t < n * t: + result += str(n) + if counter == 0: + result += "." + + if decimal == counter: + break + + counter += 1 + nr = 10 * (r - n * t) + n = ((10 * (3 * q + r)) // t) - 10 * n + q *= 10 + r = nr + else: + nr = (2 * q + r) * l + nn = (q * (7 * k) + 2 + (r * l)) // (t * l) + q *= k + t *= l + l += 2 + k += 1 + n = nn + r = nr + return result + + +def main() -> None: + print(f"{calculate_pi(50) = }") + import doctest + + doctest.testmod() + + +if __name__ == "__main__": + main() From bf30b18192dd7ff9a43523ee6efe5c015ae6b99c Mon Sep 17 00:00:00 2001 From: Rohan Anand <96521078+rohan472000@users.noreply.github.com> Date: Mon, 24 Apr 2023 10:58:30 +0530 Subject: [PATCH 052/757] Update linear_discriminant_analysis.py and rsa_cipher.py (#8680) * Update rsa_cipher.py by replacing %s with {} * Update rsa_cipher.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update linear_discriminant_analysis.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update linear_discriminant_analysis.py * Update linear_discriminant_analysis.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update linear_discriminant_analysis.py * Update linear_discriminant_analysis.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update linear_discriminant_analysis.py * Update machine_learning/linear_discriminant_analysis.py Co-authored-by: Christian Clauss * Update linear_discriminant_analysis.py * updated --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- ciphers/rsa_cipher.py | 14 ++++++++------ machine_learning/linear_discriminant_analysis.py | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/ciphers/rsa_cipher.py b/ciphers/rsa_cipher.py index de26992f5eeb..9c41cdc5d472 100644 --- a/ciphers/rsa_cipher.py +++ b/ciphers/rsa_cipher.py @@ -76,10 +76,11 @@ def encrypt_and_write_to_file( key_size, n, e = read_key_file(key_filename) if key_size < block_size * 8: sys.exit( - "ERROR: Block size is %s bits and key size is %s bits. The RSA cipher " + "ERROR: Block size is {} bits and key size is {} bits. The RSA cipher " "requires the block size to be equal to or greater than the key size. " - "Either decrease the block size or use different keys." - % (block_size * 8, key_size) + "Either decrease the block size or use different keys.".format( + block_size * 8, key_size + ) ) encrypted_blocks = [str(i) for i in encrypt_message(message, (n, e), block_size)] @@ -101,10 +102,11 @@ def read_from_file_and_decrypt(message_filename: str, key_filename: str) -> str: if key_size < block_size * 8: sys.exit( - "ERROR: Block size is %s bits and key size is %s bits. The RSA cipher " + "ERROR: Block size is {} bits and key size is {} bits. The RSA cipher " "requires the block size to be equal to or greater than the key size. " - "Did you specify the correct key file and encrypted file?" - % (block_size * 8, key_size) + "Did you specify the correct key file and encrypted file?".format( + block_size * 8, key_size + ) ) encrypted_blocks = [] diff --git a/machine_learning/linear_discriminant_analysis.py b/machine_learning/linear_discriminant_analysis.py index f4fb5ba76b64..c0a477be10c7 100644 --- a/machine_learning/linear_discriminant_analysis.py +++ b/machine_learning/linear_discriminant_analysis.py @@ -399,7 +399,7 @@ def main(): if input("Press any key to restart or 'q' for quit: ").strip().lower() == "q": print("\n" + "GoodBye!".center(100, "-") + "\n") break - system("cls" if name == "nt" else "clear") + system("clear" if name == "posix" else "cls") # noqa: S605 if __name__ == "__main__": From a650426350dc7833ff1110bc2e434763caed631e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 25 Apr 2023 06:05:45 +0200 Subject: [PATCH 053/757] [pre-commit.ci] pre-commit autoupdate (#8691) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/charliermarsh/ruff-pre-commit: v0.0.261 → v0.0.262](https://github.com/charliermarsh/ruff-pre-commit/compare/v0.0.261...v0.0.262) - [github.com/tox-dev/pyproject-fmt: 0.9.2 → 0.10.0](https://github.com/tox-dev/pyproject-fmt/compare/0.9.2...0.10.0) * updating DIRECTORY.md --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- .pre-commit-config.yaml | 4 ++-- DIRECTORY.md | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 55345a574ce9..288473ca365f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/charliermarsh/ruff-pre-commit - rev: v0.0.261 + rev: v0.0.262 hooks: - id: ruff @@ -33,7 +33,7 @@ repos: - tomli - repo: https://github.com/tox-dev/pyproject-fmt - rev: "0.9.2" + rev: "0.10.0" hooks: - id: pyproject-fmt diff --git a/DIRECTORY.md b/DIRECTORY.md index 36f5a752c48b..8e67c85c6fa8 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -327,6 +327,7 @@ * [Minimum Coin Change](dynamic_programming/minimum_coin_change.py) * [Minimum Cost Path](dynamic_programming/minimum_cost_path.py) * [Minimum Partition](dynamic_programming/minimum_partition.py) + * [Minimum Size Subarray Sum](dynamic_programming/minimum_size_subarray_sum.py) * [Minimum Squares To Represent A Number](dynamic_programming/minimum_squares_to_represent_a_number.py) * [Minimum Steps To One](dynamic_programming/minimum_steps_to_one.py) * [Minimum Tickets Cost](dynamic_programming/minimum_tickets_cost.py) @@ -339,6 +340,7 @@ * [Word Break](dynamic_programming/word_break.py) ## Electronics + * [Apparent Power](electronics/apparent_power.py) * [Builtin Voltage](electronics/builtin_voltage.py) * [Carrier Concentration](electronics/carrier_concentration.py) * [Circular Convolution](electronics/circular_convolution.py) @@ -348,6 +350,7 @@ * [Electrical Impedance](electronics/electrical_impedance.py) * [Ind Reactance](electronics/ind_reactance.py) * [Ohms Law](electronics/ohms_law.py) + * [Real And Reactive Power](electronics/real_and_reactive_power.py) * [Resistor Equivalence](electronics/resistor_equivalence.py) * [Resonant Frequency](electronics/resonant_frequency.py) @@ -483,6 +486,7 @@ * [Astar](machine_learning/astar.py) * [Data Transformations](machine_learning/data_transformations.py) * [Decision Tree](machine_learning/decision_tree.py) + * [Dimensionality Reduction](machine_learning/dimensionality_reduction.py) * Forecasting * [Run](machine_learning/forecasting/run.py) * [Gradient Descent](machine_learning/gradient_descent.py) @@ -604,6 +608,7 @@ * [Perfect Number](maths/perfect_number.py) * [Perfect Square](maths/perfect_square.py) * [Persistence](maths/persistence.py) + * [Pi Generator](maths/pi_generator.py) * [Pi Monte Carlo Estimation](maths/pi_monte_carlo_estimation.py) * [Points Are Collinear 3D](maths/points_are_collinear_3d.py) * [Pollard Rho](maths/pollard_rho.py) From c1b3ea5355266bb47daba378ca10200c4d359453 Mon Sep 17 00:00:00 2001 From: Dipankar Mitra <50228537+Mitra-babu@users.noreply.github.com> Date: Tue, 25 Apr 2023 21:36:14 +0530 Subject: [PATCH 054/757] The tanh activation function is added (#8689) * tanh function been added * tanh function been added * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * tanh function is added * tanh function is added * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * tanh function added * tanh function added * tanh function is added * Apply suggestions from code review --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- maths/tanh.py | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 maths/tanh.py diff --git a/maths/tanh.py b/maths/tanh.py new file mode 100644 index 000000000000..ddab3e1ab717 --- /dev/null +++ b/maths/tanh.py @@ -0,0 +1,42 @@ +""" +This script demonstrates the implementation of the tangent hyperbolic +or tanh function. + +The function takes a vector of K real numbers as input and +then (e^x - e^(-x))/(e^x + e^(-x)). After through tanh, the +element of the vector mostly -1 between 1. + +Script inspired from its corresponding Wikipedia article +https://en.wikipedia.org/wiki/Activation_function +""" +import numpy as np + + +def tangent_hyperbolic(vector: np.array) -> np.array: + """ + Implements the tanh function + + Parameters: + vector: np.array + + Returns: + tanh (np.array): The input numpy array after applying tanh. + + mathematically (e^x - e^(-x))/(e^x + e^(-x)) can be written as (2/(1+e^(-2x))-1 + + Examples: + >>> tangent_hyperbolic(np.array([1,5,6,-0.67])) + array([ 0.76159416, 0.9999092 , 0.99998771, -0.58497988]) + + >>> tangent_hyperbolic(np.array([8,10,2,-0.98,13])) + array([ 0.99999977, 1. , 0.96402758, -0.7530659 , 1. ]) + + """ + + return (2 / (1 + np.exp(-2 * vector))) - 1 + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 4c1f876567673db0934ba65d662ea221465ec921 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Thu, 27 Apr 2023 19:32:07 +0200 Subject: [PATCH 055/757] Solving the `Top k most frequent words` problem using a max-heap (#8685) * Solving the `Top k most frequent words` problem using a max-heap * Mentioning Python standard library solution in `Top k most frequent words` docstring * ruff --fix . * updating DIRECTORY.md --------- Co-authored-by: Amos Paribocci Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 1 + data_structures/heap/heap.py | 31 ++++-- .../linear_discriminant_analysis.py | 2 +- strings/top_k_frequent_words.py | 101 ++++++++++++++++++ 4 files changed, 128 insertions(+), 7 deletions(-) create mode 100644 strings/top_k_frequent_words.py diff --git a/DIRECTORY.md b/DIRECTORY.md index 8e67c85c6fa8..681d252b232d 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -1167,6 +1167,7 @@ * [Snake Case To Camel Pascal Case](strings/snake_case_to_camel_pascal_case.py) * [Split](strings/split.py) * [Text Justification](strings/text_justification.py) + * [Top K Frequent Words](strings/top_k_frequent_words.py) * [Upper](strings/upper.py) * [Wave](strings/wave.py) * [Wildcard Pattern Matching](strings/wildcard_pattern_matching.py) diff --git a/data_structures/heap/heap.py b/data_structures/heap/heap.py index b14c55d9db4c..c1004f349479 100644 --- a/data_structures/heap/heap.py +++ b/data_structures/heap/heap.py @@ -1,9 +1,28 @@ from __future__ import annotations +from abc import abstractmethod from collections.abc import Iterable +from typing import Generic, Protocol, TypeVar -class Heap: +class Comparable(Protocol): + @abstractmethod + def __lt__(self: T, other: T) -> bool: + pass + + @abstractmethod + def __gt__(self: T, other: T) -> bool: + pass + + @abstractmethod + def __eq__(self: T, other: object) -> bool: + pass + + +T = TypeVar("T", bound=Comparable) + + +class Heap(Generic[T]): """A Max Heap Implementation >>> unsorted = [103, 9, 1, 7, 11, 15, 25, 201, 209, 107, 5] @@ -27,7 +46,7 @@ class Heap: """ def __init__(self) -> None: - self.h: list[float] = [] + self.h: list[T] = [] self.heap_size: int = 0 def __repr__(self) -> str: @@ -79,7 +98,7 @@ def max_heapify(self, index: int) -> None: # fix the subsequent violation recursively if any self.max_heapify(violation) - def build_max_heap(self, collection: Iterable[float]) -> None: + def build_max_heap(self, collection: Iterable[T]) -> None: """build max heap from an unsorted array""" self.h = list(collection) self.heap_size = len(self.h) @@ -88,7 +107,7 @@ def build_max_heap(self, collection: Iterable[float]) -> None: for i in range(self.heap_size // 2 - 1, -1, -1): self.max_heapify(i) - def extract_max(self) -> float: + def extract_max(self) -> T: """get and remove max from heap""" if self.heap_size >= 2: me = self.h[0] @@ -102,7 +121,7 @@ def extract_max(self) -> float: else: raise Exception("Empty heap") - def insert(self, value: float) -> None: + def insert(self, value: T) -> None: """insert a new value into the max heap""" self.h.append(value) idx = (self.heap_size - 1) // 2 @@ -144,7 +163,7 @@ def heap_sort(self) -> None: ]: print(f"unsorted array: {unsorted}") - heap = Heap() + heap: Heap[int] = Heap() heap.build_max_heap(unsorted) print(f"after build heap: {heap}") diff --git a/machine_learning/linear_discriminant_analysis.py b/machine_learning/linear_discriminant_analysis.py index c0a477be10c7..88c047157893 100644 --- a/machine_learning/linear_discriminant_analysis.py +++ b/machine_learning/linear_discriminant_analysis.py @@ -399,7 +399,7 @@ def main(): if input("Press any key to restart or 'q' for quit: ").strip().lower() == "q": print("\n" + "GoodBye!".center(100, "-") + "\n") break - system("clear" if name == "posix" else "cls") # noqa: S605 + system("cls" if name == "nt" else "clear") # noqa: S605 if __name__ == "__main__": diff --git a/strings/top_k_frequent_words.py b/strings/top_k_frequent_words.py new file mode 100644 index 000000000000..f3d1e0cd5ca7 --- /dev/null +++ b/strings/top_k_frequent_words.py @@ -0,0 +1,101 @@ +""" +Finds the top K most frequent words from the provided word list. + +This implementation aims to show how to solve the problem using the Heap class +already present in this repository. +Computing order statistics is, in fact, a typical usage of heaps. + +This is mostly shown for educational purposes, since the problem can be solved +in a few lines using collections.Counter from the Python standard library: + +from collections import Counter +def top_k_frequent_words(words, k_value): + return [x[0] for x in Counter(words).most_common(k_value)] +""" + + +from collections import Counter +from functools import total_ordering + +from data_structures.heap.heap import Heap + + +@total_ordering +class WordCount: + def __init__(self, word: str, count: int) -> None: + self.word = word + self.count = count + + def __eq__(self, other: object) -> bool: + """ + >>> WordCount('a', 1).__eq__(WordCount('b', 1)) + True + >>> WordCount('a', 1).__eq__(WordCount('a', 1)) + True + >>> WordCount('a', 1).__eq__(WordCount('a', 2)) + False + >>> WordCount('a', 1).__eq__(WordCount('b', 2)) + False + >>> WordCount('a', 1).__eq__(1) + NotImplemented + """ + if not isinstance(other, WordCount): + return NotImplemented + return self.count == other.count + + def __lt__(self, other: object) -> bool: + """ + >>> WordCount('a', 1).__lt__(WordCount('b', 1)) + False + >>> WordCount('a', 1).__lt__(WordCount('a', 1)) + False + >>> WordCount('a', 1).__lt__(WordCount('a', 2)) + True + >>> WordCount('a', 1).__lt__(WordCount('b', 2)) + True + >>> WordCount('a', 2).__lt__(WordCount('a', 1)) + False + >>> WordCount('a', 2).__lt__(WordCount('b', 1)) + False + >>> WordCount('a', 1).__lt__(1) + NotImplemented + """ + if not isinstance(other, WordCount): + return NotImplemented + return self.count < other.count + + +def top_k_frequent_words(words: list[str], k_value: int) -> list[str]: + """ + Returns the `k_value` most frequently occurring words, + in non-increasing order of occurrence. + In this context, a word is defined as an element in the provided list. + + In case `k_value` is greater than the number of distinct words, a value of k equal + to the number of distinct words will be considered, instead. + + >>> top_k_frequent_words(['a', 'b', 'c', 'a', 'c', 'c'], 3) + ['c', 'a', 'b'] + >>> top_k_frequent_words(['a', 'b', 'c', 'a', 'c', 'c'], 2) + ['c', 'a'] + >>> top_k_frequent_words(['a', 'b', 'c', 'a', 'c', 'c'], 1) + ['c'] + >>> top_k_frequent_words(['a', 'b', 'c', 'a', 'c', 'c'], 0) + [] + >>> top_k_frequent_words([], 1) + [] + >>> top_k_frequent_words(['a', 'a'], 2) + ['a'] + """ + heap: Heap[WordCount] = Heap() + count_by_word = Counter(words) + heap.build_max_heap( + [WordCount(word, count) for word, count in count_by_word.items()] + ) + return [heap.extract_max().word for _ in range(min(k_value, len(count_by_word)))] + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From c4dcc44dd44f7e3e7c65debc8e173080fc693150 Mon Sep 17 00:00:00 2001 From: Sahil Goel <55365655+sahilg13@users.noreply.github.com> Date: Sun, 30 Apr 2023 13:33:22 -0400 Subject: [PATCH 056/757] Added an algorithm to calculate the present value of cash flows (#8700) * Added an algorithm to calculate the present value of cash flows * added doctest and reference * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Resolving deprecation issues with typing module * Fixing argument type checks and adding doctest case * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fixing failing doctest case by requiring less precision due to floating point inprecision * Updating return type * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Added test cases for more coverage * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Make improvements based on Rohan's suggestions * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update financial/present_value.py Committed first suggestion Co-authored-by: Christian Clauss * Update financial/present_value.py Committed second suggestion Co-authored-by: Christian Clauss * Update financial/present_value.py Committed third suggestion Co-authored-by: Christian Clauss * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- financial/present_value.py | 41 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 financial/present_value.py diff --git a/financial/present_value.py b/financial/present_value.py new file mode 100644 index 000000000000..dc8191a6ef53 --- /dev/null +++ b/financial/present_value.py @@ -0,0 +1,41 @@ +""" +Reference: https://www.investopedia.com/terms/p/presentvalue.asp + +An algorithm that calculates the present value of a stream of yearly cash flows given... +1. The discount rate (as a decimal, not a percent) +2. An array of cash flows, with the index of the cash flow being the associated year + +Note: This algorithm assumes that cash flows are paid at the end of the specified year + + +def present_value(discount_rate: float, cash_flows: list[float]) -> float: + """ + >>> present_value(0.13, [10, 20.70, -293, 297]) + 4.69 + >>> present_value(0.07, [-109129.39, 30923.23, 15098.93, 29734,39]) + -42739.63 + >>> present_value(0.07, [109129.39, 30923.23, 15098.93, 29734,39]) + 175519.15 + >>> present_value(-1, [109129.39, 30923.23, 15098.93, 29734,39]) + Traceback (most recent call last): + ... + ValueError: Discount rate cannot be negative + >>> present_value(0.03, []) + Traceback (most recent call last): + ... + ValueError: Cash flows list cannot be empty + """ + if discount_rate < 0: + raise ValueError("Discount rate cannot be negative") + if not cash_flows: + raise ValueError("Cash flows list cannot be empty") + present_value = sum( + cash_flow / ((1 + discount_rate) ** i) for i, cash_flow in enumerate(cash_flows) + ) + return round(present_value, ndigits=2) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From f6df26bf0f5c05d53b6fd24552de9e3eec2334aa Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Mon, 1 May 2023 02:59:42 +0200 Subject: [PATCH 057/757] Fix docstring in present_value.py (#8702) Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 2 ++ financial/present_value.py | 1 + 2 files changed, 3 insertions(+) diff --git a/DIRECTORY.md b/DIRECTORY.md index 681d252b232d..167d062b4a9f 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -363,6 +363,7 @@ ## Financial * [Equated Monthly Installments](financial/equated_monthly_installments.py) * [Interest](financial/interest.py) + * [Present Value](financial/present_value.py) * [Price Plus Tax](financial/price_plus_tax.py) ## Fractals @@ -655,6 +656,7 @@ * [Sum Of Harmonic Series](maths/sum_of_harmonic_series.py) * [Sumset](maths/sumset.py) * [Sylvester Sequence](maths/sylvester_sequence.py) + * [Tanh](maths/tanh.py) * [Test Prime Check](maths/test_prime_check.py) * [Trapezoidal Rule](maths/trapezoidal_rule.py) * [Triplet Sum](maths/triplet_sum.py) diff --git a/financial/present_value.py b/financial/present_value.py index dc8191a6ef53..f74612b923af 100644 --- a/financial/present_value.py +++ b/financial/present_value.py @@ -6,6 +6,7 @@ 2. An array of cash flows, with the index of the cash flow being the associated year Note: This algorithm assumes that cash flows are paid at the end of the specified year +""" def present_value(discount_rate: float, cash_flows: list[float]) -> float: From e966c5cc0f856afab11a8bb150ef3b48f0c63112 Mon Sep 17 00:00:00 2001 From: Himanshu Tomar Date: Mon, 1 May 2023 15:53:03 +0530 Subject: [PATCH 058/757] Added minimum waiting time problem solution using greedy algorithm (#8701) * Added minimum waiting time problem solution using greedy algorithm * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * ruff --fix * Add type hints * Added two more doc test * Removed unnecessary comments * updated type hints * Updated the code as per the code review --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- DIRECTORY.md | 1 + greedy_methods/minimum_waiting_time.py | 48 ++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 greedy_methods/minimum_waiting_time.py diff --git a/DIRECTORY.md b/DIRECTORY.md index 167d062b4a9f..021669d13b4a 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -450,6 +450,7 @@ * [Fractional Knapsack](greedy_methods/fractional_knapsack.py) * [Fractional Knapsack 2](greedy_methods/fractional_knapsack_2.py) * [Optimal Merge Pattern](greedy_methods/optimal_merge_pattern.py) + * [Minimum Waiting Time ](greedy_methods/minimum_waiting_time.py) ## Hashes * [Adler32](hashes/adler32.py) diff --git a/greedy_methods/minimum_waiting_time.py b/greedy_methods/minimum_waiting_time.py new file mode 100644 index 000000000000..aaae8cf8f720 --- /dev/null +++ b/greedy_methods/minimum_waiting_time.py @@ -0,0 +1,48 @@ +""" +Calculate the minimum waiting time using a greedy algorithm. +reference: https://www.youtube.com/watch?v=Sf3eiO12eJs + +For doctests run following command: +python -m doctest -v minimum_waiting_time.py + +The minimum_waiting_time function uses a greedy algorithm to calculate the minimum +time for queries to complete. It sorts the list in non-decreasing order, calculates +the waiting time for each query by multiplying its position in the list with the +sum of all remaining query times, and returns the total waiting time. A doctest +ensures that the function produces the correct output. +""" + + +def minimum_waiting_time(queries: list[int]) -> int: + """ + This function takes a list of query times and returns the minimum waiting time + for all queries to be completed. + + Args: + queries: A list of queries measured in picoseconds + + Returns: + total_waiting_time: Minimum waiting time measured in picoseconds + + Examples: + >>> minimum_waiting_time([3, 2, 1, 2, 6]) + 17 + >>> minimum_waiting_time([3, 2, 1]) + 4 + >>> minimum_waiting_time([1, 2, 3, 4]) + 10 + >>> minimum_waiting_time([5, 5, 5, 5]) + 30 + >>> minimum_waiting_time([]) + 0 + """ + n = len(queries) + if n in (0, 1): + return 0 + return sum(query * (n - i - 1) for i, query in enumerate(sorted(queries))) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 777f966893d7042d350b44b05ce7f8431f561509 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 1 May 2023 23:48:56 +0200 Subject: [PATCH 059/757] [pre-commit.ci] pre-commit autoupdate (#8704) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/charliermarsh/ruff-pre-commit: v0.0.262 → v0.0.263](https://github.com/charliermarsh/ruff-pre-commit/compare/v0.0.262...v0.0.263) - [github.com/tox-dev/pyproject-fmt: 0.10.0 → 0.11.1](https://github.com/tox-dev/pyproject-fmt/compare/0.10.0...0.11.1) * updating DIRECTORY.md --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- .pre-commit-config.yaml | 4 ++-- DIRECTORY.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 288473ca365f..accb57da35d3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/charliermarsh/ruff-pre-commit - rev: v0.0.262 + rev: v0.0.263 hooks: - id: ruff @@ -33,7 +33,7 @@ repos: - tomli - repo: https://github.com/tox-dev/pyproject-fmt - rev: "0.10.0" + rev: "0.11.1" hooks: - id: pyproject-fmt diff --git a/DIRECTORY.md b/DIRECTORY.md index 021669d13b4a..826bd6fd39d4 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -449,8 +449,8 @@ ## Greedy Methods * [Fractional Knapsack](greedy_methods/fractional_knapsack.py) * [Fractional Knapsack 2](greedy_methods/fractional_knapsack_2.py) + * [Minimum Waiting Time](greedy_methods/minimum_waiting_time.py) * [Optimal Merge Pattern](greedy_methods/optimal_merge_pattern.py) - * [Minimum Waiting Time ](greedy_methods/minimum_waiting_time.py) ## Hashes * [Adler32](hashes/adler32.py) From 73105145090f0ce972f6fa29cc5d71f012dd8c92 Mon Sep 17 00:00:00 2001 From: Dipankar Mitra <50228537+Mitra-babu@users.noreply.github.com> Date: Tue, 2 May 2023 20:06:28 +0530 Subject: [PATCH 060/757] The ELU activation is added (#8699) * tanh function been added * tanh function been added * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * tanh function is added * tanh function is added * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * tanh function added * tanh function added * tanh function is added * Apply suggestions from code review * ELU activation function is added * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * elu activation is added * ELU activation is added * Update maths/elu_activation.py Co-authored-by: Christian Clauss * Exponential_linear_unit activation is added * Exponential_linear_unit activation is added --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- .../exponential_linear_unit.py | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 neural_network/activation_functions/exponential_linear_unit.py diff --git a/neural_network/activation_functions/exponential_linear_unit.py b/neural_network/activation_functions/exponential_linear_unit.py new file mode 100644 index 000000000000..7a3cf1d84e71 --- /dev/null +++ b/neural_network/activation_functions/exponential_linear_unit.py @@ -0,0 +1,40 @@ +""" +Implements the Exponential Linear Unit or ELU function. + +The function takes a vector of K real numbers and a real number alpha as +input and then applies the ELU function to each element of the vector. + +Script inspired from its corresponding Wikipedia article +https://en.wikipedia.org/wiki/Rectifier_(neural_networks) +""" + +import numpy as np + + +def exponential_linear_unit(vector: np.ndarray, alpha: float) -> np.ndarray: + """ + Implements the ELU activation function. + Parameters: + vector: the array containing input of elu activation + alpha: hyper-parameter + return: + elu (np.array): The input numpy array after applying elu. + + Mathematically, f(x) = x, x>0 else (alpha * (e^x -1)), x<=0, alpha >=0 + + Examples: + >>> exponential_linear_unit(vector=np.array([2.3,0.6,-2,-3.8]), alpha=0.3) + array([ 2.3 , 0.6 , -0.25939942, -0.29328877]) + + >>> exponential_linear_unit(vector=np.array([-9.2,-0.3,0.45,-4.56]), alpha=0.067) + array([-0.06699323, -0.01736518, 0.45 , -0.06629904]) + + + """ + return np.where(vector > 0, vector, (alpha * (np.exp(vector) - 1))) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 91cc3a240f05922024d4c5523422138857c48ae0 Mon Sep 17 00:00:00 2001 From: Pronoy Mandal Date: Wed, 10 May 2023 15:04:36 +0530 Subject: [PATCH 061/757] Update game_of_life.py (#8703) Rectify spelling in docstring --- cellular_automata/game_of_life.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cellular_automata/game_of_life.py b/cellular_automata/game_of_life.py index 8e54702519b9..3382af7b5db6 100644 --- a/cellular_automata/game_of_life.py +++ b/cellular_automata/game_of_life.py @@ -34,7 +34,7 @@ from matplotlib import pyplot as plt from matplotlib.colors import ListedColormap -usage_doc = "Usage of script: script_nama " +usage_doc = "Usage of script: script_name " choice = [0] * 100 + [1] * 10 random.shuffle(choice) From 209a59ee562dd4b0358d8d1a12b112ec3f3e68ed Mon Sep 17 00:00:00 2001 From: Rohan Anand <96521078+rohan472000@users.noreply.github.com> Date: Wed, 10 May 2023 15:08:52 +0530 Subject: [PATCH 062/757] Update and_gate.py (#8690) * Update and_gate.py addressing issue #8656 by calling `test_and_gate()` , ensuring that all the assertions are verified before the actual output is printed. * Update and_gate.py addressing issue #8632 --- boolean_algebra/and_gate.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/boolean_algebra/and_gate.py b/boolean_algebra/and_gate.py index cbbcfde79f33..834116772ee7 100644 --- a/boolean_algebra/and_gate.py +++ b/boolean_algebra/and_gate.py @@ -43,6 +43,8 @@ def test_and_gate() -> None: if __name__ == "__main__": + test_and_gate() + print(and_gate(1, 0)) print(and_gate(0, 0)) print(and_gate(0, 1)) print(and_gate(1, 1)) From 44aa17fb86b0c04508580425b588c0f8a0cf4ce9 Mon Sep 17 00:00:00 2001 From: shricubed Date: Wed, 10 May 2023 14:50:32 -0400 Subject: [PATCH 063/757] Working binary insertion sort in Python (#8024) --- sorts/binary_insertion_sort.py | 61 ++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 sorts/binary_insertion_sort.py diff --git a/sorts/binary_insertion_sort.py b/sorts/binary_insertion_sort.py new file mode 100644 index 000000000000..8d41025583b1 --- /dev/null +++ b/sorts/binary_insertion_sort.py @@ -0,0 +1,61 @@ +""" +This is a pure Python implementation of the binary insertion sort algorithm + +For doctests run following command: +python -m doctest -v binary_insertion_sort.py +or +python3 -m doctest -v binary_insertion_sort.py + +For manual testing run: +python binary_insertion_sort.py +""" + + +def binary_insertion_sort(collection: list) -> list: + """Pure implementation of the binary insertion sort algorithm in Python + :param collection: some mutable ordered collection with heterogeneous + comparable items inside + :return: the same collection ordered by ascending + + Examples: + >>> binary_insertion_sort([0, 4, 1234, 4, 1]) + [0, 1, 4, 4, 1234] + >>> binary_insertion_sort([]) == sorted([]) + True + >>> binary_insertion_sort([-1, -2, -3]) == sorted([-1, -2, -3]) + True + >>> lst = ['d', 'a', 'b', 'e', 'c'] + >>> binary_insertion_sort(lst) == sorted(lst) + True + >>> import random + >>> collection = random.sample(range(-50, 50), 100) + >>> binary_insertion_sort(collection) == sorted(collection) + True + >>> import string + >>> collection = random.choices(string.ascii_letters + string.digits, k=100) + >>> binary_insertion_sort(collection) == sorted(collection) + True + """ + + n = len(collection) + for i in range(1, n): + val = collection[i] + low = 0 + high = i - 1 + + while low <= high: + mid = (low + high) // 2 + if val < collection[mid]: + high = mid - 1 + else: + low = mid + 1 + for j in range(i, low, -1): + collection[j] = collection[j - 1] + collection[low] = val + return collection + + +if __name__ == "__main__": + user_input = input("Enter numbers separated by a comma:\n").strip() + unsorted = [int(item) for item in user_input.split(",")] + print(binary_insertion_sort(unsorted)) From 997d56fb633e3bd726c1fac32a2d37277361d5e9 Mon Sep 17 00:00:00 2001 From: Margaret <62753112+meg-1@users.noreply.github.com> Date: Wed, 10 May 2023 21:53:47 +0300 Subject: [PATCH 064/757] Switch case (#7995) --- strings/string_switch_case.py | 108 ++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 strings/string_switch_case.py diff --git a/strings/string_switch_case.py b/strings/string_switch_case.py new file mode 100644 index 000000000000..9a07472dfd71 --- /dev/null +++ b/strings/string_switch_case.py @@ -0,0 +1,108 @@ +import re + +""" +general info: +https://en.wikipedia.org/wiki/Naming_convention_(programming)#Python_and_Ruby + +pascal case [ an upper Camel Case ]: https://en.wikipedia.org/wiki/Camel_case + +camel case: https://en.wikipedia.org/wiki/Camel_case + +kebab case [ can be found in general info ]: +https://en.wikipedia.org/wiki/Naming_convention_(programming)#Python_and_Ruby + +snake case: https://en.wikipedia.org/wiki/Snake_case +""" + + +# assistant functions +def split_input(str_: str) -> list: + """ + >>> split_input("one two 31235three4four") + [['one', 'two', '31235three4four']] + """ + return [char.split() for char in re.split(r"[^ a-z A-Z 0-9 \s]", str_)] + + +def to_simple_case(str_: str) -> str: + """ + >>> to_simple_case("one two 31235three4four") + 'OneTwo31235three4four' + """ + string_split = split_input(str_) + return "".join( + ["".join([char.capitalize() for char in sub_str]) for sub_str in string_split] + ) + + +def to_complex_case(text: str, upper: bool, separator: str) -> str: + """ + >>> to_complex_case("one two 31235three4four", True, "_") + 'ONE_TWO_31235THREE4FOUR' + >>> to_complex_case("one two 31235three4four", False, "-") + 'one-two-31235three4four' + """ + try: + string_split = split_input(text) + if upper: + res_str = "".join( + [ + separator.join([char.upper() for char in sub_str]) + for sub_str in string_split + ] + ) + else: + res_str = "".join( + [ + separator.join([char.lower() for char in sub_str]) + for sub_str in string_split + ] + ) + return res_str + except IndexError: + return "not valid string" + + +# main content +def to_pascal_case(text: str) -> str: + """ + >>> to_pascal_case("one two 31235three4four") + 'OneTwo31235three4four' + """ + return to_simple_case(text) + + +def to_camel_case(text: str) -> str: + """ + >>> to_camel_case("one two 31235three4four") + 'oneTwo31235three4four' + """ + try: + res_str = to_simple_case(text) + return res_str[0].lower() + res_str[1:] + except IndexError: + return "not valid string" + + +def to_snake_case(text: str, upper: bool) -> str: + """ + >>> to_snake_case("one two 31235three4four", True) + 'ONE_TWO_31235THREE4FOUR' + >>> to_snake_case("one two 31235three4four", False) + 'one_two_31235three4four' + """ + return to_complex_case(text, upper, "_") + + +def to_kebab_case(text: str, upper: bool) -> str: + """ + >>> to_kebab_case("one two 31235three4four", True) + 'ONE-TWO-31235THREE4FOUR' + >>> to_kebab_case("one two 31235three4four", False) + 'one-two-31235three4four' + """ + return to_complex_case(text, upper, "-") + + +if __name__ == "__main__": + __import__("doctest").testmod() From 6939538a41202bf05f958c9c2d7c1c20e2f87430 Mon Sep 17 00:00:00 2001 From: Margaret <62753112+meg-1@users.noreply.github.com> Date: Wed, 10 May 2023 21:55:48 +0300 Subject: [PATCH 065/757] adding the remove digit algorithm (#6708) --- maths/remove_digit.py | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 maths/remove_digit.py diff --git a/maths/remove_digit.py b/maths/remove_digit.py new file mode 100644 index 000000000000..db14ac902a6f --- /dev/null +++ b/maths/remove_digit.py @@ -0,0 +1,37 @@ +def remove_digit(num: int) -> int: + """ + + returns the biggest possible result + that can be achieved by removing + one digit from the given number + + >>> remove_digit(152) + 52 + >>> remove_digit(6385) + 685 + >>> remove_digit(-11) + 1 + >>> remove_digit(2222222) + 222222 + >>> remove_digit("2222222") + Traceback (most recent call last): + TypeError: only integers accepted as input + >>> remove_digit("string input") + Traceback (most recent call last): + TypeError: only integers accepted as input + """ + + if not isinstance(num, int): + raise TypeError("only integers accepted as input") + else: + num_str = str(abs(num)) + num_transpositions = [list(num_str) for char in range(len(num_str))] + for index in range(len(num_str)): + num_transpositions[index].pop(index) + return max( + int("".join(list(transposition))) for transposition in num_transpositions + ) + + +if __name__ == "__main__": + __import__("doctest").testmod() From 793e564e1d4bd6e00b6e2f80869c5fd1fd2872b3 Mon Sep 17 00:00:00 2001 From: Pronoy Mandal Date: Thu, 11 May 2023 00:30:59 +0530 Subject: [PATCH 066/757] Create maximum_subsequence.py (#7811) --- DIRECTORY.md | 1 + other/maximum_subsequence.py | 42 ++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 other/maximum_subsequence.py diff --git a/DIRECTORY.md b/DIRECTORY.md index 826bd6fd39d4..a70ad6861d6f 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -716,6 +716,7 @@ * [Lru Cache](other/lru_cache.py) * [Magicdiamondpattern](other/magicdiamondpattern.py) * [Maximum Subarray](other/maximum_subarray.py) + * [Maximum Subsequence](other/maximum_subsequence.py) * [Nested Brackets](other/nested_brackets.py) * [Password](other/password.py) * [Quine](other/quine.py) diff --git a/other/maximum_subsequence.py b/other/maximum_subsequence.py new file mode 100644 index 000000000000..f81717596532 --- /dev/null +++ b/other/maximum_subsequence.py @@ -0,0 +1,42 @@ +from collections.abc import Sequence + + +def max_subsequence_sum(nums: Sequence[int] | None = None) -> int: + """Return the maximum possible sum amongst all non - empty subsequences. + + Raises: + ValueError: when nums is empty. + + >>> max_subsequence_sum([1,2,3,4,-2]) + 10 + >>> max_subsequence_sum([-2, -3, -1, -4, -6]) + -1 + >>> max_subsequence_sum([]) + Traceback (most recent call last): + . . . + ValueError: Input sequence should not be empty + >>> max_subsequence_sum() + Traceback (most recent call last): + . . . + ValueError: Input sequence should not be empty + """ + if nums is None or not nums: + raise ValueError("Input sequence should not be empty") + + ans = nums[0] + for i in range(1, len(nums)): + num = nums[i] + ans = max(ans, ans + num, num) + + return ans + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + + # Try on a sample input from the user + n = int(input("Enter number of elements : ").strip()) + array = list(map(int, input("\nEnter the numbers : ").strip().split()))[:n] + print(max_subsequence_sum(array)) From 1faf10b5c2dff8cef3f5d59f60a126bd19bb1c44 Mon Sep 17 00:00:00 2001 From: Caeden Perelli-Harris Date: Sun, 14 May 2023 22:03:13 +0100 Subject: [PATCH 067/757] Correct ruff failures (#8732) * fix: Correct ruff problems * updating DIRECTORY.md * fix: Fix pre-commit errors * updating DIRECTORY.md --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 6 +++++- conversions/prefix_conversions_string.py | 4 ++-- conversions/rgb_hsv_conversion.py | 4 ++-- .../test_digital_image_processing.py | 2 +- ...ion.py => strassen_matrix_multiplication.py.BROKEN} | 2 +- dynamic_programming/fibonacci.py | 2 +- maths/euclidean_distance.py | 6 +++--- physics/horizontal_projectile_motion.py | 6 +++--- searches/binary_tree_traversal.py | 10 ++++------ 9 files changed, 22 insertions(+), 20 deletions(-) rename divide_and_conquer/{strassen_matrix_multiplication.py => strassen_matrix_multiplication.py.BROKEN} (99%) diff --git a/DIRECTORY.md b/DIRECTORY.md index a70ad6861d6f..fc6cbaf7ff41 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -294,7 +294,6 @@ * [Mergesort](divide_and_conquer/mergesort.py) * [Peak](divide_and_conquer/peak.py) * [Power](divide_and_conquer/power.py) - * [Strassen Matrix Multiplication](divide_and_conquer/strassen_matrix_multiplication.py) ## Dynamic Programming * [Abbreviation](dynamic_programming/abbreviation.py) @@ -632,6 +631,7 @@ * [Radians](maths/radians.py) * [Radix2 Fft](maths/radix2_fft.py) * [Relu](maths/relu.py) + * [Remove Digit](maths/remove_digit.py) * [Runge Kutta](maths/runge_kutta.py) * [Segmented Sieve](maths/segmented_sieve.py) * Series @@ -694,6 +694,8 @@ ## Neural Network * [2 Hidden Layers Neural Network](neural_network/2_hidden_layers_neural_network.py) + * Activation Functions + * [Exponential Linear Unit](neural_network/activation_functions/exponential_linear_unit.py) * [Back Propagation Neural Network](neural_network/back_propagation_neural_network.py) * [Convolution Neural Network](neural_network/convolution_neural_network.py) * [Input Data](neural_network/input_data.py) @@ -1080,6 +1082,7 @@ ## Sorts * [Bead Sort](sorts/bead_sort.py) + * [Binary Insertion Sort](sorts/binary_insertion_sort.py) * [Bitonic Sort](sorts/bitonic_sort.py) * [Bogo Sort](sorts/bogo_sort.py) * [Bubble Sort](sorts/bubble_sort.py) @@ -1170,6 +1173,7 @@ * [Reverse Words](strings/reverse_words.py) * [Snake Case To Camel Pascal Case](strings/snake_case_to_camel_pascal_case.py) * [Split](strings/split.py) + * [String Switch Case](strings/string_switch_case.py) * [Text Justification](strings/text_justification.py) * [Top K Frequent Words](strings/top_k_frequent_words.py) * [Upper](strings/upper.py) diff --git a/conversions/prefix_conversions_string.py b/conversions/prefix_conversions_string.py index 3851d7c8b993..9344c9672a1f 100644 --- a/conversions/prefix_conversions_string.py +++ b/conversions/prefix_conversions_string.py @@ -96,7 +96,7 @@ def add_si_prefix(value: float) -> str: for name_prefix, value_prefix in prefixes.items(): numerical_part = value / (10**value_prefix) if numerical_part > 1: - return f"{str(numerical_part)} {name_prefix}" + return f"{numerical_part!s} {name_prefix}" return str(value) @@ -111,7 +111,7 @@ def add_binary_prefix(value: float) -> str: for prefix in BinaryUnit: numerical_part = value / (2**prefix.value) if numerical_part > 1: - return f"{str(numerical_part)} {prefix.name}" + return f"{numerical_part!s} {prefix.name}" return str(value) diff --git a/conversions/rgb_hsv_conversion.py b/conversions/rgb_hsv_conversion.py index 081cfe1d75e0..74b3d33e49e7 100644 --- a/conversions/rgb_hsv_conversion.py +++ b/conversions/rgb_hsv_conversion.py @@ -121,8 +121,8 @@ def rgb_to_hsv(red: int, green: int, blue: int) -> list[float]: float_red = red / 255 float_green = green / 255 float_blue = blue / 255 - value = max(max(float_red, float_green), float_blue) - chroma = value - min(min(float_red, float_green), float_blue) + value = max(float_red, float_green, float_blue) + chroma = value - min(float_red, float_green, float_blue) saturation = 0 if value == 0 else chroma / value if chroma == 0: diff --git a/digital_image_processing/test_digital_image_processing.py b/digital_image_processing/test_digital_image_processing.py index c999464ce85e..fee7ab247b55 100644 --- a/digital_image_processing/test_digital_image_processing.py +++ b/digital_image_processing/test_digital_image_processing.py @@ -96,7 +96,7 @@ def test_nearest_neighbour( def test_local_binary_pattern(): - file_path: str = "digital_image_processing/image_data/lena.jpg" + file_path = "digital_image_processing/image_data/lena.jpg" # Reading the image and converting it to grayscale. image = imread(file_path, 0) diff --git a/divide_and_conquer/strassen_matrix_multiplication.py b/divide_and_conquer/strassen_matrix_multiplication.py.BROKEN similarity index 99% rename from divide_and_conquer/strassen_matrix_multiplication.py rename to divide_and_conquer/strassen_matrix_multiplication.py.BROKEN index 371605d6d4d4..2ca91c63bf4c 100644 --- a/divide_and_conquer/strassen_matrix_multiplication.py +++ b/divide_and_conquer/strassen_matrix_multiplication.py.BROKEN @@ -122,7 +122,7 @@ def strassen(matrix1: list, matrix2: list) -> list: if dimension1[0] == dimension1[1] and dimension2[0] == dimension2[1]: return [matrix1, matrix2] - maximum = max(max(dimension1), max(dimension2)) + maximum = max(dimension1, dimension2) maxim = int(math.pow(2, math.ceil(math.log2(maximum)))) new_matrix1 = matrix1 new_matrix2 = matrix2 diff --git a/dynamic_programming/fibonacci.py b/dynamic_programming/fibonacci.py index 7ec5993ef38d..c102493aa00b 100644 --- a/dynamic_programming/fibonacci.py +++ b/dynamic_programming/fibonacci.py @@ -24,7 +24,7 @@ def get(self, index: int) -> list: return self.sequence[:index] -def main(): +def main() -> None: print( "Fibonacci Series Using Dynamic Programming\n", "Enter the index of the Fibonacci number you want to calculate ", diff --git a/maths/euclidean_distance.py b/maths/euclidean_distance.py index 22012e92c9cf..9b29b37b0ce6 100644 --- a/maths/euclidean_distance.py +++ b/maths/euclidean_distance.py @@ -1,12 +1,12 @@ from __future__ import annotations +import typing from collections.abc import Iterable -from typing import Union import numpy as np -Vector = Union[Iterable[float], Iterable[int], np.ndarray] -VectorOut = Union[np.float64, int, float] +Vector = typing.Union[Iterable[float], Iterable[int], np.ndarray] # noqa: UP007 +VectorOut = typing.Union[np.float64, int, float] # noqa: UP007 def euclidean_distance(vector_1: Vector, vector_2: Vector) -> VectorOut: diff --git a/physics/horizontal_projectile_motion.py b/physics/horizontal_projectile_motion.py index dbde3660f62f..80f85a1b7146 100644 --- a/physics/horizontal_projectile_motion.py +++ b/physics/horizontal_projectile_motion.py @@ -147,6 +147,6 @@ def test_motion() -> None: # Print results print() print("Results: ") - print(f"Horizontal Distance: {str(horizontal_distance(init_vel, angle))} [m]") - print(f"Maximum Height: {str(max_height(init_vel, angle))} [m]") - print(f"Total Time: {str(total_time(init_vel, angle))} [s]") + print(f"Horizontal Distance: {horizontal_distance(init_vel, angle)!s} [m]") + print(f"Maximum Height: {max_height(init_vel, angle)!s} [m]") + print(f"Total Time: {total_time(init_vel, angle)!s} [s]") diff --git a/searches/binary_tree_traversal.py b/searches/binary_tree_traversal.py index 76e80df25a13..6fb841af4294 100644 --- a/searches/binary_tree_traversal.py +++ b/searches/binary_tree_traversal.py @@ -13,11 +13,9 @@ def __init__(self, data): self.left = None -def build_tree(): +def build_tree() -> TreeNode: print("\n********Press N to stop entering at any point of time********\n") - check = input("Enter the value of the root node: ").strip().lower() or "n" - if check == "n": - return None + check = input("Enter the value of the root node: ").strip().lower() q: queue.Queue = queue.Queue() tree_node = TreeNode(int(check)) q.put(tree_node) @@ -37,7 +35,7 @@ def build_tree(): right_node = TreeNode(int(check)) node_found.right = right_node q.put(right_node) - return None + raise def pre_order(node: TreeNode) -> None: @@ -272,7 +270,7 @@ def prompt(s: str = "", width=50, char="*") -> str: doctest.testmod() print(prompt("Binary Tree Traversals")) - node = build_tree() + node: TreeNode = build_tree() print(prompt("Pre Order Traversal")) pre_order(node) print(prompt() + "\n") From 2a57dafce096b51b4b28d1495116e79472c8a3f4 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 15 May 2023 22:27:59 +0100 Subject: [PATCH 068/757] [pre-commit.ci] pre-commit autoupdate (#8716) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/charliermarsh/ruff-pre-commit: v0.0.263 → v0.0.267](https://github.com/charliermarsh/ruff-pre-commit/compare/v0.0.263...v0.0.267) - [github.com/tox-dev/pyproject-fmt: 0.11.1 → 0.11.2](https://github.com/tox-dev/pyproject-fmt/compare/0.11.1...0.11.2) - [github.com/pre-commit/mirrors-mypy: v1.2.0 → v1.3.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.2.0...v1.3.0) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index accb57da35d3..6bdbc7370c9c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/charliermarsh/ruff-pre-commit - rev: v0.0.263 + rev: v0.0.267 hooks: - id: ruff @@ -33,7 +33,7 @@ repos: - tomli - repo: https://github.com/tox-dev/pyproject-fmt - rev: "0.11.1" + rev: "0.11.2" hooks: - id: pyproject-fmt @@ -51,7 +51,7 @@ repos: - id: validate-pyproject - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.2.0 + rev: v1.3.0 hooks: - id: mypy args: From c0892a06515b8ea5030db2e8344dee2292bb10ad Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Tue, 16 May 2023 00:47:50 +0300 Subject: [PATCH 069/757] Reduce the complexity of genetic_algorithm/basic_string.py (#8606) --- genetic_algorithm/basic_string.py | 95 ++++++++++++++++--------------- 1 file changed, 50 insertions(+), 45 deletions(-) diff --git a/genetic_algorithm/basic_string.py b/genetic_algorithm/basic_string.py index 45b8be651f6e..388e7219f54b 100644 --- a/genetic_algorithm/basic_string.py +++ b/genetic_algorithm/basic_string.py @@ -21,6 +21,54 @@ random.seed(random.randint(0, 1000)) +def evaluate(item: str, main_target: str) -> tuple[str, float]: + """ + Evaluate how similar the item is with the target by just + counting each char in the right position + >>> evaluate("Helxo Worlx", "Hello World") + ('Helxo Worlx', 9.0) + """ + score = len([g for position, g in enumerate(item) if g == main_target[position]]) + return (item, float(score)) + + +def crossover(parent_1: str, parent_2: str) -> tuple[str, str]: + """Slice and combine two string at a random point.""" + random_slice = random.randint(0, len(parent_1) - 1) + child_1 = parent_1[:random_slice] + parent_2[random_slice:] + child_2 = parent_2[:random_slice] + parent_1[random_slice:] + return (child_1, child_2) + + +def mutate(child: str, genes: list[str]) -> str: + """Mutate a random gene of a child with another one from the list.""" + child_list = list(child) + if random.uniform(0, 1) < MUTATION_PROBABILITY: + child_list[random.randint(0, len(child)) - 1] = random.choice(genes) + return "".join(child_list) + + +# Select, crossover and mutate a new population. +def select( + parent_1: tuple[str, float], + population_score: list[tuple[str, float]], + genes: list[str], +) -> list[str]: + """Select the second parent and generate new population""" + pop = [] + # Generate more children proportionally to the fitness score. + child_n = int(parent_1[1] * 100) + 1 + child_n = 10 if child_n >= 10 else child_n + for _ in range(child_n): + parent_2 = population_score[random.randint(0, N_SELECTED)][0] + + child_1, child_2 = crossover(parent_1[0], parent_2) + # Append new string to the population list. + pop.append(mutate(child_1, genes)) + pop.append(mutate(child_2, genes)) + return pop + + 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. @@ -70,17 +118,6 @@ 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]: - """ - Evaluate how similar the item is with the target by just - counting each char in the right position - >>> evaluate("Helxo Worlx", Hello World) - ["Helxo Worlx", 9] - """ - score = len( - [g for position, g in enumerate(item) if g == main_target[position]] - ) - return (item, float(score)) # Adding a bit of concurrency can make everything faster, # @@ -94,7 +131,7 @@ def evaluate(item: str, main_target: str = target) -> tuple[str, float]: # # but with a simple algorithm like this, it will probably be slower. # We just need to call evaluate for every item inside the population. - population_score = [evaluate(item) for item in population] + population_score = [evaluate(item, target) for item in population] # Check if there is a matching evolution. population_score = sorted(population_score, key=lambda x: x[1], reverse=True) @@ -121,41 +158,9 @@ def evaluate(item: str, main_target: str = target) -> tuple[str, float]: (item, score / len(target)) for item, score in population_score ] - # Select, crossover and mutate a new population. - def select(parent_1: tuple[str, float]) -> list[str]: - """Select the second parent and generate new population""" - pop = [] - # Generate more children proportionally to the fitness score. - child_n = int(parent_1[1] * 100) + 1 - child_n = 10 if child_n >= 10 else child_n - for _ in range(child_n): - parent_2 = population_score[ # noqa: B023 - random.randint(0, N_SELECTED) - ][0] - - child_1, child_2 = crossover(parent_1[0], parent_2) - # Append new string to the population list. - pop.append(mutate(child_1)) - pop.append(mutate(child_2)) - return pop - - def crossover(parent_1: str, parent_2: str) -> tuple[str, str]: - """Slice and combine two string at a random point.""" - random_slice = random.randint(0, len(parent_1) - 1) - child_1 = parent_1[:random_slice] + parent_2[random_slice:] - child_2 = parent_2[:random_slice] + parent_1[random_slice:] - return (child_1, child_2) - - def mutate(child: str) -> str: - """Mutate a random gene of a child with another one from the list.""" - child_list = list(child) - if random.uniform(0, 1) < MUTATION_PROBABILITY: - child_list[random.randint(0, len(child)) - 1] = random.choice(genes) - return "".join(child_list) - # This is selection for i in range(N_SELECTED): - population.extend(select(population_score[int(i)])) + population.extend(select(population_score[int(i)], population_score, genes)) # Check if the population has already reached the maximum value and if so, # break the cycle. If this check is disabled, the algorithm will take # forever to compute large strings, but will also calculate small strings in From 8102424950f2d3801eda7817d7f69288fd984a63 Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Tue, 16 May 2023 17:05:55 -0700 Subject: [PATCH 070/757] `local_weighted_learning.py`: fix `mypy` errors and more (#8073) --- .../local_weighted_learning.py | 188 +++++++++++------- 1 file changed, 112 insertions(+), 76 deletions(-) diff --git a/machine_learning/local_weighted_learning/local_weighted_learning.py b/machine_learning/local_weighted_learning/local_weighted_learning.py index 6260e9ac6bfe..8dd0e55d41df 100644 --- a/machine_learning/local_weighted_learning/local_weighted_learning.py +++ b/machine_learning/local_weighted_learning/local_weighted_learning.py @@ -1,14 +1,55 @@ +""" +Locally weighted linear regression, also called local regression, is a type of +non-parametric linear regression that prioritizes data closest to a given +prediction point. The algorithm estimates the vector of model coefficients β +using weighted least squares regression: + +β = (XᵀWX)⁻¹(XᵀWy), + +where X is the design matrix, y is the response vector, and W is the diagonal +weight matrix. + +This implementation calculates wᵢ, the weight of the ith training sample, using +the Gaussian weight: + +wᵢ = exp(-‖xᵢ - x‖²/(2τ²)), + +where xᵢ is the ith training sample, x is the prediction point, τ is the +"bandwidth", and ‖x‖ is the Euclidean norm (also called the 2-norm or the L² +norm). The bandwidth τ controls how quickly the weight of a training sample +decreases as its distance from the prediction point increases. One can think of +the Gaussian weight as a bell curve centered around the prediction point: a +training sample is weighted lower if it's farther from the center, and τ +controls the spread of the bell curve. + +Other types of locally weighted regression such as locally estimated scatterplot +smoothing (LOESS) typically use different weight functions. + +References: + - https://en.wikipedia.org/wiki/Local_regression + - https://en.wikipedia.org/wiki/Weighted_least_squares + - https://cs229.stanford.edu/notes2022fall/main_notes.pdf +""" + import matplotlib.pyplot as plt import numpy as np -def weighted_matrix( - point: np.array, training_data_x: np.array, bandwidth: float -) -> np.array: +def weight_matrix(point: np.ndarray, x_train: np.ndarray, tau: float) -> np.ndarray: """ - Calculate the weight for every point in the data set. - point --> the x value at which we want to make predictions - >>> weighted_matrix( + Calculate the weight of every point in the training data around a given + prediction point + + Args: + point: x-value at which the prediction is being made + x_train: ndarray of x-values for training + tau: bandwidth value, controls how quickly the weight of training values + decreases as the distance from the prediction point increases + + Returns: + m x m weight matrix around the prediction point, where m is the size of + the training set + >>> weight_matrix( ... np.array([1., 1.]), ... np.array([[16.99, 10.34], [21.01,23.68], [24.59,25.69]]), ... 0.6 @@ -17,25 +58,30 @@ def weighted_matrix( [0.00000000e+000, 0.00000000e+000, 0.00000000e+000], [0.00000000e+000, 0.00000000e+000, 0.00000000e+000]]) """ - m, _ = np.shape(training_data_x) # m is the number of training samples - weights = np.eye(m) # Initializing weights as identity matrix - - # calculating weights for all training examples [x(i)'s] + m = len(x_train) # Number of training samples + weights = np.eye(m) # Initialize weights as identity matrix for j in range(m): - diff = point - training_data_x[j] - weights[j, j] = np.exp(diff @ diff.T / (-2.0 * bandwidth**2)) + diff = point - x_train[j] + weights[j, j] = np.exp(diff @ diff.T / (-2.0 * tau**2)) + return weights def local_weight( - point: np.array, - training_data_x: np.array, - training_data_y: np.array, - bandwidth: float, -) -> np.array: + point: np.ndarray, x_train: np.ndarray, y_train: np.ndarray, tau: float +) -> np.ndarray: """ - Calculate the local weights using the weight_matrix function on training data. - Return the weighted matrix. + Calculate the local weights at a given prediction point using the weight + matrix for that point + + Args: + point: x-value at which the prediction is being made + x_train: ndarray of x-values for training + y_train: ndarray of y-values for training + tau: bandwidth value, controls how quickly the weight of training values + decreases as the distance from the prediction point increases + Returns: + ndarray of local weights >>> local_weight( ... np.array([1., 1.]), ... np.array([[16.99, 10.34], [21.01,23.68], [24.59,25.69]]), @@ -45,19 +91,28 @@ def local_weight( array([[0.00873174], [0.08272556]]) """ - weight = weighted_matrix(point, training_data_x, bandwidth) - w = np.linalg.inv(training_data_x.T @ (weight @ training_data_x)) @ ( - training_data_x.T @ weight @ training_data_y.T + weight_mat = weight_matrix(point, x_train, tau) + weight = np.linalg.inv(x_train.T @ weight_mat @ x_train) @ ( + x_train.T @ weight_mat @ y_train.T ) - return w + return weight def local_weight_regression( - training_data_x: np.array, training_data_y: np.array, bandwidth: float -) -> np.array: + x_train: np.ndarray, y_train: np.ndarray, tau: float +) -> np.ndarray: """ - Calculate predictions for each data point on axis + Calculate predictions for each point in the training data + + Args: + x_train: ndarray of x-values for training + y_train: ndarray of y-values for training + tau: bandwidth value, controls how quickly the weight of training values + decreases as the distance from the prediction point increases + + Returns: + ndarray of predictions >>> local_weight_regression( ... np.array([[16.99, 10.34], [21.01, 23.68], [24.59, 25.69]]), ... np.array([[1.01, 1.66, 3.5]]), @@ -65,77 +120,57 @@ def local_weight_regression( ... ) array([1.07173261, 1.65970737, 3.50160179]) """ - m, _ = np.shape(training_data_x) - ypred = np.zeros(m) + y_pred = np.zeros(len(x_train)) # Initialize array of predictions + for i, item in enumerate(x_train): + y_pred[i] = item @ local_weight(item, x_train, y_train, tau) - for i, item in enumerate(training_data_x): - ypred[i] = item @ local_weight( - item, training_data_x, training_data_y, bandwidth - ) - - return ypred + return y_pred def load_data( - dataset_name: str, cola_name: str, colb_name: str -) -> tuple[np.array, np.array, np.array, np.array]: + dataset_name: str, x_name: str, y_name: str +) -> tuple[np.ndarray, np.ndarray, np.ndarray]: """ Load data from seaborn and split it into x and y points + >>> pass # No doctests, function is for demo purposes only """ import seaborn as sns data = sns.load_dataset(dataset_name) - col_a = np.array(data[cola_name]) # total_bill - col_b = np.array(data[colb_name]) # tip - - mcol_a = col_a.copy() - mcol_b = col_b.copy() - - one = np.ones(np.shape(mcol_b)[0], dtype=int) + x_data = np.array(data[x_name]) + y_data = np.array(data[y_name]) - # pairing elements of one and mcol_a - training_data_x = np.column_stack((one, mcol_a)) + one = np.ones(len(y_data)) - return training_data_x, mcol_b, col_a, col_b + # pairing elements of one and x_data + x_train = np.column_stack((one, x_data)) - -def get_preds(training_data_x: np.array, mcol_b: np.array, tau: float) -> np.array: - """ - Get predictions with minimum error for each training data - >>> get_preds( - ... np.array([[16.99, 10.34], [21.01, 23.68], [24.59, 25.69]]), - ... np.array([[1.01, 1.66, 3.5]]), - ... 0.6 - ... ) - array([1.07173261, 1.65970737, 3.50160179]) - """ - ypred = local_weight_regression(training_data_x, mcol_b, tau) - return ypred + return x_train, x_data, y_data def plot_preds( - training_data_x: np.array, - predictions: np.array, - col_x: np.array, - col_y: np.array, - cola_name: str, - colb_name: str, -) -> plt.plot: + x_train: np.ndarray, + preds: np.ndarray, + x_data: np.ndarray, + y_data: np.ndarray, + x_name: str, + y_name: str, +) -> None: """ Plot predictions and display the graph + >>> pass # No doctests, function is for demo purposes only """ - xsort = training_data_x.copy() - xsort.sort(axis=0) - plt.scatter(col_x, col_y, color="blue") + x_train_sorted = np.sort(x_train, axis=0) + plt.scatter(x_data, y_data, color="blue") plt.plot( - xsort[:, 1], - predictions[training_data_x[:, 1].argsort(0)], + x_train_sorted[:, 1], + preds[x_train[:, 1].argsort(0)], color="yellow", linewidth=5, ) plt.title("Local Weighted Regression") - plt.xlabel(cola_name) - plt.ylabel(colb_name) + plt.xlabel(x_name) + plt.ylabel(y_name) plt.show() @@ -144,6 +179,7 @@ def plot_preds( doctest.testmod() - training_data_x, mcol_b, col_a, col_b = load_data("tips", "total_bill", "tip") - predictions = get_preds(training_data_x, mcol_b, 0.5) - plot_preds(training_data_x, predictions, col_a, col_b, "total_bill", "tip") + # Demo with a dataset from the seaborn module + training_data_x, total_bill, tip = load_data("tips", "total_bill", "tip") + predictions = local_weight_regression(training_data_x, tip, 5) + plot_preds(training_data_x, predictions, total_bill, tip, "total_bill", "tip") From 3dc143f7218a1221f346c0fccb516d1199850e18 Mon Sep 17 00:00:00 2001 From: Rohan Saraogi <62804340+r0sa2@users.noreply.github.com> Date: Wed, 17 May 2023 05:38:56 +0530 Subject: [PATCH 071/757] Added odd_sieve.py (#8740) --- maths/odd_sieve.py | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 maths/odd_sieve.py diff --git a/maths/odd_sieve.py b/maths/odd_sieve.py new file mode 100644 index 000000000000..60e92921a94c --- /dev/null +++ b/maths/odd_sieve.py @@ -0,0 +1,42 @@ +from itertools import compress, repeat +from math import ceil, sqrt + + +def odd_sieve(num: int) -> list[int]: + """ + Returns the prime numbers < `num`. The prime numbers are calculated using an + odd sieve implementation of the Sieve of Eratosthenes algorithm + (see for reference https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes). + + >>> odd_sieve(2) + [] + >>> odd_sieve(3) + [2] + >>> odd_sieve(10) + [2, 3, 5, 7] + >>> odd_sieve(20) + [2, 3, 5, 7, 11, 13, 17, 19] + """ + + if num <= 2: + return [] + if num == 3: + return [2] + + # Odd sieve for numbers in range [3, num - 1] + sieve = bytearray(b"\x01") * ((num >> 1) - 1) + + for i in range(3, int(sqrt(num)) + 1, 2): + if sieve[(i >> 1) - 1]: + i_squared = i**2 + sieve[(i_squared >> 1) - 1 :: i] = repeat( + 0, ceil((num - i_squared) / (i << 1)) + ) + + return [2] + list(compress(range(3, num, 2), sieve)) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 61cfb43d2b9246d1e2019ce7f03cb91f452ed2ba Mon Sep 17 00:00:00 2001 From: Alexander Pantyukhin Date: Wed, 17 May 2023 04:21:16 +0400 Subject: [PATCH 072/757] Add h index (#8036) --- DIRECTORY.md | 1 + other/h_index.py | 71 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 other/h_index.py diff --git a/DIRECTORY.md b/DIRECTORY.md index fc6cbaf7ff41..46bd51ce91ea 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -712,6 +712,7 @@ * [Gauss Easter](other/gauss_easter.py) * [Graham Scan](other/graham_scan.py) * [Greedy](other/greedy.py) + * [H Index](other/h_index.py) * [Least Recently Used](other/least_recently_used.py) * [Lfu Cache](other/lfu_cache.py) * [Linear Congruential Generator](other/linear_congruential_generator.py) diff --git a/other/h_index.py b/other/h_index.py new file mode 100644 index 000000000000..e91389675b16 --- /dev/null +++ b/other/h_index.py @@ -0,0 +1,71 @@ +""" +Task: +Given an array of integers citations where citations[i] is the number of +citations a researcher received for their ith paper, return compute the +researcher's h-index. + +According to the definition of h-index on Wikipedia: A scientist has an +index h if h of their n papers have at least h citations each, and the other +n - h papers have no more than h citations each. + +If there are several possible values for h, the maximum one is taken as the +h-index. + +H-Index link: https://en.wikipedia.org/wiki/H-index + +Implementation notes: +Use sorting of array + +Leetcode link: https://leetcode.com/problems/h-index/description/ + +n = len(citations) +Runtime Complexity: O(n * log(n)) +Space Complexity: O(1) + +""" + + +def h_index(citations: list[int]) -> int: + """ + Return H-index of citations + + >>> h_index([3, 0, 6, 1, 5]) + 3 + >>> h_index([1, 3, 1]) + 1 + >>> h_index([1, 2, 3]) + 2 + >>> h_index('test') + Traceback (most recent call last): + ... + ValueError: The citations should be a list of non negative integers. + >>> h_index([1,2,'3']) + Traceback (most recent call last): + ... + ValueError: The citations should be a list of non negative integers. + >>> h_index([1,2,-3]) + Traceback (most recent call last): + ... + ValueError: The citations should be a list of non negative integers. + """ + + # validate: + if not isinstance(citations, list) or not all( + isinstance(item, int) and item >= 0 for item in citations + ): + raise ValueError("The citations should be a list of non negative integers.") + + citations.sort() + len_citations = len(citations) + + for i in range(len_citations): + if citations[len_citations - 1 - i] <= i: + return i + + return len_citations + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From a2783c6597a154a87f60bb5878770d2f152a1d09 Mon Sep 17 00:00:00 2001 From: Harkishan Khuva <78949167+hakiKhuva@users.noreply.github.com> Date: Wed, 17 May 2023 05:52:24 +0530 Subject: [PATCH 073/757] Create guess_the_number_search.py (#7937) --- other/guess_the_number_search.py | 165 +++++++++++++++++++++++++++++++ 1 file changed, 165 insertions(+) create mode 100644 other/guess_the_number_search.py diff --git a/other/guess_the_number_search.py b/other/guess_the_number_search.py new file mode 100644 index 000000000000..0439223f2ec9 --- /dev/null +++ b/other/guess_the_number_search.py @@ -0,0 +1,165 @@ +""" +guess the number using lower,higher and the value to find or guess + +solution works by dividing lower and higher of number guessed + +suppose lower is 0, higher is 1000 and the number to guess is 355 + +>>> guess_the_number(10, 1000, 17) +started... +guess the number : 17 +details : [505, 257, 133, 71, 40, 25, 17] + +""" + + +def temp_input_value( + min_val: int = 10, max_val: int = 1000, option: bool = True +) -> int: + """ + Temporary input values for tests + + >>> temp_input_value(option=True) + 10 + + >>> temp_input_value(option=False) + 1000 + + >>> temp_input_value(min_val=100, option=True) + 100 + + >>> temp_input_value(min_val=100, max_val=50) + Traceback (most recent call last): + ... + ValueError: Invalid value for min_val or max_val (min_value < max_value) + + >>> temp_input_value("ten","fifty",1) + Traceback (most recent call last): + ... + AssertionError: Invalid type of value(s) specified to function! + + >>> temp_input_value(min_val=-100, max_val=500) + -100 + + >>> temp_input_value(min_val=-5100, max_val=-100) + -5100 + """ + assert ( + isinstance(min_val, int) + and isinstance(max_val, int) + and isinstance(option, bool) + ), "Invalid type of value(s) specified to function!" + + if min_val > max_val: + raise ValueError("Invalid value for min_val or max_val (min_value < max_value)") + return min_val if option else max_val + + +def get_avg(number_1: int, number_2: int) -> int: + """ + Return the mid-number(whole) of two integers a and b + + >>> get_avg(10, 15) + 12 + + >>> get_avg(20, 300) + 160 + + >>> get_avg("abcd", 300) + Traceback (most recent call last): + ... + TypeError: can only concatenate str (not "int") to str + + >>> get_avg(10.5,50.25) + 30 + """ + return int((number_1 + number_2) / 2) + + +def guess_the_number(lower: int, higher: int, to_guess: int) -> None: + """ + The `guess_the_number` function that guess the number by some operations + and using inner functions + + >>> guess_the_number(10, 1000, 17) + started... + guess the number : 17 + details : [505, 257, 133, 71, 40, 25, 17] + + >>> guess_the_number(-10000, 10000, 7) + started... + guess the number : 7 + details : [0, 5000, 2500, 1250, 625, 312, 156, 78, 39, 19, 9, 4, 6, 7] + + >>> guess_the_number(10, 1000, "a") + Traceback (most recent call last): + ... + AssertionError: argument values must be type of "int" + + >>> guess_the_number(10, 1000, 5) + Traceback (most recent call last): + ... + ValueError: guess value must be within the range of lower and higher value + + >>> guess_the_number(10000, 100, 5) + Traceback (most recent call last): + ... + ValueError: argument value for lower and higher must be(lower > higher) + """ + assert ( + isinstance(lower, int) and isinstance(higher, int) and isinstance(to_guess, int) + ), 'argument values must be type of "int"' + + if lower > higher: + raise ValueError("argument value for lower and higher must be(lower > higher)") + + if not lower < to_guess < higher: + raise ValueError( + "guess value must be within the range of lower and higher value" + ) + + def answer(number: int) -> str: + """ + Returns value by comparing with entered `to_guess` number + """ + if number > to_guess: + return "high" + elif number < to_guess: + return "low" + else: + return "same" + + print("started...") + + last_lowest = lower + last_highest = higher + + last_numbers = [] + + while True: + number = get_avg(last_lowest, last_highest) + last_numbers.append(number) + + if answer(number) == "low": + last_lowest = number + elif answer(number) == "high": + last_highest = number + else: + break + + print(f"guess the number : {last_numbers[-1]}") + print(f"details : {str(last_numbers)}") + + +def main() -> None: + """ + starting point or function of script + """ + lower = int(input("Enter lower value : ").strip()) + higher = int(input("Enter high value : ").strip()) + guess = int(input("Enter value to guess : ").strip()) + guess_the_number(lower, higher, guess) + + +if __name__ == "__main__": + main() From 9b3e4028c6927a17656e590e878c2a101bc4e951 Mon Sep 17 00:00:00 2001 From: Caeden Perelli-Harris Date: Wed, 17 May 2023 07:47:23 +0100 Subject: [PATCH 074/757] Fixes broken "Create guess_the_number_search.py" (#8746) --- DIRECTORY.md | 2 ++ other/guess_the_number_search.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index 46bd51ce91ea..82791cde183d 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -605,6 +605,7 @@ * [Newton Raphson](maths/newton_raphson.py) * [Number Of Digits](maths/number_of_digits.py) * [Numerical Integration](maths/numerical_integration.py) + * [Odd Sieve](maths/odd_sieve.py) * [Perfect Cube](maths/perfect_cube.py) * [Perfect Number](maths/perfect_number.py) * [Perfect Square](maths/perfect_square.py) @@ -712,6 +713,7 @@ * [Gauss Easter](other/gauss_easter.py) * [Graham Scan](other/graham_scan.py) * [Greedy](other/greedy.py) + * [Guess The Number Search](other/guess_the_number_search.py) * [H Index](other/h_index.py) * [Least Recently Used](other/least_recently_used.py) * [Lfu Cache](other/lfu_cache.py) diff --git a/other/guess_the_number_search.py b/other/guess_the_number_search.py index 0439223f2ec9..01e8898bbb8a 100644 --- a/other/guess_the_number_search.py +++ b/other/guess_the_number_search.py @@ -148,7 +148,7 @@ def answer(number: int) -> str: break print(f"guess the number : {last_numbers[-1]}") - print(f"details : {str(last_numbers)}") + print(f"details : {last_numbers!s}") def main() -> None: From cf5e34d4794fbba04d18c98d5d09854029c83466 Mon Sep 17 00:00:00 2001 From: Rohan Saraogi <62804340+r0sa2@users.noreply.github.com> Date: Fri, 19 May 2023 05:18:22 +0530 Subject: [PATCH 075/757] Added is_palindrome.py (#8748) --- maths/is_palindrome.py | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 maths/is_palindrome.py diff --git a/maths/is_palindrome.py b/maths/is_palindrome.py new file mode 100644 index 000000000000..ba60573ab022 --- /dev/null +++ b/maths/is_palindrome.py @@ -0,0 +1,34 @@ +def is_palindrome(num: int) -> bool: + """ + Returns whether `num` is a palindrome or not + (see for reference https://en.wikipedia.org/wiki/Palindromic_number). + + >>> is_palindrome(-121) + False + >>> is_palindrome(0) + True + >>> is_palindrome(10) + False + >>> is_palindrome(11) + True + >>> is_palindrome(101) + True + >>> is_palindrome(120) + False + """ + if num < 0: + return False + + num_copy: int = num + rev_num: int = 0 + while num > 0: + rev_num = rev_num * 10 + (num % 10) + num //= 10 + + return num_copy == rev_num + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From edc17b60e00e704cb4109a0e6b18c6ad43234c26 Mon Sep 17 00:00:00 2001 From: Daniel Luo <103051750+DanielLuo7@users.noreply.github.com> Date: Thu, 18 May 2023 20:40:52 -0400 Subject: [PATCH 076/757] add __main__ around print (#8747) --- ciphers/mixed_keyword_cypher.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ciphers/mixed_keyword_cypher.py b/ciphers/mixed_keyword_cypher.py index 806004faa079..93a0e3acb7b1 100644 --- a/ciphers/mixed_keyword_cypher.py +++ b/ciphers/mixed_keyword_cypher.py @@ -65,4 +65,5 @@ def mixed_keyword(key: str = "college", pt: str = "UNIVERSITY") -> str: return cypher -print(mixed_keyword("college", "UNIVERSITY")) +if __name__ == "__main__": + print(mixed_keyword("college", "UNIVERSITY")) From ce43a8ac4ad14e1639014d374b1137906218cfe3 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 23 May 2023 05:54:30 +0200 Subject: [PATCH 077/757] [pre-commit.ci] pre-commit autoupdate (#8759) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/charliermarsh/ruff-pre-commit: v0.0.267 → v0.0.269](https://github.com/charliermarsh/ruff-pre-commit/compare/v0.0.267...v0.0.269) - [github.com/abravalheri/validate-pyproject: v0.12.2 → v0.13](https://github.com/abravalheri/validate-pyproject/compare/v0.12.2...v0.13) * updating DIRECTORY.md --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- .pre-commit-config.yaml | 4 ++-- DIRECTORY.md | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6bdbc7370c9c..bd5bca8f05ab 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/charliermarsh/ruff-pre-commit - rev: v0.0.267 + rev: v0.0.269 hooks: - id: ruff @@ -46,7 +46,7 @@ repos: pass_filenames: false - repo: https://github.com/abravalheri/validate-pyproject - rev: v0.12.2 + rev: v0.13 hooks: - id: validate-pyproject diff --git a/DIRECTORY.md b/DIRECTORY.md index 82791cde183d..3181a93f393d 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -577,6 +577,7 @@ * [Hexagonal Number](maths/hexagonal_number.py) * [Integration By Simpson Approx](maths/integration_by_simpson_approx.py) * [Is Ip V4 Address Valid](maths/is_ip_v4_address_valid.py) + * [Is Palindrome](maths/is_palindrome.py) * [Is Square Free](maths/is_square_free.py) * [Jaccard Similarity](maths/jaccard_similarity.py) * [Juggler Sequence](maths/juggler_sequence.py) From df88771905e68c0639069a92144d6b7af1d491ce Mon Sep 17 00:00:00 2001 From: Caeden Perelli-Harris Date: Thu, 25 May 2023 06:59:15 +0100 Subject: [PATCH 078/757] Mark fetch anime and play as broken (#8763) * updating DIRECTORY.md * updating DIRECTORY.md * fix: Correct ruff errors * fix: Mark anime algorithm as broken * updating DIRECTORY.md --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 1 - .../{fetch_anime_and_play.py => fetch_anime_and_play.py.BROKEN} | 0 2 files changed, 1 deletion(-) rename web_programming/{fetch_anime_and_play.py => fetch_anime_and_play.py.BROKEN} (100%) diff --git a/DIRECTORY.md b/DIRECTORY.md index 3181a93f393d..71bdf30b2ddb 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -1199,7 +1199,6 @@ * [Daily Horoscope](web_programming/daily_horoscope.py) * [Download Images From Google Query](web_programming/download_images_from_google_query.py) * [Emails From Url](web_programming/emails_from_url.py) - * [Fetch Anime And Play](web_programming/fetch_anime_and_play.py) * [Fetch Bbc News](web_programming/fetch_bbc_news.py) * [Fetch Github Info](web_programming/fetch_github_info.py) * [Fetch Jobs](web_programming/fetch_jobs.py) diff --git a/web_programming/fetch_anime_and_play.py b/web_programming/fetch_anime_and_play.py.BROKEN similarity index 100% rename from web_programming/fetch_anime_and_play.py rename to web_programming/fetch_anime_and_play.py.BROKEN From 200429fc4739c3757180635016614b984cfd2206 Mon Sep 17 00:00:00 2001 From: Chris O <46587501+ChrisO345@users.noreply.github.com> Date: Thu, 25 May 2023 18:04:42 +1200 Subject: [PATCH 079/757] Dual Number Automatic Differentiation (#8760) * Added dual_number_automatic_differentiation.py * updating DIRECTORY.md * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update maths/dual_number_automatic_differentiation.py --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- DIRECTORY.md | 1 + .../dual_number_automatic_differentiation.py | 141 ++++++++++++++++++ 2 files changed, 142 insertions(+) create mode 100644 maths/dual_number_automatic_differentiation.py diff --git a/DIRECTORY.md b/DIRECTORY.md index 71bdf30b2ddb..a75723369b06 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -549,6 +549,7 @@ * [Dodecahedron](maths/dodecahedron.py) * [Double Factorial Iterative](maths/double_factorial_iterative.py) * [Double Factorial Recursive](maths/double_factorial_recursive.py) + * [Dual Number Automatic Differentiation](maths/dual_number_automatic_differentiation.py) * [Entropy](maths/entropy.py) * [Euclidean Distance](maths/euclidean_distance.py) * [Euclidean Gcd](maths/euclidean_gcd.py) diff --git a/maths/dual_number_automatic_differentiation.py b/maths/dual_number_automatic_differentiation.py new file mode 100644 index 000000000000..9aa75830c4a1 --- /dev/null +++ b/maths/dual_number_automatic_differentiation.py @@ -0,0 +1,141 @@ +from math import factorial + +""" +https://en.wikipedia.org/wiki/Automatic_differentiation#Automatic_differentiation_using_dual_numbers +https://blog.jliszka.org/2013/10/24/exact-numeric-nth-derivatives.html + +Note this only works for basic functions, f(x) where the power of x is positive. +""" + + +class Dual: + def __init__(self, real, rank): + self.real = real + if isinstance(rank, int): + self.duals = [1] * rank + else: + self.duals = rank + + def __repr__(self): + return ( + f"{self.real}+" + f"{'+'.join(str(dual)+'E'+str(n+1)for n,dual in enumerate(self.duals))}" + ) + + def reduce(self): + cur = self.duals.copy() + while cur[-1] == 0: + cur.pop(-1) + return Dual(self.real, cur) + + def __add__(self, other): + if not isinstance(other, Dual): + return Dual(self.real + other, self.duals) + s_dual = self.duals.copy() + o_dual = other.duals.copy() + if len(s_dual) > len(o_dual): + o_dual.extend([1] * (len(s_dual) - len(o_dual))) + elif len(s_dual) < len(o_dual): + s_dual.extend([1] * (len(o_dual) - len(s_dual))) + new_duals = [] + for i in range(len(s_dual)): + new_duals.append(s_dual[i] + o_dual[i]) + return Dual(self.real + other.real, new_duals) + + __radd__ = __add__ + + def __sub__(self, other): + return self + other * -1 + + def __mul__(self, other): + if not isinstance(other, Dual): + new_duals = [] + for i in self.duals: + new_duals.append(i * other) + return Dual(self.real * other, new_duals) + new_duals = [0] * (len(self.duals) + len(other.duals) + 1) + for i, item in enumerate(self.duals): + for j, jtem in enumerate(other.duals): + new_duals[i + j + 1] += item * jtem + for k in range(len(self.duals)): + new_duals[k] += self.duals[k] * other.real + for index in range(len(other.duals)): + new_duals[index] += other.duals[index] * self.real + return Dual(self.real * other.real, new_duals) + + __rmul__ = __mul__ + + def __truediv__(self, other): + if not isinstance(other, Dual): + new_duals = [] + for i in self.duals: + new_duals.append(i / other) + return Dual(self.real / other, new_duals) + raise ValueError() + + def __floordiv__(self, other): + if not isinstance(other, Dual): + new_duals = [] + for i in self.duals: + new_duals.append(i // other) + return Dual(self.real // other, new_duals) + raise ValueError() + + def __pow__(self, n): + if n < 0 or isinstance(n, float): + raise ValueError("power must be a positive integer") + if n == 0: + return 1 + if n == 1: + return self + x = self + for _ in range(n - 1): + x *= self + return x + + +def differentiate(func, position, order): + """ + >>> differentiate(lambda x: x**2, 2, 2) + 2 + >>> differentiate(lambda x: x**2 * x**4, 9, 2) + 196830 + >>> differentiate(lambda y: 0.5 * (y + 3) ** 6, 3.5, 4) + 7605.0 + >>> differentiate(lambda y: y ** 2, 4, 3) + 0 + >>> differentiate(8, 8, 8) + Traceback (most recent call last): + ... + ValueError: differentiate() requires a function as input for func + >>> differentiate(lambda x: x **2, "", 1) + Traceback (most recent call last): + ... + ValueError: differentiate() requires a float as input for position + >>> differentiate(lambda x: x**2, 3, "") + Traceback (most recent call last): + ... + ValueError: differentiate() requires an int as input for order + """ + if not callable(func): + raise ValueError("differentiate() requires a function as input for func") + if not isinstance(position, (float, int)): + raise ValueError("differentiate() requires a float as input for position") + if not isinstance(order, int): + raise ValueError("differentiate() requires an int as input for order") + d = Dual(position, 1) + result = func(d) + if order == 0: + return result.real + return result.duals[order - 1] * factorial(order) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + + def f(y): + return y**2 * y**4 + + print(differentiate(f, 9, 2)) From a6631487b0a9d6a310d8c45d211e8b7b7bd93cab Mon Sep 17 00:00:00 2001 From: Ratnesh Kumar <89133941+ratneshrt@users.noreply.github.com> Date: Thu, 25 May 2023 16:04:11 +0530 Subject: [PATCH 080/757] Fix CI badge in the README.md (#8137) From cfbbfd9896cc96379f7374a68ff04b245bb3527c Mon Sep 17 00:00:00 2001 From: Caeden Perelli-Harris Date: Thu, 25 May 2023 11:56:23 +0100 Subject: [PATCH 081/757] Merge and add benchmarks to palindrome algorithms in the strings/ directory (#8749) * refactor: Merge and add benchmarks to palindrome * updating DIRECTORY.md * chore: Fix failing tests * Update strings/palindrome.py Co-authored-by: Christian Clauss * Update palindrome.py --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: Christian Clauss --- DIRECTORY.md | 1 - strings/is_palindrome.py | 41 ---------------------------------------- strings/palindrome.py | 40 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 39 insertions(+), 43 deletions(-) delete mode 100644 strings/is_palindrome.py diff --git a/DIRECTORY.md b/DIRECTORY.md index a75723369b06..fe4baac863d0 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -1156,7 +1156,6 @@ * [Indian Phone Validator](strings/indian_phone_validator.py) * [Is Contains Unique Chars](strings/is_contains_unique_chars.py) * [Is Isogram](strings/is_isogram.py) - * [Is Palindrome](strings/is_palindrome.py) * [Is Pangram](strings/is_pangram.py) * [Is Spain National Id](strings/is_spain_national_id.py) * [Is Srilankan Phone Number](strings/is_srilankan_phone_number.py) diff --git a/strings/is_palindrome.py b/strings/is_palindrome.py deleted file mode 100644 index 406aa2e8d3c3..000000000000 --- a/strings/is_palindrome.py +++ /dev/null @@ -1,41 +0,0 @@ -def is_palindrome(s: str) -> bool: - """ - Determine if the string s is a palindrome. - - >>> is_palindrome("A man, A plan, A canal -- Panama!") - True - >>> is_palindrome("Hello") - False - >>> is_palindrome("Able was I ere I saw Elba") - True - >>> is_palindrome("racecar") - True - >>> is_palindrome("Mr. Owl ate my metal worm?") - True - """ - # Since punctuation, capitalization, and spaces are often ignored while checking - # palindromes, we first remove them from our string. - s = "".join(character for character in s.lower() if character.isalnum()) - # return s == s[::-1] the slicing method - # uses extra spaces we can - # better with iteration method. - - end = len(s) // 2 - n = len(s) - - # We need to traverse till half of the length of string - # as we can get access of the i'th last element from - # i'th index. - # eg: [0,1,2,3,4,5] => 4th index can be accessed - # with the help of 1st index (i==n-i-1) - # where n is length of string - - return all(s[i] == s[n - i - 1] for i in range(end)) - - -if __name__ == "__main__": - s = input("Please enter a string to see if it is a palindrome: ") - if is_palindrome(s): - print(f"'{s}' is a palindrome.") - else: - print(f"'{s}' is not a palindrome.") diff --git a/strings/palindrome.py b/strings/palindrome.py index dd1fe316f479..bfdb3ddcf396 100644 --- a/strings/palindrome.py +++ b/strings/palindrome.py @@ -1,5 +1,7 @@ # Algorithms to determine if a string is palindrome +from timeit import timeit + test_data = { "MALAYALAM": True, "String": False, @@ -33,6 +35,25 @@ def is_palindrome(s: str) -> bool: return True +def is_palindrome_traversal(s: str) -> bool: + """ + Return True if s is a palindrome otherwise return False. + + >>> all(is_palindrome_traversal(key) is value for key, value in test_data.items()) + True + """ + end = len(s) // 2 + n = len(s) + + # We need to traverse till half of the length of string + # as we can get access of the i'th last element from + # i'th index. + # eg: [0,1,2,3,4,5] => 4th index can be accessed + # with the help of 1st index (i==n-i-1) + # where n is length of string + return all(s[i] == s[n - i - 1] for i in range(end)) + + def is_palindrome_recursive(s: str) -> bool: """ Return True if s is a palindrome otherwise return False. @@ -40,7 +61,7 @@ def is_palindrome_recursive(s: str) -> bool: >>> all(is_palindrome_recursive(key) is value for key, value in test_data.items()) True """ - if len(s) <= 1: + if len(s) <= 2: return True if s[0] == s[len(s) - 1]: return is_palindrome_recursive(s[1:-1]) @@ -58,9 +79,26 @@ def is_palindrome_slice(s: str) -> bool: return s == s[::-1] +def benchmark_function(name: str) -> None: + stmt = f"all({name}(key) is value for key, value in test_data.items())" + setup = f"from __main__ import test_data, {name}" + number = 500000 + result = timeit(stmt=stmt, setup=setup, number=number) + print(f"{name:<35} finished {number:,} runs in {result:.5f} seconds") + + if __name__ == "__main__": for key, value in test_data.items(): assert is_palindrome(key) is is_palindrome_recursive(key) assert is_palindrome(key) is is_palindrome_slice(key) print(f"{key:21} {value}") print("a man a plan a canal panama") + + # finished 500,000 runs in 0.46793 seconds + benchmark_function("is_palindrome_slice") + # finished 500,000 runs in 0.85234 seconds + benchmark_function("is_palindrome") + # finished 500,000 runs in 1.32028 seconds + benchmark_function("is_palindrome_recursive") + # finished 500,000 runs in 2.08679 seconds + benchmark_function("is_palindrome_traversal") From a17791d022bdc942c8badabc52307c354069a7ae Mon Sep 17 00:00:00 2001 From: Juyoung Kim <61103343+JadeKim042386@users.noreply.github.com> Date: Thu, 25 May 2023 21:54:18 +0900 Subject: [PATCH 082/757] fix: graphs/greedy_best_first typo (#8766) #8764 --- graphs/greedy_best_first.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/graphs/greedy_best_first.py b/graphs/greedy_best_first.py index d49e65b9d814..35f7ca9feeef 100644 --- a/graphs/greedy_best_first.py +++ b/graphs/greedy_best_first.py @@ -58,8 +58,8 @@ def calculate_heuristic(self) -> float: The heuristic here is the Manhattan Distance Could elaborate to offer more than one choice """ - dy = abs(self.pos_x - self.goal_x) - dx = abs(self.pos_y - self.goal_y) + dx = abs(self.pos_x - self.goal_x) + dy = abs(self.pos_y - self.goal_y) return dx + dy def __lt__(self, other) -> bool: From dd3b499bfa972507759d0705b77e2e1946f42596 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Fri, 26 May 2023 08:50:33 +0200 Subject: [PATCH 083/757] Rename is_palindrome.py to is_int_palindrome.py (#8768) * Rename is_palindrome.py to is_int_palindrome.py * updating DIRECTORY.md --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 2 +- maths/{is_palindrome.py => is_int_palindrome.py} | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) rename maths/{is_palindrome.py => is_int_palindrome.py} (67%) diff --git a/DIRECTORY.md b/DIRECTORY.md index fe4baac863d0..11ff93c91430 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -577,8 +577,8 @@ * [Hardy Ramanujanalgo](maths/hardy_ramanujanalgo.py) * [Hexagonal Number](maths/hexagonal_number.py) * [Integration By Simpson Approx](maths/integration_by_simpson_approx.py) + * [Is Int Palindrome](maths/is_int_palindrome.py) * [Is Ip V4 Address Valid](maths/is_ip_v4_address_valid.py) - * [Is Palindrome](maths/is_palindrome.py) * [Is Square Free](maths/is_square_free.py) * [Jaccard Similarity](maths/jaccard_similarity.py) * [Juggler Sequence](maths/juggler_sequence.py) diff --git a/maths/is_palindrome.py b/maths/is_int_palindrome.py similarity index 67% rename from maths/is_palindrome.py rename to maths/is_int_palindrome.py index ba60573ab022..63dc9e2138e8 100644 --- a/maths/is_palindrome.py +++ b/maths/is_int_palindrome.py @@ -1,19 +1,19 @@ -def is_palindrome(num: int) -> bool: +def is_int_palindrome(num: int) -> bool: """ Returns whether `num` is a palindrome or not (see for reference https://en.wikipedia.org/wiki/Palindromic_number). - >>> is_palindrome(-121) + >>> is_int_palindrome(-121) False - >>> is_palindrome(0) + >>> is_int_palindrome(0) True - >>> is_palindrome(10) + >>> is_int_palindrome(10) False - >>> is_palindrome(11) + >>> is_int_palindrome(11) True - >>> is_palindrome(101) + >>> is_int_palindrome(101) True - >>> is_palindrome(120) + >>> is_int_palindrome(120) False """ if num < 0: From 4b79d771cd81a820c195e62430100c416a1618ea Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Fri, 26 May 2023 09:34:17 +0200 Subject: [PATCH 084/757] Add more ruff rules (#8767) * Add more ruff rules * Add more ruff rules * pre-commit: Update ruff v0.0.269 -> v0.0.270 * Apply suggestions from code review * Fix doctest * Fix doctest (ignore whitespace) * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: Dhruv Manilawala Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- .../jacobi_iteration_method.py | 30 ++-- arithmetic_analysis/lu_decomposition.py | 5 +- audio_filters/iir_filter.py | 14 +- backtracking/knight_tour.py | 3 +- bit_manipulation/reverse_bits.py | 3 +- ciphers/base64.py | 12 +- ciphers/beaufort_cipher.py | 2 +- ciphers/cryptomath_module.py | 3 +- ciphers/enigma_machine2.py | 30 ++-- ciphers/hill_cipher.py | 7 +- .../astronomical_length_scale_conversion.py | 6 +- conversions/length_conversion.py | 6 +- conversions/speed_conversions.py | 3 +- conversions/weight_conversion.py | 3 +- .../binary_search_tree_recursive.py | 6 +- .../binary_tree/binary_tree_mirror.py | 3 +- data_structures/disjoint_set/disjoint_set.py | 3 +- .../linked_list/circular_linked_list.py | 8 +- .../linked_list/doubly_linked_list.py | 4 +- .../linked_list/singly_linked_list.py | 4 +- data_structures/stacks/stack.py | 6 +- digital_image_processing/dithering/burkes.py | 3 +- divide_and_conquer/convex_hull.py | 8 +- dynamic_programming/knapsack.py | 15 +- dynamic_programming/minimum_steps_to_one.py | 3 +- dynamic_programming/rod_cutting.py | 10 +- dynamic_programming/viterbi.py | 17 ++- electronics/resistor_equivalence.py | 6 +- genetic_algorithm/basic_string.py | 8 +- graphics/vector3_for_2d_rendering.py | 8 +- graphs/breadth_first_search_shortest_path.py | 3 +- linear_algebra/src/schur_complement.py | 14 +- machine_learning/similarity_search.py | 21 +-- machine_learning/support_vector_machines.py | 3 +- maths/3n_plus_1.py | 6 +- maths/automorphic_number.py | 3 +- maths/catalan_number.py | 6 +- .../dual_number_automatic_differentiation.py | 4 +- maths/hexagonal_number.py | 3 +- maths/juggler_sequence.py | 6 +- maths/liouville_lambda.py | 3 +- maths/manhattan_distance.py | 18 +-- maths/pronic_number.py | 3 +- maths/proth_number.py | 6 +- maths/radix2_fft.py | 2 +- maths/sieve_of_eratosthenes.py | 3 +- maths/sylvester_sequence.py | 3 +- maths/twin_prime.py | 3 +- matrix/matrix_operation.py | 12 +- matrix/sherman_morrison.py | 3 +- neural_network/input_data.py | 12 +- other/nested_brackets.py | 2 +- other/scoring_algorithm.py | 3 +- project_euler/problem_054/sol1.py | 6 +- project_euler/problem_068/sol1.py | 3 +- project_euler/problem_131/sol1.py | 5 +- pyproject.toml | 139 +++++++++++++----- scripts/build_directory_md.py | 2 +- sorts/dutch_national_flag_sort.py | 5 +- strings/barcode_validator.py | 3 +- strings/capitalize.py | 2 +- strings/is_spain_national_id.py | 3 +- strings/snake_case_to_camel_pascal_case.py | 8 +- web_programming/reddit.py | 3 +- web_programming/search_books_by_isbn.py | 3 +- web_programming/slack_message.py | 7 +- 67 files changed, 349 insertions(+), 223 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index bd5bca8f05ab..4c70ae219f74 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/charliermarsh/ruff-pre-commit - rev: v0.0.269 + rev: v0.0.270 hooks: - id: ruff diff --git a/arithmetic_analysis/jacobi_iteration_method.py b/arithmetic_analysis/jacobi_iteration_method.py index fe506a94a65d..17edf4bf4b8b 100644 --- a/arithmetic_analysis/jacobi_iteration_method.py +++ b/arithmetic_analysis/jacobi_iteration_method.py @@ -49,7 +49,9 @@ def jacobi_iteration_method( >>> constant = np.array([[2], [-6]]) >>> init_val = [0.5, -0.5, -0.5] >>> iterations = 3 - >>> jacobi_iteration_method(coefficient, constant, init_val, iterations) + >>> jacobi_iteration_method( + ... coefficient, constant, init_val, iterations + ... ) # doctest: +NORMALIZE_WHITESPACE Traceback (most recent call last): ... ValueError: Coefficient and constant matrices dimensions must be nxn and nx1 but @@ -59,7 +61,9 @@ def jacobi_iteration_method( >>> constant = np.array([[2], [-6], [-4]]) >>> init_val = [0.5, -0.5] >>> iterations = 3 - >>> jacobi_iteration_method(coefficient, constant, init_val, iterations) + >>> jacobi_iteration_method( + ... coefficient, constant, init_val, iterations + ... ) # doctest: +NORMALIZE_WHITESPACE Traceback (most recent call last): ... ValueError: Number of initial values must be equal to number of rows in coefficient @@ -79,24 +83,26 @@ def jacobi_iteration_method( rows2, cols2 = constant_matrix.shape if rows1 != cols1: - raise ValueError( - f"Coefficient matrix dimensions must be nxn but received {rows1}x{cols1}" - ) + msg = f"Coefficient matrix dimensions must be nxn but received {rows1}x{cols1}" + raise ValueError(msg) if cols2 != 1: - raise ValueError(f"Constant matrix must be nx1 but received {rows2}x{cols2}") + msg = f"Constant matrix must be nx1 but received {rows2}x{cols2}" + raise ValueError(msg) if rows1 != rows2: - raise ValueError( - f"""Coefficient and constant matrices dimensions must be nxn and nx1 but - received {rows1}x{cols1} and {rows2}x{cols2}""" + msg = ( + "Coefficient and constant matrices dimensions must be nxn and nx1 but " + f"received {rows1}x{cols1} and {rows2}x{cols2}" ) + raise ValueError(msg) if len(init_val) != rows1: - raise ValueError( - f"""Number of initial values must be equal to number of rows in coefficient - matrix but received {len(init_val)} and {rows1}""" + msg = ( + "Number of initial values must be equal to number of rows in coefficient " + f"matrix but received {len(init_val)} and {rows1}" ) + raise ValueError(msg) if iterations <= 0: raise ValueError("Iterations must be at least 1") diff --git a/arithmetic_analysis/lu_decomposition.py b/arithmetic_analysis/lu_decomposition.py index 941c1dadf556..eaabce5449c5 100644 --- a/arithmetic_analysis/lu_decomposition.py +++ b/arithmetic_analysis/lu_decomposition.py @@ -80,10 +80,11 @@ def lower_upper_decomposition(table: np.ndarray) -> tuple[np.ndarray, np.ndarray # Ensure that table is a square array rows, columns = np.shape(table) if rows != columns: - raise ValueError( - f"'table' has to be of square shaped array but got a " + msg = ( + "'table' has to be of square shaped array but got a " f"{rows}x{columns} array:\n{table}" ) + raise ValueError(msg) lower = np.zeros((rows, columns)) upper = np.zeros((rows, columns)) diff --git a/audio_filters/iir_filter.py b/audio_filters/iir_filter.py index bd448175f6f3..f3c1ad43b001 100644 --- a/audio_filters/iir_filter.py +++ b/audio_filters/iir_filter.py @@ -50,16 +50,18 @@ def set_coefficients(self, a_coeffs: list[float], b_coeffs: list[float]) -> None a_coeffs = [1.0, *a_coeffs] if len(a_coeffs) != self.order + 1: - raise ValueError( - f"Expected a_coeffs to have {self.order + 1} elements for {self.order}" - f"-order filter, got {len(a_coeffs)}" + msg = ( + f"Expected a_coeffs to have {self.order + 1} elements " + f"for {self.order}-order filter, got {len(a_coeffs)}" ) + raise ValueError(msg) if len(b_coeffs) != self.order + 1: - raise ValueError( - f"Expected b_coeffs to have {self.order + 1} elements for {self.order}" - f"-order filter, got {len(a_coeffs)}" + msg = ( + f"Expected b_coeffs to have {self.order + 1} elements " + f"for {self.order}-order filter, got {len(a_coeffs)}" ) + raise ValueError(msg) self.a_coeffs = a_coeffs self.b_coeffs = b_coeffs diff --git a/backtracking/knight_tour.py b/backtracking/knight_tour.py index bb650ece3f5e..cc88307b7fe8 100644 --- a/backtracking/knight_tour.py +++ b/backtracking/knight_tour.py @@ -91,7 +91,8 @@ def open_knight_tour(n: int) -> list[list[int]]: return board board[i][j] = 0 - raise ValueError(f"Open Kight Tour cannot be performed on a board of size {n}") + msg = f"Open Kight Tour cannot be performed on a board of size {n}" + raise ValueError(msg) if __name__ == "__main__": diff --git a/bit_manipulation/reverse_bits.py b/bit_manipulation/reverse_bits.py index 55608ae12908..a8c77c11bfdd 100644 --- a/bit_manipulation/reverse_bits.py +++ b/bit_manipulation/reverse_bits.py @@ -14,10 +14,11 @@ def get_reverse_bit_string(number: int) -> str: TypeError: operation can not be conducted on a object of type str """ if not isinstance(number, int): - raise TypeError( + msg = ( "operation can not be conducted on a object of type " f"{type(number).__name__}" ) + raise TypeError(msg) bit_string = "" for _ in range(0, 32): bit_string += str(number % 2) diff --git a/ciphers/base64.py b/ciphers/base64.py index 38a952acc307..2b950b1be37d 100644 --- a/ciphers/base64.py +++ b/ciphers/base64.py @@ -34,9 +34,8 @@ def base64_encode(data: bytes) -> bytes: """ # Make sure the supplied data is a bytes-like object if not isinstance(data, bytes): - raise TypeError( - f"a bytes-like object is required, not '{data.__class__.__name__}'" - ) + msg = f"a bytes-like object is required, not '{data.__class__.__name__}'" + raise TypeError(msg) binary_stream = "".join(bin(byte)[2:].zfill(8) for byte in data) @@ -88,10 +87,11 @@ def base64_decode(encoded_data: str) -> bytes: """ # Make sure encoded_data is either a string or a bytes-like object if not isinstance(encoded_data, bytes) and not isinstance(encoded_data, str): - raise TypeError( - "argument should be a bytes-like object or ASCII string, not " - f"'{encoded_data.__class__.__name__}'" + msg = ( + "argument should be a bytes-like object or ASCII string, " + f"not '{encoded_data.__class__.__name__}'" ) + raise TypeError(msg) # In case encoded_data is a bytes-like object, make sure it contains only # ASCII characters so we convert it to a string object diff --git a/ciphers/beaufort_cipher.py b/ciphers/beaufort_cipher.py index 8eae847a7ff7..788fc72b89c3 100644 --- a/ciphers/beaufort_cipher.py +++ b/ciphers/beaufort_cipher.py @@ -5,7 +5,7 @@ from string import ascii_uppercase dict1 = {char: i for i, char in enumerate(ascii_uppercase)} -dict2 = {i: char for i, char in enumerate(ascii_uppercase)} +dict2 = dict(enumerate(ascii_uppercase)) # This function generates the key in diff --git a/ciphers/cryptomath_module.py b/ciphers/cryptomath_module.py index be8764ff38c3..6f15f7b733e6 100644 --- a/ciphers/cryptomath_module.py +++ b/ciphers/cryptomath_module.py @@ -6,7 +6,8 @@ def gcd(a: int, b: int) -> int: def find_mod_inverse(a: int, m: int) -> int: if gcd(a, m) != 1: - raise ValueError(f"mod inverse of {a!r} and {m!r} does not exist") + msg = f"mod inverse of {a!r} and {m!r} does not exist" + raise ValueError(msg) u1, u2, u3 = 1, 0, a v1, v2, v3 = 0, 1, m while v3 != 0: diff --git a/ciphers/enigma_machine2.py b/ciphers/enigma_machine2.py index 07d21893f192..ec0d44e4a6c6 100644 --- a/ciphers/enigma_machine2.py +++ b/ciphers/enigma_machine2.py @@ -87,22 +87,20 @@ def _validator( # Checks if there are 3 unique rotors if (unique_rotsel := len(set(rotsel))) < 3: - raise Exception(f"Please use 3 unique rotors (not {unique_rotsel})") + msg = f"Please use 3 unique rotors (not {unique_rotsel})" + raise Exception(msg) # Checks if rotor positions are valid rotorpos1, rotorpos2, rotorpos3 = rotpos if not 0 < rotorpos1 <= len(abc): - raise ValueError( - "First rotor position is not within range of 1..26 (" f"{rotorpos1}" - ) + msg = f"First rotor position is not within range of 1..26 ({rotorpos1}" + raise ValueError(msg) if not 0 < rotorpos2 <= len(abc): - raise ValueError( - "Second rotor position is not within range of 1..26 (" f"{rotorpos2})" - ) + msg = f"Second rotor position is not within range of 1..26 ({rotorpos2})" + raise ValueError(msg) if not 0 < rotorpos3 <= len(abc): - raise ValueError( - "Third rotor position is not within range of 1..26 (" f"{rotorpos3})" - ) + msg = f"Third rotor position is not within range of 1..26 ({rotorpos3})" + raise ValueError(msg) # Validates string and returns dict pbdict = _plugboard(pb) @@ -130,9 +128,11 @@ def _plugboard(pbstring: str) -> dict[str, str]: # a) is type string # b) has even length (so pairs can be made) if not isinstance(pbstring, str): - raise TypeError(f"Plugboard setting isn't type string ({type(pbstring)})") + msg = f"Plugboard setting isn't type string ({type(pbstring)})" + raise TypeError(msg) elif len(pbstring) % 2 != 0: - raise Exception(f"Odd number of symbols ({len(pbstring)})") + msg = f"Odd number of symbols ({len(pbstring)})" + raise Exception(msg) elif pbstring == "": return {} @@ -142,9 +142,11 @@ def _plugboard(pbstring: str) -> dict[str, str]: tmppbl = set() for i in pbstring: if i not in abc: - raise Exception(f"'{i}' not in list of symbols") + msg = f"'{i}' not in list of symbols" + raise Exception(msg) elif i in tmppbl: - raise Exception(f"Duplicate symbol ({i})") + msg = f"Duplicate symbol ({i})" + raise Exception(msg) else: tmppbl.add(i) del tmppbl diff --git a/ciphers/hill_cipher.py b/ciphers/hill_cipher.py index f646d567b4c8..b4424e82298e 100644 --- a/ciphers/hill_cipher.py +++ b/ciphers/hill_cipher.py @@ -104,10 +104,11 @@ def check_determinant(self) -> None: req_l = len(self.key_string) if greatest_common_divisor(det, len(self.key_string)) != 1: - raise ValueError( - f"determinant modular {req_l} of encryption key({det}) is not co prime " - f"w.r.t {req_l}.\nTry another key." + msg = ( + f"determinant modular {req_l} of encryption key({det}) " + f"is not co prime w.r.t {req_l}.\nTry another key." ) + raise ValueError(msg) def process_text(self, text: str) -> str: """ diff --git a/conversions/astronomical_length_scale_conversion.py b/conversions/astronomical_length_scale_conversion.py index 804d82487a25..0f413644906d 100644 --- a/conversions/astronomical_length_scale_conversion.py +++ b/conversions/astronomical_length_scale_conversion.py @@ -77,15 +77,17 @@ def length_conversion(value: float, from_type: str, to_type: str) -> float: to_sanitized = UNIT_SYMBOL.get(to_sanitized, to_sanitized) if from_sanitized not in METRIC_CONVERSION: - raise ValueError( + msg = ( f"Invalid 'from_type' value: {from_type!r}.\n" f"Conversion abbreviations are: {', '.join(METRIC_CONVERSION)}" ) + raise ValueError(msg) if to_sanitized not in METRIC_CONVERSION: - raise ValueError( + msg = ( f"Invalid 'to_type' value: {to_type!r}.\n" f"Conversion abbreviations are: {', '.join(METRIC_CONVERSION)}" ) + raise ValueError(msg) from_exponent = METRIC_CONVERSION[from_sanitized] to_exponent = METRIC_CONVERSION[to_sanitized] exponent = 1 diff --git a/conversions/length_conversion.py b/conversions/length_conversion.py index 790d9c116845..d8f39515255e 100644 --- a/conversions/length_conversion.py +++ b/conversions/length_conversion.py @@ -104,15 +104,17 @@ def length_conversion(value: float, from_type: str, to_type: str) -> float: new_to = to_type.lower().rstrip("s") new_to = TYPE_CONVERSION.get(new_to, new_to) if new_from not in METRIC_CONVERSION: - raise ValueError( + msg = ( f"Invalid 'from_type' value: {from_type!r}.\n" f"Conversion abbreviations are: {', '.join(METRIC_CONVERSION)}" ) + raise ValueError(msg) if new_to not in METRIC_CONVERSION: - raise ValueError( + msg = ( f"Invalid 'to_type' value: {to_type!r}.\n" f"Conversion abbreviations are: {', '.join(METRIC_CONVERSION)}" ) + raise ValueError(msg) return value * METRIC_CONVERSION[new_from].from_ * METRIC_CONVERSION[new_to].to diff --git a/conversions/speed_conversions.py b/conversions/speed_conversions.py index 62da9e137bc7..ba497119d3f5 100644 --- a/conversions/speed_conversions.py +++ b/conversions/speed_conversions.py @@ -57,10 +57,11 @@ def convert_speed(speed: float, unit_from: str, unit_to: str) -> float: 115.078 """ if unit_to not in speed_chart or unit_from not in speed_chart_inverse: - raise ValueError( + msg = ( f"Incorrect 'from_type' or 'to_type' value: {unit_from!r}, {unit_to!r}\n" f"Valid values are: {', '.join(speed_chart_inverse)}" ) + raise ValueError(msg) return round(speed * speed_chart[unit_from] * speed_chart_inverse[unit_to], 3) diff --git a/conversions/weight_conversion.py b/conversions/weight_conversion.py index 5c032a497a7b..e8326e0b688f 100644 --- a/conversions/weight_conversion.py +++ b/conversions/weight_conversion.py @@ -299,10 +299,11 @@ def weight_conversion(from_type: str, to_type: str, value: float) -> float: 1.999999998903455 """ if to_type not in KILOGRAM_CHART or from_type not in WEIGHT_TYPE_CHART: - raise ValueError( + msg = ( f"Invalid 'from_type' or 'to_type' value: {from_type!r}, {to_type!r}\n" f"Supported values are: {', '.join(WEIGHT_TYPE_CHART)}" ) + raise ValueError(msg) return value * KILOGRAM_CHART[to_type] * WEIGHT_TYPE_CHART[from_type] diff --git a/data_structures/binary_tree/binary_search_tree_recursive.py b/data_structures/binary_tree/binary_search_tree_recursive.py index 97eb8e25bedd..b5b983b9ba4c 100644 --- a/data_structures/binary_tree/binary_search_tree_recursive.py +++ b/data_structures/binary_tree/binary_search_tree_recursive.py @@ -77,7 +77,8 @@ def _put(self, node: Node | None, label: int, parent: Node | None = None) -> Nod elif label > node.label: node.right = self._put(node.right, label, node) else: - raise Exception(f"Node with label {label} already exists") + msg = f"Node with label {label} already exists" + raise Exception(msg) return node @@ -100,7 +101,8 @@ def search(self, label: int) -> Node: def _search(self, node: Node | None, label: int) -> Node: if node is None: - raise Exception(f"Node with label {label} does not exist") + msg = f"Node with label {label} does not exist" + raise Exception(msg) else: if label < node.label: node = self._search(node.left, label) diff --git a/data_structures/binary_tree/binary_tree_mirror.py b/data_structures/binary_tree/binary_tree_mirror.py index 1ef950ad62d7..b8548f4ec515 100644 --- a/data_structures/binary_tree/binary_tree_mirror.py +++ b/data_structures/binary_tree/binary_tree_mirror.py @@ -31,7 +31,8 @@ def binary_tree_mirror(binary_tree: dict, root: int = 1) -> dict: if not binary_tree: raise ValueError("binary tree cannot be empty") if root not in binary_tree: - raise ValueError(f"root {root} is not present in the binary_tree") + msg = f"root {root} is not present in the binary_tree" + raise ValueError(msg) binary_tree_mirror_dictionary = dict(binary_tree) binary_tree_mirror_dict(binary_tree_mirror_dictionary, root) return binary_tree_mirror_dictionary diff --git a/data_structures/disjoint_set/disjoint_set.py b/data_structures/disjoint_set/disjoint_set.py index f8500bf2c3af..12dafb2d935e 100644 --- a/data_structures/disjoint_set/disjoint_set.py +++ b/data_structures/disjoint_set/disjoint_set.py @@ -56,7 +56,8 @@ def find_python_set(node: Node) -> set: for s in sets: if node.data in s: return s - raise ValueError(f"{node.data} is not in {sets}") + msg = f"{node.data} is not in {sets}" + raise ValueError(msg) def test_disjoint_set() -> None: diff --git a/data_structures/linked_list/circular_linked_list.py b/data_structures/linked_list/circular_linked_list.py index 9092fb29e3ff..325d91026137 100644 --- a/data_structures/linked_list/circular_linked_list.py +++ b/data_structures/linked_list/circular_linked_list.py @@ -94,25 +94,25 @@ def test_circular_linked_list() -> None: try: circular_linked_list.delete_front() - raise AssertionError() # This should not happen + raise AssertionError # This should not happen except IndexError: assert True # This should happen try: circular_linked_list.delete_tail() - raise AssertionError() # This should not happen + raise AssertionError # This should not happen except IndexError: assert True # This should happen try: circular_linked_list.delete_nth(-1) - raise AssertionError() + raise AssertionError except IndexError: assert True try: circular_linked_list.delete_nth(0) - raise AssertionError() + raise AssertionError except IndexError: assert True diff --git a/data_structures/linked_list/doubly_linked_list.py b/data_structures/linked_list/doubly_linked_list.py index 69763d12da15..1a6c48191c4e 100644 --- a/data_structures/linked_list/doubly_linked_list.py +++ b/data_structures/linked_list/doubly_linked_list.py @@ -198,13 +198,13 @@ def test_doubly_linked_list() -> None: try: linked_list.delete_head() - raise AssertionError() # This should not happen. + raise AssertionError # This should not happen. except IndexError: assert True # This should happen. try: linked_list.delete_tail() - raise AssertionError() # This should not happen. + raise AssertionError # This should not happen. except IndexError: assert True # This should happen. diff --git a/data_structures/linked_list/singly_linked_list.py b/data_structures/linked_list/singly_linked_list.py index a8f9e8ebb977..890e21c9b404 100644 --- a/data_structures/linked_list/singly_linked_list.py +++ b/data_structures/linked_list/singly_linked_list.py @@ -353,13 +353,13 @@ def test_singly_linked_list() -> None: try: linked_list.delete_head() - raise AssertionError() # This should not happen. + raise AssertionError # This should not happen. except IndexError: assert True # This should happen. try: linked_list.delete_tail() - raise AssertionError() # This should not happen. + raise AssertionError # This should not happen. except IndexError: assert True # This should happen. diff --git a/data_structures/stacks/stack.py b/data_structures/stacks/stack.py index 55d424d5018b..a14f4648a399 100644 --- a/data_structures/stacks/stack.py +++ b/data_structures/stacks/stack.py @@ -92,13 +92,13 @@ def test_stack() -> None: try: _ = stack.pop() - raise AssertionError() # This should not happen + raise AssertionError # This should not happen except StackUnderflowError: assert True # This should happen try: _ = stack.peek() - raise AssertionError() # This should not happen + raise AssertionError # This should not happen except StackUnderflowError: assert True # This should happen @@ -118,7 +118,7 @@ def test_stack() -> None: try: stack.push(200) - raise AssertionError() # This should not happen + raise AssertionError # This should not happen except StackOverflowError: assert True # This should happen diff --git a/digital_image_processing/dithering/burkes.py b/digital_image_processing/dithering/burkes.py index 2bf0bbe03225..0804104abe58 100644 --- a/digital_image_processing/dithering/burkes.py +++ b/digital_image_processing/dithering/burkes.py @@ -21,7 +21,8 @@ def __init__(self, input_img, threshold: int): self.max_threshold = int(self.get_greyscale(255, 255, 255)) if not self.min_threshold < threshold < self.max_threshold: - raise ValueError(f"Factor value should be from 0 to {self.max_threshold}") + msg = f"Factor value should be from 0 to {self.max_threshold}" + raise ValueError(msg) self.input_img = input_img self.threshold = threshold diff --git a/divide_and_conquer/convex_hull.py b/divide_and_conquer/convex_hull.py index 39e78be04a71..1ad933417da6 100644 --- a/divide_and_conquer/convex_hull.py +++ b/divide_and_conquer/convex_hull.py @@ -174,12 +174,12 @@ def _validate_input(points: list[Point] | list[list[float]]) -> list[Point]: """ if not hasattr(points, "__iter__"): - raise ValueError( - f"Expecting an iterable object but got an non-iterable type {points}" - ) + msg = f"Expecting an iterable object but got an non-iterable type {points}" + raise ValueError(msg) if not points: - raise ValueError(f"Expecting a list of points but got {points}") + msg = f"Expecting a list of points but got {points}" + raise ValueError(msg) return _construct_points(points) diff --git a/dynamic_programming/knapsack.py b/dynamic_programming/knapsack.py index b12d30313e31..489b5ada450a 100644 --- a/dynamic_programming/knapsack.py +++ b/dynamic_programming/knapsack.py @@ -78,17 +78,18 @@ def knapsack_with_example_solution(w: int, wt: list, val: list): num_items = len(wt) if num_items != len(val): - raise ValueError( - "The number of weights must be the " - "same as the number of values.\nBut " - f"got {num_items} weights and {len(val)} values" + msg = ( + "The number of weights must be the same as the number of values.\n" + f"But got {num_items} weights and {len(val)} values" ) + raise ValueError(msg) for i in range(num_items): if not isinstance(wt[i], int): - raise TypeError( - "All weights must be integers but " - f"got weight of type {type(wt[i])} at index {i}" + msg = ( + "All weights must be integers but got weight of " + f"type {type(wt[i])} at index {i}" ) + raise TypeError(msg) optimal_val, dp_table = knapsack(w, wt, val, num_items) example_optional_set: set = set() diff --git a/dynamic_programming/minimum_steps_to_one.py b/dynamic_programming/minimum_steps_to_one.py index f4eb7033dd20..8785027fbff3 100644 --- a/dynamic_programming/minimum_steps_to_one.py +++ b/dynamic_programming/minimum_steps_to_one.py @@ -42,7 +42,8 @@ def min_steps_to_one(number: int) -> int: """ if number <= 0: - raise ValueError(f"n must be greater than 0. Got n = {number}") + msg = f"n must be greater than 0. Got n = {number}" + raise ValueError(msg) table = [number + 1] * (number + 1) diff --git a/dynamic_programming/rod_cutting.py b/dynamic_programming/rod_cutting.py index 79104d8f4044..f80fa440ae86 100644 --- a/dynamic_programming/rod_cutting.py +++ b/dynamic_programming/rod_cutting.py @@ -177,13 +177,15 @@ def _enforce_args(n: int, prices: list): the rod """ if n < 0: - raise ValueError(f"n must be greater than or equal to 0. Got n = {n}") + msg = f"n must be greater than or equal to 0. Got n = {n}" + raise ValueError(msg) if n > len(prices): - raise ValueError( - "Each integral piece of rod must have a corresponding " - f"price. Got n = {n} but length of prices = {len(prices)}" + msg = ( + "Each integral piece of rod must have a corresponding price. " + f"Got n = {n} but length of prices = {len(prices)}" ) + raise ValueError(msg) def main(): diff --git a/dynamic_programming/viterbi.py b/dynamic_programming/viterbi.py index 93ab845e2ae8..764d45dc2c05 100644 --- a/dynamic_programming/viterbi.py +++ b/dynamic_programming/viterbi.py @@ -297,11 +297,13 @@ def _validate_list(_object: Any, var_name: str) -> None: """ if not isinstance(_object, list): - raise ValueError(f"{var_name} must be a list") + msg = f"{var_name} must be a list" + raise ValueError(msg) else: for x in _object: if not isinstance(x, str): - raise ValueError(f"{var_name} must be a list of strings") + msg = f"{var_name} must be a list of strings" + raise ValueError(msg) def _validate_dicts( @@ -384,14 +386,15 @@ def _validate_dict( ValueError: mock_name nested dictionary all values must be float """ if not isinstance(_object, dict): - raise ValueError(f"{var_name} must be a dict") + msg = f"{var_name} must be a dict" + raise ValueError(msg) if not all(isinstance(x, str) for x in _object): - raise ValueError(f"{var_name} all keys must be strings") + msg = f"{var_name} all keys must be strings" + raise ValueError(msg) if not all(isinstance(x, value_type) for x in _object.values()): nested_text = "nested dictionary " if nested else "" - raise ValueError( - f"{var_name} {nested_text}all values must be {value_type.__name__}" - ) + msg = f"{var_name} {nested_text}all values must be {value_type.__name__}" + raise ValueError(msg) if __name__ == "__main__": diff --git a/electronics/resistor_equivalence.py b/electronics/resistor_equivalence.py index 7142f838a065..55e7f2d6b5d2 100644 --- a/electronics/resistor_equivalence.py +++ b/electronics/resistor_equivalence.py @@ -23,7 +23,8 @@ def resistor_parallel(resistors: list[float]) -> float: index = 0 for resistor in resistors: if resistor <= 0: - raise ValueError(f"Resistor at index {index} has a negative or zero value!") + msg = f"Resistor at index {index} has a negative or zero value!" + raise ValueError(msg) first_sum += 1 / float(resistor) index += 1 return 1 / first_sum @@ -47,7 +48,8 @@ def resistor_series(resistors: list[float]) -> float: for resistor in resistors: sum_r += resistor if resistor < 0: - raise ValueError(f"Resistor at index {index} has a negative value!") + msg = f"Resistor at index {index} has a negative value!" + raise ValueError(msg) index += 1 return sum_r diff --git a/genetic_algorithm/basic_string.py b/genetic_algorithm/basic_string.py index 388e7219f54b..089c5c99a1ec 100644 --- a/genetic_algorithm/basic_string.py +++ b/genetic_algorithm/basic_string.py @@ -96,13 +96,13 @@ def basic(target: str, genes: list[str], debug: bool = True) -> tuple[int, int, # Verify if N_POPULATION is bigger than N_SELECTED if N_POPULATION < N_SELECTED: - raise ValueError(f"{N_POPULATION} must be bigger than {N_SELECTED}") + msg = f"{N_POPULATION} must be bigger than {N_SELECTED}" + raise ValueError(msg) # Verify that the target contains no genes besides the ones inside genes variable. not_in_genes_list = sorted({c for c in target if c not in genes}) if not_in_genes_list: - raise ValueError( - f"{not_in_genes_list} is not in genes list, evolution cannot converge" - ) + msg = f"{not_in_genes_list} is not in genes list, evolution cannot converge" + raise ValueError(msg) # Generate random starting population. population = [] diff --git a/graphics/vector3_for_2d_rendering.py b/graphics/vector3_for_2d_rendering.py index dfa22262a8d8..a332206e67b6 100644 --- a/graphics/vector3_for_2d_rendering.py +++ b/graphics/vector3_for_2d_rendering.py @@ -28,9 +28,8 @@ def convert_to_2d( TypeError: Input values must either be float or int: ['1', 2, 3, 10, 10] """ if not all(isinstance(val, (float, int)) for val in locals().values()): - raise TypeError( - "Input values must either be float or int: " f"{list(locals().values())}" - ) + msg = f"Input values must either be float or int: {list(locals().values())}" + raise TypeError(msg) projected_x = ((x * distance) / (z + distance)) * scale projected_y = ((y * distance) / (z + distance)) * scale return projected_x, projected_y @@ -71,10 +70,11 @@ def rotate( input_variables = locals() del input_variables["axis"] if not all(isinstance(val, (float, int)) for val in input_variables.values()): - raise TypeError( + msg = ( "Input values except axis must either be float or int: " f"{list(input_variables.values())}" ) + raise TypeError(msg) angle = (angle % 360) / 450 * 180 / math.pi if axis == "z": new_x = x * math.cos(angle) - y * math.sin(angle) diff --git a/graphs/breadth_first_search_shortest_path.py b/graphs/breadth_first_search_shortest_path.py index cb21076f91d2..d489b110b3a7 100644 --- a/graphs/breadth_first_search_shortest_path.py +++ b/graphs/breadth_first_search_shortest_path.py @@ -73,9 +73,10 @@ def shortest_path(self, target_vertex: str) -> str: target_vertex_parent = self.parent.get(target_vertex) if target_vertex_parent is None: - raise ValueError( + msg = ( f"No path from vertex: {self.source_vertex} to vertex: {target_vertex}" ) + raise ValueError(msg) return self.shortest_path(target_vertex_parent) + f"->{target_vertex}" diff --git a/linear_algebra/src/schur_complement.py b/linear_algebra/src/schur_complement.py index 3a5f4443afd3..750f4de5e397 100644 --- a/linear_algebra/src/schur_complement.py +++ b/linear_algebra/src/schur_complement.py @@ -31,16 +31,18 @@ def schur_complement( shape_c = np.shape(mat_c) if shape_a[0] != shape_b[0]: - raise ValueError( - f"Expected the same number of rows for A and B. \ - Instead found A of size {shape_a} and B of size {shape_b}" + msg = ( + "Expected the same number of rows for A and B. " + f"Instead found A of size {shape_a} and B of size {shape_b}" ) + raise ValueError(msg) if shape_b[1] != shape_c[1]: - raise ValueError( - f"Expected the same number of columns for B and C. \ - Instead found B of size {shape_b} and C of size {shape_c}" + msg = ( + "Expected the same number of columns for B and C. " + f"Instead found B of size {shape_b} and C of size {shape_c}" ) + raise ValueError(msg) a_inv = pseudo_inv if a_inv is None: diff --git a/machine_learning/similarity_search.py b/machine_learning/similarity_search.py index 72979181f67c..7a23ec463c8f 100644 --- a/machine_learning/similarity_search.py +++ b/machine_learning/similarity_search.py @@ -97,26 +97,29 @@ def similarity_search( """ if dataset.ndim != value_array.ndim: - raise ValueError( - f"Wrong input data's dimensions... dataset : {dataset.ndim}, " - f"value_array : {value_array.ndim}" + msg = ( + "Wrong input data's dimensions... " + f"dataset : {dataset.ndim}, value_array : {value_array.ndim}" ) + raise ValueError(msg) try: if dataset.shape[1] != value_array.shape[1]: - raise ValueError( - f"Wrong input data's shape... dataset : {dataset.shape[1]}, " - f"value_array : {value_array.shape[1]}" + msg = ( + "Wrong input data's shape... " + f"dataset : {dataset.shape[1]}, value_array : {value_array.shape[1]}" ) + raise ValueError(msg) except IndexError: if dataset.ndim != value_array.ndim: raise TypeError("Wrong shape") if dataset.dtype != value_array.dtype: - raise TypeError( - f"Input data have different datatype... dataset : {dataset.dtype}, " - f"value_array : {value_array.dtype}" + msg = ( + "Input data have different datatype... " + f"dataset : {dataset.dtype}, value_array : {value_array.dtype}" ) + raise TypeError(msg) answer = [] diff --git a/machine_learning/support_vector_machines.py b/machine_learning/support_vector_machines.py index df854cc850b1..24046115ebc4 100644 --- a/machine_learning/support_vector_machines.py +++ b/machine_learning/support_vector_machines.py @@ -74,7 +74,8 @@ def __init__( # sklear: def_gamma = 1/(n_features * X.var()) (wiki) # previously it was 1/(n_features) else: - raise ValueError(f"Unknown kernel: {kernel}") + msg = f"Unknown kernel: {kernel}" + raise ValueError(msg) # kernels def __linear(self, vector1: ndarray, vector2: ndarray) -> float: diff --git a/maths/3n_plus_1.py b/maths/3n_plus_1.py index 59fdec48e100..f9f6dfeb9faa 100644 --- a/maths/3n_plus_1.py +++ b/maths/3n_plus_1.py @@ -9,9 +9,11 @@ def n31(a: int) -> tuple[list[int], int]: """ if not isinstance(a, int): - raise TypeError(f"Must be int, not {type(a).__name__}") + msg = f"Must be int, not {type(a).__name__}" + raise TypeError(msg) if a < 1: - raise ValueError(f"Given integer must be positive, not {a}") + msg = f"Given integer must be positive, not {a}" + raise ValueError(msg) path = [a] while a != 1: diff --git a/maths/automorphic_number.py b/maths/automorphic_number.py index 103fc7301831..8ed9375632a4 100644 --- a/maths/automorphic_number.py +++ b/maths/automorphic_number.py @@ -40,7 +40,8 @@ def is_automorphic_number(number: int) -> bool: TypeError: Input value of [number=5.0] must be an integer """ if not isinstance(number, int): - raise TypeError(f"Input value of [number={number}] must be an integer") + msg = f"Input value of [number={number}] must be an integer" + raise TypeError(msg) if number < 0: return False number_square = number * number diff --git a/maths/catalan_number.py b/maths/catalan_number.py index 85607dc1eca4..20c2cfb17c06 100644 --- a/maths/catalan_number.py +++ b/maths/catalan_number.py @@ -31,10 +31,12 @@ def catalan(number: int) -> int: """ if not isinstance(number, int): - raise TypeError(f"Input value of [number={number}] must be an integer") + msg = f"Input value of [number={number}] must be an integer" + raise TypeError(msg) if number < 1: - raise ValueError(f"Input value of [number={number}] must be > 0") + msg = f"Input value of [number={number}] must be > 0" + raise ValueError(msg) current_number = 1 diff --git a/maths/dual_number_automatic_differentiation.py b/maths/dual_number_automatic_differentiation.py index 9aa75830c4a1..f98997c8be4d 100644 --- a/maths/dual_number_automatic_differentiation.py +++ b/maths/dual_number_automatic_differentiation.py @@ -71,7 +71,7 @@ def __truediv__(self, other): for i in self.duals: new_duals.append(i / other) return Dual(self.real / other, new_duals) - raise ValueError() + raise ValueError def __floordiv__(self, other): if not isinstance(other, Dual): @@ -79,7 +79,7 @@ def __floordiv__(self, other): for i in self.duals: new_duals.append(i // other) return Dual(self.real // other, new_duals) - raise ValueError() + raise ValueError def __pow__(self, n): if n < 0 or isinstance(n, float): diff --git a/maths/hexagonal_number.py b/maths/hexagonal_number.py index 28735c638f80..3677ab95ee00 100644 --- a/maths/hexagonal_number.py +++ b/maths/hexagonal_number.py @@ -36,7 +36,8 @@ def hexagonal(number: int) -> int: TypeError: Input value of [number=11.0] must be an integer """ if not isinstance(number, int): - raise TypeError(f"Input value of [number={number}] must be an integer") + msg = f"Input value of [number={number}] must be an integer" + raise TypeError(msg) if number < 1: raise ValueError("Input must be a positive integer") return number * (2 * number - 1) diff --git a/maths/juggler_sequence.py b/maths/juggler_sequence.py index 9daba8bc0e8a..7f65d1dff925 100644 --- a/maths/juggler_sequence.py +++ b/maths/juggler_sequence.py @@ -40,9 +40,11 @@ def juggler_sequence(number: int) -> list[int]: ValueError: Input value of [number=-1] must be a positive integer """ if not isinstance(number, int): - raise TypeError(f"Input value of [number={number}] must be an integer") + msg = f"Input value of [number={number}] must be an integer" + raise TypeError(msg) if number < 1: - raise ValueError(f"Input value of [number={number}] must be a positive integer") + msg = f"Input value of [number={number}] must be a positive integer" + raise ValueError(msg) sequence = [number] while number != 1: if number % 2 == 0: diff --git a/maths/liouville_lambda.py b/maths/liouville_lambda.py index 5993efa42d66..1ed228dd5434 100644 --- a/maths/liouville_lambda.py +++ b/maths/liouville_lambda.py @@ -33,7 +33,8 @@ def liouville_lambda(number: int) -> int: TypeError: Input value of [number=11.0] must be an integer """ if not isinstance(number, int): - raise TypeError(f"Input value of [number={number}] must be an integer") + msg = f"Input value of [number={number}] must be an integer" + raise TypeError(msg) if number < 1: raise ValueError("Input must be a positive integer") return -1 if len(prime_factors(number)) % 2 else 1 diff --git a/maths/manhattan_distance.py b/maths/manhattan_distance.py index 2711d4c8ccd6..413991468a49 100644 --- a/maths/manhattan_distance.py +++ b/maths/manhattan_distance.py @@ -15,15 +15,15 @@ def manhattan_distance(point_a: list, point_b: list) -> float: 9.0 >>> manhattan_distance([1,1], None) Traceback (most recent call last): - ... + ... ValueError: Missing an input >>> manhattan_distance([1,1], [2, 2, 2]) Traceback (most recent call last): - ... + ... ValueError: Both points must be in the same n-dimensional space >>> manhattan_distance([1,"one"], [2, 2, 2]) Traceback (most recent call last): - ... + ... TypeError: Expected a list of numbers as input, found str >>> manhattan_distance(1, [2, 2, 2]) Traceback (most recent call last): @@ -66,14 +66,14 @@ def _validate_point(point: list[float]) -> None: if isinstance(point, list): for item in point: if not isinstance(item, (int, float)): - raise TypeError( - f"Expected a list of numbers as input, " - f"found {type(item).__name__}" + msg = ( + "Expected a list of numbers as input, found " + f"{type(item).__name__}" ) + raise TypeError(msg) else: - raise TypeError( - f"Expected a list of numbers as input, found {type(point).__name__}" - ) + msg = f"Expected a list of numbers as input, found {type(point).__name__}" + raise TypeError(msg) else: raise ValueError("Missing an input") diff --git a/maths/pronic_number.py b/maths/pronic_number.py index 8b554dbbd602..cf4d3d2eb24b 100644 --- a/maths/pronic_number.py +++ b/maths/pronic_number.py @@ -41,7 +41,8 @@ def is_pronic(number: int) -> bool: TypeError: Input value of [number=6.0] must be an integer """ if not isinstance(number, int): - raise TypeError(f"Input value of [number={number}] must be an integer") + msg = f"Input value of [number={number}] must be an integer" + raise TypeError(msg) if number < 0 or number % 2 == 1: return False number_sqrt = int(number**0.5) diff --git a/maths/proth_number.py b/maths/proth_number.py index ce911473a2d2..47747ed260f7 100644 --- a/maths/proth_number.py +++ b/maths/proth_number.py @@ -29,10 +29,12 @@ def proth(number: int) -> int: """ if not isinstance(number, int): - raise TypeError(f"Input value of [number={number}] must be an integer") + msg = f"Input value of [number={number}] must be an integer" + raise TypeError(msg) if number < 1: - raise ValueError(f"Input value of [number={number}] must be > 0") + msg = f"Input value of [number={number}] must be > 0" + raise ValueError(msg) elif number == 1: return 3 elif number == 2: diff --git a/maths/radix2_fft.py b/maths/radix2_fft.py index af98f24f9538..2c5cdc004d1d 100644 --- a/maths/radix2_fft.py +++ b/maths/radix2_fft.py @@ -167,7 +167,7 @@ def __str__(self): f"{coef}*x^{i}" for coef, i in enumerate(self.product) ) - return "\n".join((a, b, c)) + return f"{a}\n{b}\n{c}" # Unit tests diff --git a/maths/sieve_of_eratosthenes.py b/maths/sieve_of_eratosthenes.py index 3cd6ce0b4d9d..a0520aa5cf50 100644 --- a/maths/sieve_of_eratosthenes.py +++ b/maths/sieve_of_eratosthenes.py @@ -34,7 +34,8 @@ def prime_sieve(num: int) -> list[int]: """ if num <= 0: - raise ValueError(f"{num}: Invalid input, please enter a positive integer.") + msg = f"{num}: Invalid input, please enter a positive integer." + raise ValueError(msg) sieve = [True] * (num + 1) prime = [] diff --git a/maths/sylvester_sequence.py b/maths/sylvester_sequence.py index 114c9dd58582..607424c6a90b 100644 --- a/maths/sylvester_sequence.py +++ b/maths/sylvester_sequence.py @@ -31,7 +31,8 @@ def sylvester(number: int) -> int: if number == 1: return 2 elif number < 1: - raise ValueError(f"The input value of [n={number}] has to be > 0") + msg = f"The input value of [n={number}] has to be > 0" + raise ValueError(msg) else: num = sylvester(number - 1) lower = num - 1 diff --git a/maths/twin_prime.py b/maths/twin_prime.py index e6ac0cc7805b..912b10b366c0 100644 --- a/maths/twin_prime.py +++ b/maths/twin_prime.py @@ -32,7 +32,8 @@ def twin_prime(number: int) -> int: TypeError: Input value of [number=6.0] must be an integer """ if not isinstance(number, int): - raise TypeError(f"Input value of [number={number}] must be an integer") + msg = f"Input value of [number={number}] must be an integer" + raise TypeError(msg) if is_prime(number) and is_prime(number + 2): return number + 2 else: diff --git a/matrix/matrix_operation.py b/matrix/matrix_operation.py index 576094902af4..f189f1898d33 100644 --- a/matrix/matrix_operation.py +++ b/matrix/matrix_operation.py @@ -70,10 +70,11 @@ def multiply(matrix_a: list[list[int]], matrix_b: list[list[int]]) -> list[list[ rows, cols = _verify_matrix_sizes(matrix_a, matrix_b) if cols[0] != rows[1]: - raise ValueError( - f"Cannot multiply matrix of dimensions ({rows[0]},{cols[0]}) " - f"and ({rows[1]},{cols[1]})" + msg = ( + "Cannot multiply matrix of dimensions " + f"({rows[0]},{cols[0]}) and ({rows[1]},{cols[1]})" ) + raise ValueError(msg) return [ [sum(m * n for m, n in zip(i, j)) for j in zip(*matrix_b)] for i in matrix_a ] @@ -174,10 +175,11 @@ def _verify_matrix_sizes( ) -> tuple[tuple[int, int], tuple[int, int]]: shape = _shape(matrix_a) + _shape(matrix_b) if shape[0] != shape[3] or shape[1] != shape[2]: - raise ValueError( - f"operands could not be broadcast together with shape " + msg = ( + "operands could not be broadcast together with shape " f"({shape[0], shape[1]}), ({shape[2], shape[3]})" ) + raise ValueError(msg) return (shape[0], shape[2]), (shape[1], shape[3]) diff --git a/matrix/sherman_morrison.py b/matrix/sherman_morrison.py index 39eddfed81f3..256271e8a87d 100644 --- a/matrix/sherman_morrison.py +++ b/matrix/sherman_morrison.py @@ -173,7 +173,8 @@ def __mul__(self, another: int | float | Matrix) -> Matrix: result[r, c] += self[r, i] * another[i, c] return result else: - raise TypeError(f"Unsupported type given for another ({type(another)})") + msg = f"Unsupported type given for another ({type(another)})" + raise TypeError(msg) def transpose(self) -> Matrix: """ diff --git a/neural_network/input_data.py b/neural_network/input_data.py index 2a32f0b82c37..94c018ece9ba 100644 --- a/neural_network/input_data.py +++ b/neural_network/input_data.py @@ -198,10 +198,7 @@ def next_batch(self, batch_size, fake_data=False, shuffle=True): """Return the next `batch_size` examples from this data set.""" if fake_data: fake_image = [1] * 784 - if self.one_hot: - fake_label = [1] + [0] * 9 - else: - fake_label = 0 + fake_label = [1] + [0] * 9 if self.one_hot else 0 return ( [fake_image for _ in range(batch_size)], [fake_label for _ in range(batch_size)], @@ -324,10 +321,11 @@ def fake(): test_labels = _extract_labels(f, one_hot=one_hot) if not 0 <= validation_size <= len(train_images): - raise ValueError( - f"Validation size should be between 0 and {len(train_images)}. " - f"Received: {validation_size}." + msg = ( + "Validation size should be between 0 and " + f"{len(train_images)}. Received: {validation_size}." ) + raise ValueError(msg) validation_images = train_images[:validation_size] validation_labels = train_labels[:validation_size] diff --git a/other/nested_brackets.py b/other/nested_brackets.py index ea48c0a5f532..19c6dd53c8b2 100644 --- a/other/nested_brackets.py +++ b/other/nested_brackets.py @@ -18,7 +18,7 @@ def is_balanced(s): stack = [] open_brackets = set({"(", "[", "{"}) closed_brackets = set({")", "]", "}"}) - open_to_closed = dict({"{": "}", "[": "]", "(": ")"}) + open_to_closed = {"{": "}", "[": "]", "(": ")"} for i in range(len(s)): if s[i] in open_brackets: diff --git a/other/scoring_algorithm.py b/other/scoring_algorithm.py index 8e04a8f30dd7..af04f432e433 100644 --- a/other/scoring_algorithm.py +++ b/other/scoring_algorithm.py @@ -68,7 +68,8 @@ def calculate_each_score( # weight not 0 or 1 else: - raise ValueError(f"Invalid weight of {weight:f} provided") + msg = f"Invalid weight of {weight:f} provided" + raise ValueError(msg) score_lists.append(score) diff --git a/project_euler/problem_054/sol1.py b/project_euler/problem_054/sol1.py index 9af7aef5a716..74409f32c712 100644 --- a/project_euler/problem_054/sol1.py +++ b/project_euler/problem_054/sol1.py @@ -119,10 +119,12 @@ def __init__(self, hand: str) -> None: For example: "6S 4C KC AS TH" """ if not isinstance(hand, str): - raise TypeError(f"Hand should be of type 'str': {hand!r}") + msg = f"Hand should be of type 'str': {hand!r}" + raise TypeError(msg) # 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}") + msg = f"Hand should contain only 5 cards: {hand!r}" + raise ValueError(msg) self._hand = hand self._first_pair = 0 self._second_pair = 0 diff --git a/project_euler/problem_068/sol1.py b/project_euler/problem_068/sol1.py index 772be359f630..cf814b001d57 100644 --- a/project_euler/problem_068/sol1.py +++ b/project_euler/problem_068/sol1.py @@ -73,7 +73,8 @@ def solution(gon_side: int = 5) -> int: if is_magic_gon(numbers): return int("".join(str(n) for n in numbers)) - raise ValueError(f"Magic {gon_side}-gon ring is impossible") + msg = f"Magic {gon_side}-gon ring is impossible" + raise ValueError(msg) def generate_gon_ring(gon_side: int, perm: list[int]) -> list[int]: diff --git a/project_euler/problem_131/sol1.py b/project_euler/problem_131/sol1.py index f5302aac8644..be3ea9c81ae4 100644 --- a/project_euler/problem_131/sol1.py +++ b/project_euler/problem_131/sol1.py @@ -26,10 +26,7 @@ def is_prime(number: int) -> bool: False """ - for divisor in range(2, isqrt(number) + 1): - if number % divisor == 0: - return False - return True + return all(number % divisor != 0 for divisor in range(2, isqrt(number) + 1)) def solution(max_prime: int = 10**6) -> int: diff --git a/pyproject.toml b/pyproject.toml index 48c3fbd4009d..a526196685f5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,45 +17,88 @@ ignore-words-list = "3rt,ans,crate,damon,fo,followings,hist,iff,kwanza,mater,sec skip = "./.*,*.json,ciphers/prehistoric_men.txt,project_euler/problem_022/p022_names.txt,pyproject.toml,strings/dictionary.txt,strings/words.txt" [tool.ruff] -ignore = [ # `ruff rule S101` for a description of that rule - "B904", # B904: Within an `except` clause, raise exceptions with `raise ... from err` - "B905", # B905: `zip()` without an explicit `strict=` parameter - "E741", # E741: Ambiguous variable name 'l' - "G004", # G004 Logging statement uses f-string - "N999", # N999: Invalid module name - "PLC1901", # PLC1901: `{}` can be simplified to `{}` as an empty string is falsey - "PLR2004", # PLR2004: Magic value used in comparison - "PLR5501", # PLR5501: Consider using `elif` instead of `else` - "PLW0120", # PLW0120: `else` clause on loop without a `break` statement - "PLW060", # PLW060: Using global for `{name}` but no assignment is done -- DO NOT FIX - "PLW2901", # PLW2901: Redefined loop variable - "RUF00", # RUF00: Ambiguous unicode character -- DO NOT FIX - "RUF100", # RUF100: Unused `noqa` directive - "S101", # S101: Use of `assert` detected -- DO NOT FIX - "S105", # S105: Possible hardcoded password: 'password' - "S113", # S113: Probable use of requests call without timeout - "S311", # S311: Standard pseudo-random generators are not suitable for cryptographic purposes - "UP038", # UP038: Use `X | Y` in `{}` call instead of `(X, Y)` -- DO NOT FIX +ignore = [ # `ruff rule S101` for a description of that rule + "ARG001", # Unused function argument `amount` -- FIX ME? + "B904", # Within an `except` clause, raise exceptions with `raise ... from err` -- FIX ME + "B905", # `zip()` without an explicit `strict=` parameter -- FIX ME + "DTZ001", # The use of `datetime.datetime()` without `tzinfo` argument is not allowed -- FIX ME + "DTZ005", # The use of `datetime.datetime.now()` without `tzinfo` argument is not allowed -- FIX ME + "E741", # Ambiguous variable name 'l' -- FIX ME + "EM101", # Exception must not use a string literal, assign to variable first + "EXE001", # Shebang is present but file is not executable" -- FIX ME + "G004", # Logging statement uses f-string + "ICN001", # `matplotlib.pyplot` should be imported as `plt` -- FIX ME + "INP001", # File `x/y/z.py` is part of an implicit namespace package. Add an `__init__.py`. -- FIX ME + "N999", # Invalid module name -- FIX ME + "NPY002", # Replace legacy `np.random.choice` call with `np.random.Generator` -- FIX ME + "PGH003", # Use specific rule codes when ignoring type issues -- FIX ME + "PLC1901", # `{}` can be simplified to `{}` as an empty string is falsey + "PLR5501", # Consider using `elif` instead of `else` -- FIX ME + "PLW0120", # `else` clause on loop without a `break` statement -- FIX ME + "PLW060", # Using global for `{name}` but no assignment is done -- DO NOT FIX + "PLW2901", # PLW2901: Redefined loop variable -- FIX ME + "RUF00", # Ambiguous unicode character and other rules + "RUF100", # Unused `noqa` directive -- FIX ME + "S101", # Use of `assert` detected -- DO NOT FIX + "S105", # Possible hardcoded password: 'password' + "S113", # Probable use of requests call without timeout -- FIX ME + "S311", # Standard pseudo-random generators are not suitable for cryptographic purposes -- FIX ME + "SIM102", # Use a single `if` statement instead of nested `if` statements -- FIX ME + "SLF001", # Private member accessed: `_Iterator` -- FIX ME + "UP038", # Use `X | Y` in `{}` call instead of `(X, Y)` -- DO NOT FIX ] -select = [ # https://beta.ruff.rs/docs/rules - "A", # A: builtins - "B", # B: bugbear - "C40", # C40: comprehensions - "C90", # C90: mccabe code complexity - "E", # E: pycodestyle errors - "F", # F: pyflakes - "G", # G: logging format - "I", # I: isort - "N", # N: pep8 naming - "PL", # PL: pylint - "PIE", # PIE: pie - "PYI", # PYI: type hinting stub files - "RUF", # RUF: ruff - "S", # S: bandit - "TID", # TID: tidy imports - "UP", # UP: pyupgrade - "W", # W: pycodestyle warnings - "YTT", # YTT: year 2020 +select = [ # https://beta.ruff.rs/docs/rules + "A", # flake8-builtins + "ARG", # flake8-unused-arguments + "ASYNC", # flake8-async + "B", # flake8-bugbear + "BLE", # flake8-blind-except + "C4", # flake8-comprehensions + "C90", # McCabe cyclomatic complexity + "DTZ", # flake8-datetimez + "E", # pycodestyle + "EM", # flake8-errmsg + "EXE", # flake8-executable + "F", # Pyflakes + "FA", # flake8-future-annotations + "FLY", # flynt + "G", # flake8-logging-format + "I", # isort + "ICN", # flake8-import-conventions + "INP", # flake8-no-pep420 + "INT", # flake8-gettext + "N", # pep8-naming + "NPY", # NumPy-specific rules + "PGH", # pygrep-hooks + "PIE", # flake8-pie + "PL", # Pylint + "PYI", # flake8-pyi + "RSE", # flake8-raise + "RUF", # Ruff-specific rules + "S", # flake8-bandit + "SIM", # flake8-simplify + "SLF", # flake8-self + "T10", # flake8-debugger + "TD", # flake8-todos + "TID", # flake8-tidy-imports + "UP", # pyupgrade + "W", # pycodestyle + "YTT", # flake8-2020 + # "ANN", # flake8-annotations # FIX ME? + # "COM", # flake8-commas + # "D", # pydocstyle -- FIX ME? + # "DJ", # flake8-django + # "ERA", # eradicate -- DO NOT FIX + # "FBT", # flake8-boolean-trap # FIX ME + # "ISC", # flake8-implicit-str-concat # FIX ME + # "PD", # pandas-vet + # "PT", # flake8-pytest-style + # "PTH", # flake8-use-pathlib # FIX ME + # "Q", # flake8-quotes + # "RET", # flake8-return # FIX ME? + # "T20", # flake8-print + # "TCH", # flake8-type-checking + # "TRY", # tryceratops ] show-source = true target-version = "py311" @@ -63,7 +106,27 @@ target-version = "py311" [tool.ruff.mccabe] # DO NOT INCREASE THIS VALUE max-complexity = 17 # default: 10 +[tool.ruff.per-file-ignores] +"arithmetic_analysis/newton_raphson.py" = ["PGH001"] +"audio_filters/show_response.py" = ["ARG002"] +"data_structures/binary_tree/binary_search_tree_recursive.py" = ["BLE001"] +"data_structures/binary_tree/treap.py" = ["SIM114"] +"data_structures/hashing/hash_table.py" = ["ARG002"] +"data_structures/hashing/quadratic_probing.py" = ["ARG002"] +"data_structures/hashing/tests/test_hash_map.py" = ["BLE001"] +"data_structures/heap/max_heap.py" = ["SIM114"] +"graphs/minimum_spanning_tree_prims.py" = ["SIM114"] +"hashes/enigma_machine.py" = ["BLE001"] +"machine_learning/decision_tree.py" = ["SIM114"] +"machine_learning/linear_discriminant_analysis.py" = ["ARG005"] +"machine_learning/sequential_minimum_optimization.py" = ["SIM115"] +"matrix/sherman_morrison.py" = ["SIM103", "SIM114"] +"physics/newtons_second_law_of_motion.py" = ["BLE001"] +"project_euler/problem_099/sol1.py" = ["SIM115"] +"sorts/external_sort.py" = ["SIM115"] + [tool.ruff.pylint] # DO NOT INCREASE THESE VALUES +allow-magic-value-types = ["float", "int", "str"] max-args = 10 # default: 5 max-branches = 20 # default: 12 max-returns = 8 # default: 6 diff --git a/scripts/build_directory_md.py b/scripts/build_directory_md.py index b95be9ebc254..24bc00cd036f 100755 --- a/scripts/build_directory_md.py +++ b/scripts/build_directory_md.py @@ -33,7 +33,7 @@ def print_directory_md(top_dir: str = ".") -> None: if filepath != old_path: old_path = print_path(old_path, filepath) indent = (filepath.count(os.sep) + 1) if filepath else 0 - url = "/".join((filepath, filename)).replace(" ", "%20") + url = f"{filepath}/{filename}".replace(" ", "%20") filename = os.path.splitext(filename.replace("_", " ").title())[0] print(f"{md_prefix(indent)} [{filename}]({url})") diff --git a/sorts/dutch_national_flag_sort.py b/sorts/dutch_national_flag_sort.py index 79afefa73afe..758e3a887b84 100644 --- a/sorts/dutch_national_flag_sort.py +++ b/sorts/dutch_national_flag_sort.py @@ -84,9 +84,8 @@ def dutch_national_flag_sort(sequence: list) -> list: sequence[mid], sequence[high] = sequence[high], sequence[mid] high -= 1 else: - raise ValueError( - f"The elements inside the sequence must contains only {colors} values" - ) + msg = f"The elements inside the sequence must contains only {colors} values" + raise ValueError(msg) return sequence diff --git a/strings/barcode_validator.py b/strings/barcode_validator.py index e050cd337d74..b4f3864e2642 100644 --- a/strings/barcode_validator.py +++ b/strings/barcode_validator.py @@ -65,7 +65,8 @@ def get_barcode(barcode: str) -> int: ValueError: Barcode 'dwefgiweuf' has alphabetic characters. """ if str(barcode).isalpha(): - raise ValueError(f"Barcode '{barcode}' has alphabetic characters.") + msg = f"Barcode '{barcode}' has alphabetic characters." + raise ValueError(msg) elif int(barcode) < 0: raise ValueError("The entered barcode has a negative value. Try again.") else: diff --git a/strings/capitalize.py b/strings/capitalize.py index 63603aa07e2d..e7e97c2beb53 100644 --- a/strings/capitalize.py +++ b/strings/capitalize.py @@ -17,7 +17,7 @@ def capitalize(sentence: str) -> str: """ if not sentence: return "" - lower_to_upper = {lc: uc for lc, uc in zip(ascii_lowercase, ascii_uppercase)} + lower_to_upper = dict(zip(ascii_lowercase, ascii_uppercase)) return lower_to_upper.get(sentence[0], sentence[0]) + sentence[1:] diff --git a/strings/is_spain_national_id.py b/strings/is_spain_national_id.py index 67f49755f412..60d06e123aae 100644 --- a/strings/is_spain_national_id.py +++ b/strings/is_spain_national_id.py @@ -48,7 +48,8 @@ def is_spain_national_id(spanish_id: str) -> bool: """ if not isinstance(spanish_id, str): - raise TypeError(f"Expected string as input, found {type(spanish_id).__name__}") + msg = f"Expected string as input, found {type(spanish_id).__name__}" + raise TypeError(msg) spanish_id_clean = spanish_id.replace("-", "").upper() if len(spanish_id_clean) != 9: diff --git a/strings/snake_case_to_camel_pascal_case.py b/strings/snake_case_to_camel_pascal_case.py index 28a28b517a01..8219337a63b0 100644 --- a/strings/snake_case_to_camel_pascal_case.py +++ b/strings/snake_case_to_camel_pascal_case.py @@ -27,11 +27,11 @@ def snake_to_camel_case(input_str: str, use_pascal: bool = False) -> str: """ if not isinstance(input_str, str): - raise ValueError(f"Expected string as input, found {type(input_str)}") + msg = f"Expected string as input, found {type(input_str)}" + raise ValueError(msg) if not isinstance(use_pascal, bool): - raise ValueError( - f"Expected boolean as use_pascal parameter, found {type(use_pascal)}" - ) + msg = f"Expected boolean as use_pascal parameter, found {type(use_pascal)}" + raise ValueError(msg) words = input_str.split("_") diff --git a/web_programming/reddit.py b/web_programming/reddit.py index 6a31c81c34bd..5ca5f828c0fb 100644 --- a/web_programming/reddit.py +++ b/web_programming/reddit.py @@ -26,7 +26,8 @@ def get_subreddit_data( """ wanted_data = wanted_data or [] if invalid_search_terms := ", ".join(sorted(set(wanted_data) - valid_terms)): - raise ValueError(f"Invalid search term: {invalid_search_terms}") + msg = f"Invalid search term: {invalid_search_terms}" + raise ValueError(msg) response = requests.get( f"https://reddit.com/r/{subreddit}/{age}.json?limit={limit}", headers={"User-agent": "A random string"}, diff --git a/web_programming/search_books_by_isbn.py b/web_programming/search_books_by_isbn.py index abac3c70b22e..d5d4cfe92f20 100644 --- a/web_programming/search_books_by_isbn.py +++ b/web_programming/search_books_by_isbn.py @@ -22,7 +22,8 @@ def get_openlibrary_data(olid: str = "isbn/0140328726") -> dict: """ new_olid = olid.strip().strip("/") # Remove leading/trailing whitespace & slashes if new_olid.count("/") != 1: - raise ValueError(f"{olid} is not a valid Open Library olid") + msg = f"{olid} is not a valid Open Library olid" + raise ValueError(msg) return requests.get(f"https://openlibrary.org/{new_olid}.json").json() diff --git a/web_programming/slack_message.py b/web_programming/slack_message.py index f35aa3ca587e..5e97d6b64c75 100644 --- a/web_programming/slack_message.py +++ b/web_programming/slack_message.py @@ -7,10 +7,11 @@ def send_slack_message(message_body: str, slack_url: str) -> None: headers = {"Content-Type": "application/json"} response = requests.post(slack_url, json={"text": message_body}, headers=headers) if response.status_code != 200: - raise ValueError( - f"Request to slack returned an error {response.status_code}, " - f"the response is:\n{response.text}" + msg = ( + "Request to slack returned an error " + f"{response.status_code}, the response is:\n{response.text}" ) + raise ValueError(msg) if __name__ == "__main__": From c93659d7ce65e3717f06333e3d049ebaa888e597 Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Mon, 29 May 2023 17:37:54 -0700 Subject: [PATCH 085/757] Fix type error in `strassen_matrix_multiplication.py` (#8784) * Fix type error in strassen_matrix_multiplication.py * updating DIRECTORY.md --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 1 + ...ion.py.BROKEN => strassen_matrix_multiplication.py} | 10 ++++++---- 2 files changed, 7 insertions(+), 4 deletions(-) rename divide_and_conquer/{strassen_matrix_multiplication.py.BROKEN => strassen_matrix_multiplication.py} (97%) diff --git a/DIRECTORY.md b/DIRECTORY.md index 11ff93c91430..231b0e2f1d2f 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -294,6 +294,7 @@ * [Mergesort](divide_and_conquer/mergesort.py) * [Peak](divide_and_conquer/peak.py) * [Power](divide_and_conquer/power.py) + * [Strassen Matrix Multiplication](divide_and_conquer/strassen_matrix_multiplication.py) ## Dynamic Programming * [Abbreviation](dynamic_programming/abbreviation.py) diff --git a/divide_and_conquer/strassen_matrix_multiplication.py.BROKEN b/divide_and_conquer/strassen_matrix_multiplication.py similarity index 97% rename from divide_and_conquer/strassen_matrix_multiplication.py.BROKEN rename to divide_and_conquer/strassen_matrix_multiplication.py index 2ca91c63bf4c..cbfc7e5655db 100644 --- a/divide_and_conquer/strassen_matrix_multiplication.py.BROKEN +++ b/divide_and_conquer/strassen_matrix_multiplication.py @@ -112,17 +112,19 @@ def strassen(matrix1: list, matrix2: list) -> list: [[139, 163], [121, 134], [100, 121]] """ if matrix_dimensions(matrix1)[1] != matrix_dimensions(matrix2)[0]: - raise Exception( - "Unable to multiply these matrices, please check the dimensions. \n" - f"Matrix A:{matrix1} \nMatrix B:{matrix2}" + msg = ( + "Unable to multiply these matrices, please check the dimensions.\n" + f"Matrix A: {matrix1}\n" + f"Matrix B: {matrix2}" ) + raise Exception(msg) dimension1 = matrix_dimensions(matrix1) dimension2 = matrix_dimensions(matrix2) if dimension1[0] == dimension1[1] and dimension2[0] == dimension2[1]: return [matrix1, matrix2] - maximum = max(dimension1, dimension2) + maximum = max(*dimension1, *dimension2) maxim = int(math.pow(2, math.ceil(math.log2(maximum)))) new_matrix1 = matrix1 new_matrix2 = matrix2 From 4a27b544303e6bab90ed57b72fa3acf3d785429e Mon Sep 17 00:00:00 2001 From: Sundaram Kumar Jha Date: Wed, 31 May 2023 06:26:59 +0530 Subject: [PATCH 086/757] Update permutations.py (#8102) --- data_structures/arrays/permutations.py | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/data_structures/arrays/permutations.py b/data_structures/arrays/permutations.py index eb3f26517863..4558bd8d468a 100644 --- a/data_structures/arrays/permutations.py +++ b/data_structures/arrays/permutations.py @@ -1,7 +1,6 @@ def permute(nums: list[int]) -> list[list[int]]: """ Return all permutations. - >>> from itertools import permutations >>> numbers= [1,2,3] >>> all(list(nums) in permute(numbers) for nums in permutations(numbers)) @@ -20,7 +19,32 @@ def permute(nums: list[int]) -> list[list[int]]: return result +def permute2(nums): + """ + Return all permutations of the given list. + + >>> permute2([1, 2, 3]) + [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 2, 1], [3, 1, 2]] + """ + + def backtrack(start): + if start == len(nums) - 1: + output.append(nums[:]) + else: + for i in range(start, len(nums)): + nums[start], nums[i] = nums[i], nums[start] + backtrack(start + 1) + nums[start], nums[i] = nums[i], nums[start] # backtrack + + output = [] + backtrack(0) + return output + + if __name__ == "__main__": import doctest + # use res to print the data in permute2 function + res = permute2([1, 2, 3]) + print(res) doctest.testmod() From e871540e37b834673f9e6650b8e2281d7d36a8c3 Mon Sep 17 00:00:00 2001 From: Rudransh Bhardwaj <115872354+rudransh61@users.noreply.github.com> Date: Wed, 31 May 2023 20:33:02 +0530 Subject: [PATCH 087/757] Added rank of matrix in linear algebra (#8687) * Added rank of matrix in linear algebra * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Corrected name of function * Corrected Rank_of_Matrix.py * Completed rank_of_matrix.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * delete to rename Rank_of_Matrix.py * created rank_of_matrix * added more doctests in rank_of_matrix.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fixed some issues in rank_of_matrix.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * added moreeee doctestsss in rank_of_mtrix.py and fixed some bugss * Update linear_algebra/src/rank_of_matrix.py Co-authored-by: Christian Clauss * Update linear_algebra/src/rank_of_matrix.py Co-authored-by: Christian Clauss * Update linear_algebra/src/rank_of_matrix.py Co-authored-by: Christian Clauss * Update rank_of_matrix.py * Update linear_algebra/src/rank_of_matrix.py Co-authored-by: Caeden Perelli-Harris --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss Co-authored-by: Caeden Perelli-Harris --- linear_algebra/src/rank_of_matrix.py | 89 ++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 linear_algebra/src/rank_of_matrix.py diff --git a/linear_algebra/src/rank_of_matrix.py b/linear_algebra/src/rank_of_matrix.py new file mode 100644 index 000000000000..7ff3c1699a69 --- /dev/null +++ b/linear_algebra/src/rank_of_matrix.py @@ -0,0 +1,89 @@ +""" +Calculate the rank of a matrix. + +See: https://en.wikipedia.org/wiki/Rank_(linear_algebra) +""" + + +def rank_of_matrix(matrix: list[list[int | float]]) -> int: + """ + Finds the rank of a matrix. + Args: + matrix: The matrix as a list of lists. + Returns: + The rank of the matrix. + Example: + >>> matrix1 = [[1, 2, 3], + ... [4, 5, 6], + ... [7, 8, 9]] + >>> rank_of_matrix(matrix1) + 2 + >>> matrix2 = [[1, 0, 0], + ... [0, 1, 0], + ... [0, 0, 0]] + >>> rank_of_matrix(matrix2) + 2 + >>> matrix3 = [[1, 2, 3, 4], + ... [5, 6, 7, 8], + ... [9, 10, 11, 12]] + >>> rank_of_matrix(matrix3) + 2 + >>> rank_of_matrix([[2,3,-1,-1], + ... [1,-1,-2,4], + ... [3,1,3,-2], + ... [6,3,0,-7]]) + 4 + >>> rank_of_matrix([[2,1,-3,-6], + ... [3,-3,1,2], + ... [1,1,1,2]]) + 3 + >>> rank_of_matrix([[2,-1,0], + ... [1,3,4], + ... [4,1,-3]]) + 3 + >>> rank_of_matrix([[3,2,1], + ... [-6,-4,-2]]) + 1 + >>> rank_of_matrix([[],[]]) + 0 + >>> rank_of_matrix([[1]]) + 1 + >>> rank_of_matrix([[]]) + 0 + """ + + rows = len(matrix) + columns = len(matrix[0]) + rank = min(rows, columns) + + for row in range(rank): + # Check if diagonal element is not zero + if matrix[row][row] != 0: + # Eliminate all the elements below the diagonal + for col in range(row + 1, rows): + multiplier = matrix[col][row] / matrix[row][row] + for i in range(row, columns): + matrix[col][i] -= multiplier * matrix[row][i] + else: + # Find a non-zero diagonal element to swap rows + reduce = True + for i in range(row + 1, rows): + if matrix[i][row] != 0: + matrix[row], matrix[i] = matrix[i], matrix[row] + reduce = False + break + if reduce: + rank -= 1 + for i in range(rows): + matrix[i][row] = matrix[i][rank] + + # Reduce the row pointer by one to stay on the same row + row -= 1 + + return rank + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 4621b0bb4f5d3fff2fa4f0e53d6cb862fe002c60 Mon Sep 17 00:00:00 2001 From: nith2001 <75632283+nith2001@users.noreply.github.com> Date: Wed, 31 May 2023 13:06:12 -0700 Subject: [PATCH 088/757] Improved Graph Implementations (#8730) * Improved Graph Implementations Provides new implementation for graph_list.py and graph_matrix.py along with pytest suites for each. Fixes #8709 * Graph implementation style fixes, corrections, and refactored tests * Helpful docs about graph implementation * Refactored code to separate files and applied enumerate() * Renamed files and refactored code to fail fast * Error handling style fix * Fixed f-string code quality issue * Last f-string fix * Added return types to test functions and more style fixes * Added more function return types * Added more function return types pt2 * Fixed error messages --- graphs/graph_adjacency_list.py | 589 ++++++++++++++++++++++++++++++ graphs/graph_adjacency_matrix.py | 608 +++++++++++++++++++++++++++++++ graphs/graph_matrix.py | 24 -- graphs/tests/__init__.py | 0 4 files changed, 1197 insertions(+), 24 deletions(-) create mode 100644 graphs/graph_adjacency_list.py create mode 100644 graphs/graph_adjacency_matrix.py delete mode 100644 graphs/graph_matrix.py create mode 100644 graphs/tests/__init__.py diff --git a/graphs/graph_adjacency_list.py b/graphs/graph_adjacency_list.py new file mode 100644 index 000000000000..76f34f845860 --- /dev/null +++ b/graphs/graph_adjacency_list.py @@ -0,0 +1,589 @@ +#!/usr/bin/env python3 +""" +Author: Vikram Nithyanandam + +Description: +The following implementation is a robust unweighted Graph data structure +implemented using an adjacency list. This vertices and edges of this graph can be +effectively initialized and modified while storing your chosen generic +value in each vertex. + +Adjacency List: https://en.wikipedia.org/wiki/Adjacency_list + +Potential Future Ideas: +- Add a flag to set edge weights on and set edge weights +- Make edge weights and vertex values customizable to store whatever the client wants +- Support multigraph functionality if the client wants it +""" +from __future__ import annotations + +import random +import unittest +from pprint import pformat +from typing import Generic, TypeVar + +T = TypeVar("T") + + +class GraphAdjacencyList(Generic[T]): + def __init__( + self, vertices: list[T], edges: list[list[T]], directed: bool = True + ) -> None: + """ + Parameters: + - vertices: (list[T]) The list of vertex names the client wants to + pass in. Default is empty. + - edges: (list[list[T]]) The list of edges the client wants to + pass in. Each edge is a 2-element list. Default is empty. + - directed: (bool) Indicates if graph is directed or undirected. + Default is True. + """ + self.adj_list: dict[T, list[T]] = {} # dictionary of lists of T + self.directed = directed + + # Falsey checks + edges = edges or [] + vertices = vertices or [] + + for vertex in vertices: + self.add_vertex(vertex) + + for edge in edges: + if len(edge) != 2: + msg = f"Invalid input: {edge} is the wrong length." + raise ValueError(msg) + self.add_edge(edge[0], edge[1]) + + def add_vertex(self, vertex: T) -> None: + """ + Adds a vertex to the graph. If the given vertex already exists, + a ValueError will be thrown. + """ + if self.contains_vertex(vertex): + msg = f"Incorrect input: {vertex} is already in the graph." + raise ValueError(msg) + self.adj_list[vertex] = [] + + def add_edge(self, source_vertex: T, destination_vertex: T) -> None: + """ + Creates an edge from source vertex to destination vertex. If any + given vertex doesn't exist or the edge already exists, a ValueError + will be thrown. + """ + if not ( + self.contains_vertex(source_vertex) + and self.contains_vertex(destination_vertex) + ): + msg = ( + f"Incorrect input: Either {source_vertex} or " + f"{destination_vertex} does not exist" + ) + raise ValueError(msg) + if self.contains_edge(source_vertex, destination_vertex): + msg = ( + "Incorrect input: The edge already exists between " + f"{source_vertex} and {destination_vertex}" + ) + raise ValueError(msg) + + # add the destination vertex to the list associated with the source vertex + # and vice versa if not directed + self.adj_list[source_vertex].append(destination_vertex) + if not self.directed: + self.adj_list[destination_vertex].append(source_vertex) + + def remove_vertex(self, vertex: T) -> None: + """ + Removes the given vertex from the graph and deletes all incoming and + outgoing edges from the given vertex as well. If the given vertex + does not exist, a ValueError will be thrown. + """ + if not self.contains_vertex(vertex): + msg = f"Incorrect input: {vertex} does not exist in this graph." + raise ValueError(msg) + + if not self.directed: + # If not directed, find all neighboring vertices and delete all references + # of edges connecting to the given vertex + for neighbor in self.adj_list[vertex]: + self.adj_list[neighbor].remove(vertex) + else: + # If directed, search all neighbors of all vertices and delete all + # references of edges connecting to the given vertex + for edge_list in self.adj_list.values(): + if vertex in edge_list: + edge_list.remove(vertex) + + # Finally, delete the given vertex and all of its outgoing edge references + self.adj_list.pop(vertex) + + def remove_edge(self, source_vertex: T, destination_vertex: T) -> None: + """ + Removes the edge between the two vertices. If any given vertex + doesn't exist or the edge does not exist, a ValueError will be thrown. + """ + if not ( + self.contains_vertex(source_vertex) + and self.contains_vertex(destination_vertex) + ): + msg = ( + f"Incorrect input: Either {source_vertex} or " + f"{destination_vertex} does not exist" + ) + raise ValueError(msg) + if not self.contains_edge(source_vertex, destination_vertex): + msg = ( + "Incorrect input: The edge does NOT exist between " + f"{source_vertex} and {destination_vertex}" + ) + raise ValueError(msg) + + # remove the destination vertex from the list associated with the source + # vertex and vice versa if not directed + self.adj_list[source_vertex].remove(destination_vertex) + if not self.directed: + self.adj_list[destination_vertex].remove(source_vertex) + + def contains_vertex(self, vertex: T) -> bool: + """ + Returns True if the graph contains the vertex, False otherwise. + """ + return vertex in self.adj_list + + def contains_edge(self, source_vertex: T, destination_vertex: T) -> bool: + """ + Returns True if the graph contains the edge from the source_vertex to the + destination_vertex, False otherwise. If any given vertex doesn't exist, a + ValueError will be thrown. + """ + if not ( + self.contains_vertex(source_vertex) + and self.contains_vertex(destination_vertex) + ): + msg = ( + f"Incorrect input: Either {source_vertex} " + f"or {destination_vertex} does not exist." + ) + raise ValueError(msg) + + return destination_vertex in self.adj_list[source_vertex] + + def clear_graph(self) -> None: + """ + Clears all vertices and edges. + """ + self.adj_list = {} + + def __repr__(self) -> str: + return pformat(self.adj_list) + + +class TestGraphAdjacencyList(unittest.TestCase): + def __assert_graph_edge_exists_check( + self, + undirected_graph: GraphAdjacencyList, + directed_graph: GraphAdjacencyList, + edge: list[int], + ) -> None: + self.assertTrue(undirected_graph.contains_edge(edge[0], edge[1])) + self.assertTrue(undirected_graph.contains_edge(edge[1], edge[0])) + self.assertTrue(directed_graph.contains_edge(edge[0], edge[1])) + + def __assert_graph_edge_does_not_exist_check( + self, + undirected_graph: GraphAdjacencyList, + directed_graph: GraphAdjacencyList, + edge: list[int], + ) -> None: + self.assertFalse(undirected_graph.contains_edge(edge[0], edge[1])) + self.assertFalse(undirected_graph.contains_edge(edge[1], edge[0])) + self.assertFalse(directed_graph.contains_edge(edge[0], edge[1])) + + def __assert_graph_vertex_exists_check( + self, + undirected_graph: GraphAdjacencyList, + directed_graph: GraphAdjacencyList, + vertex: int, + ) -> None: + self.assertTrue(undirected_graph.contains_vertex(vertex)) + self.assertTrue(directed_graph.contains_vertex(vertex)) + + def __assert_graph_vertex_does_not_exist_check( + self, + undirected_graph: GraphAdjacencyList, + directed_graph: GraphAdjacencyList, + vertex: int, + ) -> None: + self.assertFalse(undirected_graph.contains_vertex(vertex)) + self.assertFalse(directed_graph.contains_vertex(vertex)) + + def __generate_random_edges( + self, vertices: list[int], edge_pick_count: int + ) -> list[list[int]]: + self.assertTrue(edge_pick_count <= len(vertices)) + + random_source_vertices: list[int] = random.sample( + vertices[0 : int(len(vertices) / 2)], edge_pick_count + ) + random_destination_vertices: list[int] = random.sample( + vertices[int(len(vertices) / 2) :], edge_pick_count + ) + random_edges: list[list[int]] = [] + + for source in random_source_vertices: + for dest in random_destination_vertices: + random_edges.append([source, dest]) + + return random_edges + + def __generate_graphs( + self, vertex_count: int, min_val: int, max_val: int, edge_pick_count: int + ) -> tuple[GraphAdjacencyList, GraphAdjacencyList, list[int], list[list[int]]]: + if max_val - min_val + 1 < vertex_count: + raise ValueError( + "Will result in duplicate vertices. Either increase range " + "between min_val and max_val or decrease vertex count." + ) + + # generate graph input + random_vertices: list[int] = random.sample( + range(min_val, max_val + 1), vertex_count + ) + random_edges: list[list[int]] = self.__generate_random_edges( + random_vertices, edge_pick_count + ) + + # build graphs + undirected_graph = GraphAdjacencyList( + vertices=random_vertices, edges=random_edges, directed=False + ) + directed_graph = GraphAdjacencyList( + vertices=random_vertices, edges=random_edges, directed=True + ) + + return undirected_graph, directed_graph, random_vertices, random_edges + + def test_init_check(self) -> None: + ( + undirected_graph, + directed_graph, + random_vertices, + random_edges, + ) = self.__generate_graphs(20, 0, 100, 4) + + # test graph initialization with vertices and edges + for num in random_vertices: + self.__assert_graph_vertex_exists_check( + undirected_graph, directed_graph, num + ) + + for edge in random_edges: + self.__assert_graph_edge_exists_check( + undirected_graph, directed_graph, edge + ) + self.assertFalse(undirected_graph.directed) + self.assertTrue(directed_graph.directed) + + def test_contains_vertex(self) -> None: + random_vertices: list[int] = random.sample(range(101), 20) + + # Build graphs WITHOUT edges + undirected_graph = GraphAdjacencyList( + vertices=random_vertices, edges=[], directed=False + ) + directed_graph = GraphAdjacencyList( + vertices=random_vertices, edges=[], directed=True + ) + + # Test contains_vertex + for num in range(101): + self.assertEqual( + num in random_vertices, undirected_graph.contains_vertex(num) + ) + self.assertEqual( + num in random_vertices, directed_graph.contains_vertex(num) + ) + + def test_add_vertices(self) -> None: + random_vertices: list[int] = random.sample(range(101), 20) + + # build empty graphs + undirected_graph: GraphAdjacencyList = GraphAdjacencyList( + vertices=[], edges=[], directed=False + ) + directed_graph: GraphAdjacencyList = GraphAdjacencyList( + vertices=[], edges=[], directed=True + ) + + # run add_vertex + for num in random_vertices: + undirected_graph.add_vertex(num) + + for num in random_vertices: + directed_graph.add_vertex(num) + + # test add_vertex worked + for num in random_vertices: + self.__assert_graph_vertex_exists_check( + undirected_graph, directed_graph, num + ) + + def test_remove_vertices(self) -> None: + random_vertices: list[int] = random.sample(range(101), 20) + + # build graphs WITHOUT edges + undirected_graph = GraphAdjacencyList( + vertices=random_vertices, edges=[], directed=False + ) + directed_graph = GraphAdjacencyList( + vertices=random_vertices, edges=[], directed=True + ) + + # test remove_vertex worked + for num in random_vertices: + self.__assert_graph_vertex_exists_check( + undirected_graph, directed_graph, num + ) + + undirected_graph.remove_vertex(num) + directed_graph.remove_vertex(num) + + self.__assert_graph_vertex_does_not_exist_check( + undirected_graph, directed_graph, num + ) + + def test_add_and_remove_vertices_repeatedly(self) -> None: + random_vertices1: list[int] = random.sample(range(51), 20) + random_vertices2: list[int] = random.sample(range(51, 101), 20) + + # build graphs WITHOUT edges + undirected_graph = GraphAdjacencyList( + vertices=random_vertices1, edges=[], directed=False + ) + directed_graph = GraphAdjacencyList( + vertices=random_vertices1, edges=[], directed=True + ) + + # test adding and removing vertices + for i, _ in enumerate(random_vertices1): + undirected_graph.add_vertex(random_vertices2[i]) + directed_graph.add_vertex(random_vertices2[i]) + + self.__assert_graph_vertex_exists_check( + undirected_graph, directed_graph, random_vertices2[i] + ) + + undirected_graph.remove_vertex(random_vertices1[i]) + directed_graph.remove_vertex(random_vertices1[i]) + + self.__assert_graph_vertex_does_not_exist_check( + undirected_graph, directed_graph, random_vertices1[i] + ) + + # remove all vertices + for i, _ in enumerate(random_vertices1): + undirected_graph.remove_vertex(random_vertices2[i]) + directed_graph.remove_vertex(random_vertices2[i]) + + self.__assert_graph_vertex_does_not_exist_check( + undirected_graph, directed_graph, random_vertices2[i] + ) + + def test_contains_edge(self) -> None: + # generate graphs and graph input + vertex_count = 20 + ( + undirected_graph, + directed_graph, + random_vertices, + random_edges, + ) = self.__generate_graphs(vertex_count, 0, 100, 4) + + # generate all possible edges for testing + all_possible_edges: list[list[int]] = [] + for i in range(vertex_count - 1): + for j in range(i + 1, vertex_count): + all_possible_edges.append([random_vertices[i], random_vertices[j]]) + all_possible_edges.append([random_vertices[j], random_vertices[i]]) + + # test contains_edge function + for edge in all_possible_edges: + if edge in random_edges: + self.__assert_graph_edge_exists_check( + undirected_graph, directed_graph, edge + ) + elif [edge[1], edge[0]] in random_edges: + # since this edge exists for undirected but the reverse + # may not exist for directed + self.__assert_graph_edge_exists_check( + undirected_graph, directed_graph, [edge[1], edge[0]] + ) + else: + self.__assert_graph_edge_does_not_exist_check( + undirected_graph, directed_graph, edge + ) + + def test_add_edge(self) -> None: + # generate graph input + random_vertices: list[int] = random.sample(range(101), 15) + random_edges: list[list[int]] = self.__generate_random_edges(random_vertices, 4) + + # build graphs WITHOUT edges + undirected_graph = GraphAdjacencyList( + vertices=random_vertices, edges=[], directed=False + ) + directed_graph = GraphAdjacencyList( + vertices=random_vertices, edges=[], directed=True + ) + + # run and test add_edge + for edge in random_edges: + undirected_graph.add_edge(edge[0], edge[1]) + directed_graph.add_edge(edge[0], edge[1]) + self.__assert_graph_edge_exists_check( + undirected_graph, directed_graph, edge + ) + + def test_remove_edge(self) -> None: + # generate graph input and graphs + ( + undirected_graph, + directed_graph, + random_vertices, + random_edges, + ) = self.__generate_graphs(20, 0, 100, 4) + + # run and test remove_edge + for edge in random_edges: + self.__assert_graph_edge_exists_check( + undirected_graph, directed_graph, edge + ) + undirected_graph.remove_edge(edge[0], edge[1]) + directed_graph.remove_edge(edge[0], edge[1]) + self.__assert_graph_edge_does_not_exist_check( + undirected_graph, directed_graph, edge + ) + + def test_add_and_remove_edges_repeatedly(self) -> None: + ( + undirected_graph, + directed_graph, + random_vertices, + random_edges, + ) = self.__generate_graphs(20, 0, 100, 4) + + # make some more edge options! + more_random_edges: list[list[int]] = [] + + while len(more_random_edges) != len(random_edges): + edges: list[list[int]] = self.__generate_random_edges(random_vertices, 4) + for edge in edges: + if len(more_random_edges) == len(random_edges): + break + elif edge not in more_random_edges and edge not in random_edges: + more_random_edges.append(edge) + + for i, _ in enumerate(random_edges): + undirected_graph.add_edge(more_random_edges[i][0], more_random_edges[i][1]) + directed_graph.add_edge(more_random_edges[i][0], more_random_edges[i][1]) + + self.__assert_graph_edge_exists_check( + undirected_graph, directed_graph, more_random_edges[i] + ) + + undirected_graph.remove_edge(random_edges[i][0], random_edges[i][1]) + directed_graph.remove_edge(random_edges[i][0], random_edges[i][1]) + + self.__assert_graph_edge_does_not_exist_check( + undirected_graph, directed_graph, random_edges[i] + ) + + def test_add_vertex_exception_check(self) -> None: + ( + undirected_graph, + directed_graph, + random_vertices, + random_edges, + ) = self.__generate_graphs(20, 0, 100, 4) + + for vertex in random_vertices: + with self.assertRaises(ValueError): + undirected_graph.add_vertex(vertex) + with self.assertRaises(ValueError): + directed_graph.add_vertex(vertex) + + def test_remove_vertex_exception_check(self) -> None: + ( + undirected_graph, + directed_graph, + random_vertices, + random_edges, + ) = self.__generate_graphs(20, 0, 100, 4) + + for i in range(101): + if i not in random_vertices: + with self.assertRaises(ValueError): + undirected_graph.remove_vertex(i) + with self.assertRaises(ValueError): + directed_graph.remove_vertex(i) + + def test_add_edge_exception_check(self) -> None: + ( + undirected_graph, + directed_graph, + random_vertices, + random_edges, + ) = self.__generate_graphs(20, 0, 100, 4) + + for edge in random_edges: + with self.assertRaises(ValueError): + undirected_graph.add_edge(edge[0], edge[1]) + with self.assertRaises(ValueError): + directed_graph.add_edge(edge[0], edge[1]) + + def test_remove_edge_exception_check(self) -> None: + ( + undirected_graph, + directed_graph, + random_vertices, + random_edges, + ) = self.__generate_graphs(20, 0, 100, 4) + + more_random_edges: list[list[int]] = [] + + while len(more_random_edges) != len(random_edges): + edges: list[list[int]] = self.__generate_random_edges(random_vertices, 4) + for edge in edges: + if len(more_random_edges) == len(random_edges): + break + elif edge not in more_random_edges and edge not in random_edges: + more_random_edges.append(edge) + + for edge in more_random_edges: + with self.assertRaises(ValueError): + undirected_graph.remove_edge(edge[0], edge[1]) + with self.assertRaises(ValueError): + directed_graph.remove_edge(edge[0], edge[1]) + + def test_contains_edge_exception_check(self) -> None: + ( + undirected_graph, + directed_graph, + random_vertices, + random_edges, + ) = self.__generate_graphs(20, 0, 100, 4) + + for vertex in random_vertices: + with self.assertRaises(ValueError): + undirected_graph.contains_edge(vertex, 102) + with self.assertRaises(ValueError): + directed_graph.contains_edge(vertex, 102) + + with self.assertRaises(ValueError): + undirected_graph.contains_edge(103, 102) + with self.assertRaises(ValueError): + directed_graph.contains_edge(103, 102) + + +if __name__ == "__main__": + unittest.main() diff --git a/graphs/graph_adjacency_matrix.py b/graphs/graph_adjacency_matrix.py new file mode 100644 index 000000000000..4d2e02f737f9 --- /dev/null +++ b/graphs/graph_adjacency_matrix.py @@ -0,0 +1,608 @@ +#!/usr/bin/env python3 +""" +Author: Vikram Nithyanandam + +Description: +The following implementation is a robust unweighted Graph data structure +implemented using an adjacency matrix. This vertices and edges of this graph can be +effectively initialized and modified while storing your chosen generic +value in each vertex. + +Adjacency Matrix: https://mathworld.wolfram.com/AdjacencyMatrix.html + +Potential Future Ideas: +- Add a flag to set edge weights on and set edge weights +- Make edge weights and vertex values customizable to store whatever the client wants +- Support multigraph functionality if the client wants it +""" +from __future__ import annotations + +import random +import unittest +from pprint import pformat +from typing import Generic, TypeVar + +T = TypeVar("T") + + +class GraphAdjacencyMatrix(Generic[T]): + def __init__( + self, vertices: list[T], edges: list[list[T]], directed: bool = True + ) -> None: + """ + Parameters: + - vertices: (list[T]) The list of vertex names the client wants to + pass in. Default is empty. + - edges: (list[list[T]]) The list of edges the client wants to + pass in. Each edge is a 2-element list. Default is empty. + - directed: (bool) Indicates if graph is directed or undirected. + Default is True. + """ + self.directed = directed + self.vertex_to_index: dict[T, int] = {} + self.adj_matrix: list[list[int]] = [] + + # Falsey checks + edges = edges or [] + vertices = vertices or [] + + for vertex in vertices: + self.add_vertex(vertex) + + for edge in edges: + if len(edge) != 2: + msg = f"Invalid input: {edge} must have length 2." + raise ValueError(msg) + self.add_edge(edge[0], edge[1]) + + def add_edge(self, source_vertex: T, destination_vertex: T) -> None: + """ + Creates an edge from source vertex to destination vertex. If any + given vertex doesn't exist or the edge already exists, a ValueError + will be thrown. + """ + if not ( + self.contains_vertex(source_vertex) + and self.contains_vertex(destination_vertex) + ): + msg = ( + f"Incorrect input: Either {source_vertex} or " + f"{destination_vertex} does not exist" + ) + raise ValueError(msg) + if self.contains_edge(source_vertex, destination_vertex): + msg = ( + "Incorrect input: The edge already exists between " + f"{source_vertex} and {destination_vertex}" + ) + raise ValueError(msg) + + # Get the indices of the corresponding vertices and set their edge value to 1. + u: int = self.vertex_to_index[source_vertex] + v: int = self.vertex_to_index[destination_vertex] + self.adj_matrix[u][v] = 1 + if not self.directed: + self.adj_matrix[v][u] = 1 + + def remove_edge(self, source_vertex: T, destination_vertex: T) -> None: + """ + Removes the edge between the two vertices. If any given vertex + doesn't exist or the edge does not exist, a ValueError will be thrown. + """ + if not ( + self.contains_vertex(source_vertex) + and self.contains_vertex(destination_vertex) + ): + msg = ( + f"Incorrect input: Either {source_vertex} or " + f"{destination_vertex} does not exist" + ) + raise ValueError(msg) + if not self.contains_edge(source_vertex, destination_vertex): + msg = ( + "Incorrect input: The edge does NOT exist between " + f"{source_vertex} and {destination_vertex}" + ) + raise ValueError(msg) + + # Get the indices of the corresponding vertices and set their edge value to 0. + u: int = self.vertex_to_index[source_vertex] + v: int = self.vertex_to_index[destination_vertex] + self.adj_matrix[u][v] = 0 + if not self.directed: + self.adj_matrix[v][u] = 0 + + def add_vertex(self, vertex: T) -> None: + """ + Adds a vertex to the graph. If the given vertex already exists, + a ValueError will be thrown. + """ + if self.contains_vertex(vertex): + msg = f"Incorrect input: {vertex} already exists in this graph." + raise ValueError(msg) + + # build column for vertex + for row in self.adj_matrix: + row.append(0) + + # build row for vertex and update other data structures + self.adj_matrix.append([0] * (len(self.adj_matrix) + 1)) + self.vertex_to_index[vertex] = len(self.adj_matrix) - 1 + + def remove_vertex(self, vertex: T) -> None: + """ + Removes the given vertex from the graph and deletes all incoming and + outgoing edges from the given vertex as well. If the given vertex + does not exist, a ValueError will be thrown. + """ + if not self.contains_vertex(vertex): + msg = f"Incorrect input: {vertex} does not exist in this graph." + raise ValueError(msg) + + # first slide up the rows by deleting the row corresponding to + # the vertex being deleted. + start_index = self.vertex_to_index[vertex] + self.adj_matrix.pop(start_index) + + # next, slide the columns to the left by deleting the values in + # the column corresponding to the vertex being deleted + for lst in self.adj_matrix: + lst.pop(start_index) + + # final clean up + self.vertex_to_index.pop(vertex) + + # decrement indices for vertices shifted by the deleted vertex in the adj matrix + for vertex in self.vertex_to_index: + if self.vertex_to_index[vertex] >= start_index: + self.vertex_to_index[vertex] = self.vertex_to_index[vertex] - 1 + + def contains_vertex(self, vertex: T) -> bool: + """ + Returns True if the graph contains the vertex, False otherwise. + """ + return vertex in self.vertex_to_index + + def contains_edge(self, source_vertex: T, destination_vertex: T) -> bool: + """ + Returns True if the graph contains the edge from the source_vertex to the + destination_vertex, False otherwise. If any given vertex doesn't exist, a + ValueError will be thrown. + """ + if not ( + self.contains_vertex(source_vertex) + and self.contains_vertex(destination_vertex) + ): + msg = ( + f"Incorrect input: Either {source_vertex} " + f"or {destination_vertex} does not exist." + ) + raise ValueError(msg) + + u = self.vertex_to_index[source_vertex] + v = self.vertex_to_index[destination_vertex] + return self.adj_matrix[u][v] == 1 + + def clear_graph(self) -> None: + """ + Clears all vertices and edges. + """ + self.vertex_to_index = {} + self.adj_matrix = [] + + def __repr__(self) -> str: + first = "Adj Matrix:\n" + pformat(self.adj_matrix) + second = "\nVertex to index mapping:\n" + pformat(self.vertex_to_index) + return first + second + + +class TestGraphMatrix(unittest.TestCase): + def __assert_graph_edge_exists_check( + self, + undirected_graph: GraphAdjacencyMatrix, + directed_graph: GraphAdjacencyMatrix, + edge: list[int], + ) -> None: + self.assertTrue(undirected_graph.contains_edge(edge[0], edge[1])) + self.assertTrue(undirected_graph.contains_edge(edge[1], edge[0])) + self.assertTrue(directed_graph.contains_edge(edge[0], edge[1])) + + def __assert_graph_edge_does_not_exist_check( + self, + undirected_graph: GraphAdjacencyMatrix, + directed_graph: GraphAdjacencyMatrix, + edge: list[int], + ) -> None: + self.assertFalse(undirected_graph.contains_edge(edge[0], edge[1])) + self.assertFalse(undirected_graph.contains_edge(edge[1], edge[0])) + self.assertFalse(directed_graph.contains_edge(edge[0], edge[1])) + + def __assert_graph_vertex_exists_check( + self, + undirected_graph: GraphAdjacencyMatrix, + directed_graph: GraphAdjacencyMatrix, + vertex: int, + ) -> None: + self.assertTrue(undirected_graph.contains_vertex(vertex)) + self.assertTrue(directed_graph.contains_vertex(vertex)) + + def __assert_graph_vertex_does_not_exist_check( + self, + undirected_graph: GraphAdjacencyMatrix, + directed_graph: GraphAdjacencyMatrix, + vertex: int, + ) -> None: + self.assertFalse(undirected_graph.contains_vertex(vertex)) + self.assertFalse(directed_graph.contains_vertex(vertex)) + + def __generate_random_edges( + self, vertices: list[int], edge_pick_count: int + ) -> list[list[int]]: + self.assertTrue(edge_pick_count <= len(vertices)) + + random_source_vertices: list[int] = random.sample( + vertices[0 : int(len(vertices) / 2)], edge_pick_count + ) + random_destination_vertices: list[int] = random.sample( + vertices[int(len(vertices) / 2) :], edge_pick_count + ) + random_edges: list[list[int]] = [] + + for source in random_source_vertices: + for dest in random_destination_vertices: + random_edges.append([source, dest]) + + return random_edges + + def __generate_graphs( + self, vertex_count: int, min_val: int, max_val: int, edge_pick_count: int + ) -> tuple[GraphAdjacencyMatrix, GraphAdjacencyMatrix, list[int], list[list[int]]]: + if max_val - min_val + 1 < vertex_count: + raise ValueError( + "Will result in duplicate vertices. Either increase " + "range between min_val and max_val or decrease vertex count" + ) + + # generate graph input + random_vertices: list[int] = random.sample( + range(min_val, max_val + 1), vertex_count + ) + random_edges: list[list[int]] = self.__generate_random_edges( + random_vertices, edge_pick_count + ) + + # build graphs + undirected_graph = GraphAdjacencyMatrix( + vertices=random_vertices, edges=random_edges, directed=False + ) + directed_graph = GraphAdjacencyMatrix( + vertices=random_vertices, edges=random_edges, directed=True + ) + + return undirected_graph, directed_graph, random_vertices, random_edges + + def test_init_check(self) -> None: + ( + undirected_graph, + directed_graph, + random_vertices, + random_edges, + ) = self.__generate_graphs(20, 0, 100, 4) + + # test graph initialization with vertices and edges + for num in random_vertices: + self.__assert_graph_vertex_exists_check( + undirected_graph, directed_graph, num + ) + + for edge in random_edges: + self.__assert_graph_edge_exists_check( + undirected_graph, directed_graph, edge + ) + + self.assertFalse(undirected_graph.directed) + self.assertTrue(directed_graph.directed) + + def test_contains_vertex(self) -> None: + random_vertices: list[int] = random.sample(range(101), 20) + + # Build graphs WITHOUT edges + undirected_graph = GraphAdjacencyMatrix( + vertices=random_vertices, edges=[], directed=False + ) + directed_graph = GraphAdjacencyMatrix( + vertices=random_vertices, edges=[], directed=True + ) + + # Test contains_vertex + for num in range(101): + self.assertEqual( + num in random_vertices, undirected_graph.contains_vertex(num) + ) + self.assertEqual( + num in random_vertices, directed_graph.contains_vertex(num) + ) + + def test_add_vertices(self) -> None: + random_vertices: list[int] = random.sample(range(101), 20) + + # build empty graphs + undirected_graph: GraphAdjacencyMatrix = GraphAdjacencyMatrix( + vertices=[], edges=[], directed=False + ) + directed_graph: GraphAdjacencyMatrix = GraphAdjacencyMatrix( + vertices=[], edges=[], directed=True + ) + + # run add_vertex + for num in random_vertices: + undirected_graph.add_vertex(num) + + for num in random_vertices: + directed_graph.add_vertex(num) + + # test add_vertex worked + for num in random_vertices: + self.__assert_graph_vertex_exists_check( + undirected_graph, directed_graph, num + ) + + def test_remove_vertices(self) -> None: + random_vertices: list[int] = random.sample(range(101), 20) + + # build graphs WITHOUT edges + undirected_graph = GraphAdjacencyMatrix( + vertices=random_vertices, edges=[], directed=False + ) + directed_graph = GraphAdjacencyMatrix( + vertices=random_vertices, edges=[], directed=True + ) + + # test remove_vertex worked + for num in random_vertices: + self.__assert_graph_vertex_exists_check( + undirected_graph, directed_graph, num + ) + + undirected_graph.remove_vertex(num) + directed_graph.remove_vertex(num) + + self.__assert_graph_vertex_does_not_exist_check( + undirected_graph, directed_graph, num + ) + + def test_add_and_remove_vertices_repeatedly(self) -> None: + random_vertices1: list[int] = random.sample(range(51), 20) + random_vertices2: list[int] = random.sample(range(51, 101), 20) + + # build graphs WITHOUT edges + undirected_graph = GraphAdjacencyMatrix( + vertices=random_vertices1, edges=[], directed=False + ) + directed_graph = GraphAdjacencyMatrix( + vertices=random_vertices1, edges=[], directed=True + ) + + # test adding and removing vertices + for i, _ in enumerate(random_vertices1): + undirected_graph.add_vertex(random_vertices2[i]) + directed_graph.add_vertex(random_vertices2[i]) + + self.__assert_graph_vertex_exists_check( + undirected_graph, directed_graph, random_vertices2[i] + ) + + undirected_graph.remove_vertex(random_vertices1[i]) + directed_graph.remove_vertex(random_vertices1[i]) + + self.__assert_graph_vertex_does_not_exist_check( + undirected_graph, directed_graph, random_vertices1[i] + ) + + # remove all vertices + for i, _ in enumerate(random_vertices1): + undirected_graph.remove_vertex(random_vertices2[i]) + directed_graph.remove_vertex(random_vertices2[i]) + + self.__assert_graph_vertex_does_not_exist_check( + undirected_graph, directed_graph, random_vertices2[i] + ) + + def test_contains_edge(self) -> None: + # generate graphs and graph input + vertex_count = 20 + ( + undirected_graph, + directed_graph, + random_vertices, + random_edges, + ) = self.__generate_graphs(vertex_count, 0, 100, 4) + + # generate all possible edges for testing + all_possible_edges: list[list[int]] = [] + for i in range(vertex_count - 1): + for j in range(i + 1, vertex_count): + all_possible_edges.append([random_vertices[i], random_vertices[j]]) + all_possible_edges.append([random_vertices[j], random_vertices[i]]) + + # test contains_edge function + for edge in all_possible_edges: + if edge in random_edges: + self.__assert_graph_edge_exists_check( + undirected_graph, directed_graph, edge + ) + elif [edge[1], edge[0]] in random_edges: + # since this edge exists for undirected but the reverse may + # not exist for directed + self.__assert_graph_edge_exists_check( + undirected_graph, directed_graph, [edge[1], edge[0]] + ) + else: + self.__assert_graph_edge_does_not_exist_check( + undirected_graph, directed_graph, edge + ) + + def test_add_edge(self) -> None: + # generate graph input + random_vertices: list[int] = random.sample(range(101), 15) + random_edges: list[list[int]] = self.__generate_random_edges(random_vertices, 4) + + # build graphs WITHOUT edges + undirected_graph = GraphAdjacencyMatrix( + vertices=random_vertices, edges=[], directed=False + ) + directed_graph = GraphAdjacencyMatrix( + vertices=random_vertices, edges=[], directed=True + ) + + # run and test add_edge + for edge in random_edges: + undirected_graph.add_edge(edge[0], edge[1]) + directed_graph.add_edge(edge[0], edge[1]) + self.__assert_graph_edge_exists_check( + undirected_graph, directed_graph, edge + ) + + def test_remove_edge(self) -> None: + # generate graph input and graphs + ( + undirected_graph, + directed_graph, + random_vertices, + random_edges, + ) = self.__generate_graphs(20, 0, 100, 4) + + # run and test remove_edge + for edge in random_edges: + self.__assert_graph_edge_exists_check( + undirected_graph, directed_graph, edge + ) + undirected_graph.remove_edge(edge[0], edge[1]) + directed_graph.remove_edge(edge[0], edge[1]) + self.__assert_graph_edge_does_not_exist_check( + undirected_graph, directed_graph, edge + ) + + def test_add_and_remove_edges_repeatedly(self) -> None: + ( + undirected_graph, + directed_graph, + random_vertices, + random_edges, + ) = self.__generate_graphs(20, 0, 100, 4) + + # make some more edge options! + more_random_edges: list[list[int]] = [] + + while len(more_random_edges) != len(random_edges): + edges: list[list[int]] = self.__generate_random_edges(random_vertices, 4) + for edge in edges: + if len(more_random_edges) == len(random_edges): + break + elif edge not in more_random_edges and edge not in random_edges: + more_random_edges.append(edge) + + for i, _ in enumerate(random_edges): + undirected_graph.add_edge(more_random_edges[i][0], more_random_edges[i][1]) + directed_graph.add_edge(more_random_edges[i][0], more_random_edges[i][1]) + + self.__assert_graph_edge_exists_check( + undirected_graph, directed_graph, more_random_edges[i] + ) + + undirected_graph.remove_edge(random_edges[i][0], random_edges[i][1]) + directed_graph.remove_edge(random_edges[i][0], random_edges[i][1]) + + self.__assert_graph_edge_does_not_exist_check( + undirected_graph, directed_graph, random_edges[i] + ) + + def test_add_vertex_exception_check(self) -> None: + ( + undirected_graph, + directed_graph, + random_vertices, + random_edges, + ) = self.__generate_graphs(20, 0, 100, 4) + + for vertex in random_vertices: + with self.assertRaises(ValueError): + undirected_graph.add_vertex(vertex) + with self.assertRaises(ValueError): + directed_graph.add_vertex(vertex) + + def test_remove_vertex_exception_check(self) -> None: + ( + undirected_graph, + directed_graph, + random_vertices, + random_edges, + ) = self.__generate_graphs(20, 0, 100, 4) + + for i in range(101): + if i not in random_vertices: + with self.assertRaises(ValueError): + undirected_graph.remove_vertex(i) + with self.assertRaises(ValueError): + directed_graph.remove_vertex(i) + + def test_add_edge_exception_check(self) -> None: + ( + undirected_graph, + directed_graph, + random_vertices, + random_edges, + ) = self.__generate_graphs(20, 0, 100, 4) + + for edge in random_edges: + with self.assertRaises(ValueError): + undirected_graph.add_edge(edge[0], edge[1]) + with self.assertRaises(ValueError): + directed_graph.add_edge(edge[0], edge[1]) + + def test_remove_edge_exception_check(self) -> None: + ( + undirected_graph, + directed_graph, + random_vertices, + random_edges, + ) = self.__generate_graphs(20, 0, 100, 4) + + more_random_edges: list[list[int]] = [] + + while len(more_random_edges) != len(random_edges): + edges: list[list[int]] = self.__generate_random_edges(random_vertices, 4) + for edge in edges: + if len(more_random_edges) == len(random_edges): + break + elif edge not in more_random_edges and edge not in random_edges: + more_random_edges.append(edge) + + for edge in more_random_edges: + with self.assertRaises(ValueError): + undirected_graph.remove_edge(edge[0], edge[1]) + with self.assertRaises(ValueError): + directed_graph.remove_edge(edge[0], edge[1]) + + def test_contains_edge_exception_check(self) -> None: + ( + undirected_graph, + directed_graph, + random_vertices, + random_edges, + ) = self.__generate_graphs(20, 0, 100, 4) + + for vertex in random_vertices: + with self.assertRaises(ValueError): + undirected_graph.contains_edge(vertex, 102) + with self.assertRaises(ValueError): + directed_graph.contains_edge(vertex, 102) + + with self.assertRaises(ValueError): + undirected_graph.contains_edge(103, 102) + with self.assertRaises(ValueError): + directed_graph.contains_edge(103, 102) + + +if __name__ == "__main__": + unittest.main() diff --git a/graphs/graph_matrix.py b/graphs/graph_matrix.py deleted file mode 100644 index 4adc6c0bb93b..000000000000 --- a/graphs/graph_matrix.py +++ /dev/null @@ -1,24 +0,0 @@ -class Graph: - def __init__(self, vertex): - self.vertex = vertex - self.graph = [[0] * vertex for i in range(vertex)] - - def add_edge(self, u, v): - self.graph[u - 1][v - 1] = 1 - self.graph[v - 1][u - 1] = 1 - - def show(self): - for i in self.graph: - for j in i: - print(j, end=" ") - print(" ") - - -g = Graph(100) - -g.add_edge(1, 4) -g.add_edge(4, 2) -g.add_edge(4, 5) -g.add_edge(2, 5) -g.add_edge(5, 3) -g.show() diff --git a/graphs/tests/__init__.py b/graphs/tests/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 From 3a9e5fa5ecea0df54ed3ffdcb74f46171199f552 Mon Sep 17 00:00:00 2001 From: Chris O <46587501+ChrisO345@users.noreply.github.com> Date: Fri, 2 Jun 2023 17:14:25 +1200 Subject: [PATCH 089/757] Create a Simultaneous Equation Solver Algorithm (#8773) * Added simultaneous_linear_equation_solver.py * Removed Augment class, replaced with recursive functions * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fixed edge cases * Update settings.json --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- .vscode/settings.json | 5 + maths/simultaneous_linear_equation_solver.py | 142 +++++++++++++++++++ 2 files changed, 147 insertions(+) create mode 100644 .vscode/settings.json create mode 100644 maths/simultaneous_linear_equation_solver.py diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000000..ef16fa1aa7ac --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "githubPullRequests.ignoredPullRequestBranches": [ + "master" + ] +} diff --git a/maths/simultaneous_linear_equation_solver.py b/maths/simultaneous_linear_equation_solver.py new file mode 100644 index 000000000000..1287b2002d00 --- /dev/null +++ b/maths/simultaneous_linear_equation_solver.py @@ -0,0 +1,142 @@ +""" +https://en.wikipedia.org/wiki/Augmented_matrix + +This algorithm solves simultaneous linear equations of the form +λa + λb + λc + λd + ... = γ as [λ, λ, λ, λ, ..., γ] +Where λ & γ are individual coefficients, the no. of equations = no. of coefficients - 1 + +Note in order to work there must exist 1 equation where all instances of λ and γ != 0 +""" + + +def simplify(current_set: list[list]) -> list[list]: + """ + >>> simplify([[1, 2, 3], [4, 5, 6]]) + [[1.0, 2.0, 3.0], [0.0, 0.75, 1.5]] + >>> simplify([[5, 2, 5], [5, 1, 10]]) + [[1.0, 0.4, 1.0], [0.0, 0.2, -1.0]] + """ + # Divide each row by magnitude of first term --> creates 'unit' matrix + duplicate_set = current_set.copy() + for row_index, row in enumerate(duplicate_set): + magnitude = row[0] + for column_index, column in enumerate(row): + if magnitude == 0: + current_set[row_index][column_index] = column + continue + current_set[row_index][column_index] = column / magnitude + # Subtract to cancel term + first_row = current_set[0] + final_set = [first_row] + current_set = current_set[1::] + for row in current_set: + temp_row = [] + # If first term is 0, it is already in form we want, so we preserve it + if row[0] == 0: + final_set.append(row) + continue + for column_index in range(len(row)): + temp_row.append(first_row[column_index] - row[column_index]) + final_set.append(temp_row) + # Create next recursion iteration set + if len(final_set[0]) != 3: + current_first_row = final_set[0] + current_first_column = [] + next_iteration = [] + for row in final_set[1::]: + current_first_column.append(row[0]) + next_iteration.append(row[1::]) + resultant = simplify(next_iteration) + for i in range(len(resultant)): + resultant[i].insert(0, current_first_column[i]) + resultant.insert(0, current_first_row) + final_set = resultant + return final_set + + +def solve_simultaneous(equations: list[list]) -> list: + """ + >>> solve_simultaneous([[1, 2, 3],[4, 5, 6]]) + [-1.0, 2.0] + >>> solve_simultaneous([[0, -3, 1, 7],[3, 2, -1, 11],[5, 1, -2, 12]]) + [6.4, 1.2, 10.6] + >>> solve_simultaneous([]) + Traceback (most recent call last): + ... + IndexError: solve_simultaneous() requires n lists of length n+1 + >>> solve_simultaneous([[1, 2, 3],[1, 2]]) + Traceback (most recent call last): + ... + IndexError: solve_simultaneous() requires n lists of length n+1 + >>> solve_simultaneous([[1, 2, 3],["a", 7, 8]]) + Traceback (most recent call last): + ... + ValueError: solve_simultaneous() requires lists of integers + >>> solve_simultaneous([[0, 2, 3],[4, 0, 6]]) + Traceback (most recent call last): + ... + ValueError: solve_simultaneous() requires at least 1 full equation + """ + if len(equations) == 0: + raise IndexError("solve_simultaneous() requires n lists of length n+1") + _length = len(equations) + 1 + if any(len(item) != _length for item in equations): + raise IndexError("solve_simultaneous() requires n lists of length n+1") + for row in equations: + if any(not isinstance(column, (int, float)) for column in row): + raise ValueError("solve_simultaneous() requires lists of integers") + if len(equations) == 1: + return [equations[0][-1] / equations[0][0]] + data_set = equations.copy() + if any(0 in row for row in data_set): + temp_data = data_set.copy() + full_row = [] + for row_index, row in enumerate(temp_data): + if 0 not in row: + full_row = data_set.pop(row_index) + break + if not full_row: + raise ValueError("solve_simultaneous() requires at least 1 full equation") + data_set.insert(0, full_row) + useable_form = data_set.copy() + simplified = simplify(useable_form) + simplified = simplified[::-1] + solutions: list = [] + for row in simplified: + current_solution = row[-1] + if not solutions: + if row[-2] == 0: + solutions.append(0) + continue + solutions.append(current_solution / row[-2]) + continue + temp_row = row.copy()[: len(row) - 1 :] + while temp_row[0] == 0: + temp_row.pop(0) + if len(temp_row) == 0: + solutions.append(0) + continue + temp_row = temp_row[1::] + temp_row = temp_row[::-1] + for column_index, column in enumerate(temp_row): + current_solution -= column * solutions[column_index] + solutions.append(current_solution) + final = [] + for item in solutions: + final.append(float(round(item, 5))) + return final[::-1] + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + eq = [ + [2, 1, 1, 1, 1, 4], + [1, 2, 1, 1, 1, 5], + [1, 1, 2, 1, 1, 6], + [1, 1, 1, 2, 1, 7], + [1, 1, 1, 1, 2, 8], + ] + print(solve_simultaneous(eq)) + print(solve_simultaneous([[4, 2]])) From 80d95fccc390d366a9f617d8628a546a7be7b2a3 Mon Sep 17 00:00:00 2001 From: Caeden Perelli-Harris Date: Sat, 3 Jun 2023 17:16:33 +0100 Subject: [PATCH 090/757] Pytest locally fails due to API_KEY env variable (#8738) * fix: Pytest locally fails due to API_KEY env variable (#8737) * chore: Fix ruff errors --- web_programming/currency_converter.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/web_programming/currency_converter.py b/web_programming/currency_converter.py index 69f2a2c4d421..3bbcafa8f89b 100644 --- a/web_programming/currency_converter.py +++ b/web_programming/currency_converter.py @@ -8,13 +8,7 @@ import requests URL_BASE = "https://www.amdoren.com/api/currency.php" -TESTING = os.getenv("CI", "") -API_KEY = os.getenv("AMDOREN_API_KEY", "") -if not API_KEY and not TESTING: - raise KeyError( - "API key must be provided in the 'AMDOREN_API_KEY' environment variable." - ) # Currency and their description list_of_currencies = """ @@ -175,20 +169,31 @@ def convert_currency( - from_: str = "USD", to: str = "INR", amount: float = 1.0, api_key: str = API_KEY + from_: str = "USD", to: str = "INR", amount: float = 1.0, api_key: str = "" ) -> str: """https://www.amdoren.com/currency-api/""" + # Instead of manually generating parameters params = locals() + # from is a reserved keyword params["from"] = params.pop("from_") res = requests.get(URL_BASE, params=params).json() return str(res["amount"]) if res["error"] == 0 else res["error_message"] if __name__ == "__main__": + TESTING = os.getenv("CI", "") + API_KEY = os.getenv("AMDOREN_API_KEY", "") + + if not API_KEY and not TESTING: + raise KeyError( + "API key must be provided in the 'AMDOREN_API_KEY' environment variable." + ) + print( convert_currency( input("Enter from currency: ").strip(), input("Enter to currency: ").strip(), float(input("Enter the amount: ").strip()), + API_KEY, ) ) From fa12b9a286bf42d250b30a772e8f226dc14953f4 Mon Sep 17 00:00:00 2001 From: ShivaDahal99 <130563462+ShivaDahal99@users.noreply.github.com> Date: Wed, 7 Jun 2023 23:47:27 +0200 Subject: [PATCH 091/757] Speed of sound (#8803) * Create TestShiva * Delete TestShiva * Add speed of sound * Update physics/speed_of_sound.py Co-authored-by: Christian Clauss * Update physics/speed_of_sound.py Co-authored-by: Christian Clauss * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update speed_of_sound.py * Update speed_of_sound.py --------- Co-authored-by: jlhuhn <134317018+jlhuhn@users.noreply.github.com> Co-authored-by: Christian Clauss Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- physics/speed_of_sound.py | 52 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 physics/speed_of_sound.py diff --git a/physics/speed_of_sound.py b/physics/speed_of_sound.py new file mode 100644 index 000000000000..a4658366a36c --- /dev/null +++ b/physics/speed_of_sound.py @@ -0,0 +1,52 @@ +""" +Title : Calculating the speed of sound + +Description : + The speed of sound (c) is the speed that a sound wave travels + per unit time (m/s). During propagation, the sound wave propagates + through an elastic medium. Its SI unit is meter per second (m/s). + + Only longitudinal waves can propagate in liquids and gas other then + solid where they also travel in transverse wave. The following Algo- + rithem calculates the speed of sound in fluid depanding on the bulk + module and the density of the fluid. + + Equation for calculating speed od sound in fluid: + c_fluid = (K_s*p)**0.5 + + c_fluid: speed of sound in fluid + K_s: isentropic bulk modulus + p: density of fluid + + + +Source : https://en.wikipedia.org/wiki/Speed_of_sound +""" + + +def speed_of_sound_in_a_fluid(density: float, bulk_modulus: float) -> float: + """ + This method calculates the speed of sound in fluid - + This is calculated from the other two provided values + Examples: + Example 1 --> Water 20°C: bulk_moduls= 2.15MPa, density=998kg/m³ + Example 2 --> Murcery 20°: bulk_moduls= 28.5MPa, density=13600kg/m³ + + >>> speed_of_sound_in_a_fluid(bulk_modulus=2.15*10**9, density=998) + 1467.7563207952705 + >>> speed_of_sound_in_a_fluid(bulk_modulus=28.5*10**9, density=13600) + 1447.614670861731 + """ + + if density <= 0: + raise ValueError("Impossible fluid density") + if bulk_modulus <= 0: + raise ValueError("Impossible bulk modulus") + + return (bulk_modulus / density) ** 0.5 + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 7775de0ef779a28cec7d9f28af97a89b2bc29d7e Mon Sep 17 00:00:00 2001 From: Caeden Perelli-Harris Date: Thu, 8 Jun 2023 13:40:38 +0100 Subject: [PATCH 092/757] Create number container system algorithm (#8808) * feat: Create number container system algorithm * updating DIRECTORY.md * chore: Fix failing tests * Update other/number_container_system.py Co-authored-by: Christian Clauss * Update other/number_container_system.py Co-authored-by: Christian Clauss * Update other/number_container_system.py Co-authored-by: Christian Clauss * chore: Add more tests * chore: Create binary_search_insert failing test * type: Update typehints to accept str, list and range --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: Christian Clauss --- DIRECTORY.md | 6 +- other/number_container_system.py | 180 +++++++++++++++++++++++++++++++ 2 files changed, 185 insertions(+), 1 deletion(-) create mode 100644 other/number_container_system.py diff --git a/DIRECTORY.md b/DIRECTORY.md index 231b0e2f1d2f..6dac4a9a5783 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -419,8 +419,9 @@ * [Frequent Pattern Graph Miner](graphs/frequent_pattern_graph_miner.py) * [G Topological Sort](graphs/g_topological_sort.py) * [Gale Shapley Bigraph](graphs/gale_shapley_bigraph.py) + * [Graph Adjacency List](graphs/graph_adjacency_list.py) + * [Graph Adjacency Matrix](graphs/graph_adjacency_matrix.py) * [Graph List](graphs/graph_list.py) - * [Graph Matrix](graphs/graph_matrix.py) * [Graphs Floyd Warshall](graphs/graphs_floyd_warshall.py) * [Greedy Best First](graphs/greedy_best_first.py) * [Greedy Min Vertex Cover](graphs/greedy_min_vertex_cover.py) @@ -479,6 +480,7 @@ * [Lib](linear_algebra/src/lib.py) * [Polynom For Points](linear_algebra/src/polynom_for_points.py) * [Power Iteration](linear_algebra/src/power_iteration.py) + * [Rank Of Matrix](linear_algebra/src/rank_of_matrix.py) * [Rayleigh Quotient](linear_algebra/src/rayleigh_quotient.py) * [Schur Complement](linear_algebra/src/schur_complement.py) * [Test Linear Algebra](linear_algebra/src/test_linear_algebra.py) @@ -651,6 +653,7 @@ * [Sigmoid Linear Unit](maths/sigmoid_linear_unit.py) * [Signum](maths/signum.py) * [Simpson Rule](maths/simpson_rule.py) + * [Simultaneous Linear Equation Solver](maths/simultaneous_linear_equation_solver.py) * [Sin](maths/sin.py) * [Sock Merchant](maths/sock_merchant.py) * [Softmax](maths/softmax.py) @@ -726,6 +729,7 @@ * [Maximum Subarray](other/maximum_subarray.py) * [Maximum Subsequence](other/maximum_subsequence.py) * [Nested Brackets](other/nested_brackets.py) + * [Number Container System](other/number_container_system.py) * [Password](other/password.py) * [Quine](other/quine.py) * [Scoring Algorithm](other/scoring_algorithm.py) diff --git a/other/number_container_system.py b/other/number_container_system.py new file mode 100644 index 000000000000..f547bc8a229e --- /dev/null +++ b/other/number_container_system.py @@ -0,0 +1,180 @@ +""" +A number container system that uses binary search to delete and insert values into +arrays with O(n logn) write times and O(1) read times. + +This container system holds integers at indexes. + +Further explained in this leetcode problem +> https://leetcode.com/problems/minimum-cost-tree-from-leaf-values +""" + + +class NumberContainer: + def __init__(self) -> None: + # numbermap keys are the number and its values are lists of indexes sorted + # in ascending order + self.numbermap: dict[int, list[int]] = {} + # indexmap keys are an index and it's values are the number at that index + self.indexmap: dict[int, int] = {} + + def binary_search_delete(self, array: list | str | range, item: int) -> list[int]: + """ + Removes the item from the sorted array and returns + the new array. + + >>> NumberContainer().binary_search_delete([1,2,3], 2) + [1, 3] + >>> NumberContainer().binary_search_delete([0, 0, 0], 0) + [0, 0] + >>> NumberContainer().binary_search_delete([-1, -1, -1], -1) + [-1, -1] + >>> NumberContainer().binary_search_delete([-1, 0], 0) + [-1] + >>> NumberContainer().binary_search_delete([-1, 0], -1) + [0] + >>> NumberContainer().binary_search_delete(range(7), 3) + [0, 1, 2, 4, 5, 6] + >>> NumberContainer().binary_search_delete([1.1, 2.2, 3.3], 2.2) + [1.1, 3.3] + >>> NumberContainer().binary_search_delete("abcde", "c") + ['a', 'b', 'd', 'e'] + >>> NumberContainer().binary_search_delete([0, -1, 2, 4], 0) + Traceback (most recent call last): + ... + ValueError: Either the item is not in the array or the array was unsorted + >>> NumberContainer().binary_search_delete([2, 0, 4, -1, 11], -1) + Traceback (most recent call last): + ... + ValueError: Either the item is not in the array or the array was unsorted + >>> NumberContainer().binary_search_delete(125, 1) + Traceback (most recent call last): + ... + TypeError: binary_search_delete() only accepts either a list, range or str + """ + if isinstance(array, (range, str)): + array = list(array) + elif not isinstance(array, list): + raise TypeError( + "binary_search_delete() only accepts either a list, range or str" + ) + + low = 0 + high = len(array) - 1 + + while low <= high: + mid = (low + high) // 2 + if array[mid] == item: + array.pop(mid) + return array + elif array[mid] < item: + low = mid + 1 + else: + high = mid - 1 + raise ValueError( + "Either the item is not in the array or the array was unsorted" + ) + + def binary_search_insert(self, array: list | str | range, index: int) -> list[int]: + """ + Inserts the index into the sorted array + at the correct position. + + >>> NumberContainer().binary_search_insert([1,2,3], 2) + [1, 2, 2, 3] + >>> NumberContainer().binary_search_insert([0,1,3], 2) + [0, 1, 2, 3] + >>> NumberContainer().binary_search_insert([-5, -3, 0, 0, 11, 103], 51) + [-5, -3, 0, 0, 11, 51, 103] + >>> NumberContainer().binary_search_insert([-5, -3, 0, 0, 11, 100, 103], 101) + [-5, -3, 0, 0, 11, 100, 101, 103] + >>> NumberContainer().binary_search_insert(range(10), 4) + [0, 1, 2, 3, 4, 4, 5, 6, 7, 8, 9] + >>> NumberContainer().binary_search_insert("abd", "c") + ['a', 'b', 'c', 'd'] + >>> NumberContainer().binary_search_insert(131, 23) + Traceback (most recent call last): + ... + TypeError: binary_search_insert() only accepts either a list, range or str + """ + if isinstance(array, (range, str)): + array = list(array) + elif not isinstance(array, list): + raise TypeError( + "binary_search_insert() only accepts either a list, range or str" + ) + + low = 0 + high = len(array) - 1 + + while low <= high: + mid = (low + high) // 2 + if array[mid] == index: + # If the item already exists in the array, + # insert it after the existing item + array.insert(mid + 1, index) + return array + elif array[mid] < index: + low = mid + 1 + else: + high = mid - 1 + + # If the item doesn't exist in the array, insert it at the appropriate position + array.insert(low, index) + return array + + def change(self, index: int, number: int) -> None: + """ + Changes (sets) the index as number + + >>> cont = NumberContainer() + >>> cont.change(0, 10) + >>> cont.change(0, 20) + >>> cont.change(-13, 20) + >>> cont.change(-100030, 20032903290) + """ + # Remove previous index + if index in self.indexmap: + n = self.indexmap[index] + if len(self.numbermap[n]) == 1: + del self.numbermap[n] + else: + self.numbermap[n] = self.binary_search_delete(self.numbermap[n], index) + + # Set new index + self.indexmap[index] = number + + # Number not seen before or empty so insert number value + if number not in self.numbermap: + self.numbermap[number] = [index] + + # Here we need to perform a binary search insertion in order to insert + # The item in the correct place + else: + self.numbermap[number] = self.binary_search_insert( + self.numbermap[number], index + ) + + def find(self, number: int) -> int: + """ + Returns the smallest index where the number is. + + >>> cont = NumberContainer() + >>> cont.find(10) + -1 + >>> cont.change(0, 10) + >>> cont.find(10) + 0 + >>> cont.change(0, 20) + >>> cont.find(10) + -1 + >>> cont.find(20) + 0 + """ + # Simply return the 0th index (smallest) of the indexes found (or -1) + return self.numbermap.get(number, [-1])[0] + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 9c9da8ebf1d35ae40ac5438c05cc273f7c6d4473 Mon Sep 17 00:00:00 2001 From: Jan Wojciechowski <96974442+yanvoi@users.noreply.github.com> Date: Fri, 9 Jun 2023 11:06:37 +0200 Subject: [PATCH 093/757] Improve readability of ciphers/mixed_keyword_cypher.py (#8626) * refactored the code * the code will now pass the test * looked more into it and fixed the logic * made the code easier to read, added comments and fixed the logic * got rid of redundant code + plaintext can contain chars that are not in the alphabet * fixed the reduntant conversion of ascii_uppercase to a list * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * keyword and plaintext won't have default values * ran the ruff command * Update linear_discriminant_analysis.py and rsa_cipher.py (#8680) * Update rsa_cipher.py by replacing %s with {} * Update rsa_cipher.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update linear_discriminant_analysis.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update linear_discriminant_analysis.py * Update linear_discriminant_analysis.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update linear_discriminant_analysis.py * Update linear_discriminant_analysis.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update linear_discriminant_analysis.py * Update machine_learning/linear_discriminant_analysis.py Co-authored-by: Christian Clauss * Update linear_discriminant_analysis.py * updated --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss * fixed some difficulties * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * added comments, made printing mapping optional, added 1 test * shortened the line that was too long * Update ciphers/mixed_keyword_cypher.py Co-authored-by: Tianyi Zheng --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Rohan Anand <96521078+rohan472000@users.noreply.github.com> Co-authored-by: Christian Clauss Co-authored-by: Tianyi Zheng --- ciphers/mixed_keyword_cypher.py | 100 +++++++++++++++++--------------- 1 file changed, 53 insertions(+), 47 deletions(-) diff --git a/ciphers/mixed_keyword_cypher.py b/ciphers/mixed_keyword_cypher.py index 93a0e3acb7b1..b984808fced6 100644 --- a/ciphers/mixed_keyword_cypher.py +++ b/ciphers/mixed_keyword_cypher.py @@ -1,7 +1,11 @@ -def mixed_keyword(key: str = "college", pt: str = "UNIVERSITY") -> str: - """ +from string import ascii_uppercase + - For key:hello +def mixed_keyword( + keyword: str, plaintext: str, verbose: bool = False, alphabet: str = ascii_uppercase +) -> str: + """ + For keyword: hello H E L O A B C D @@ -12,58 +16,60 @@ def mixed_keyword(key: str = "college", pt: str = "UNIVERSITY") -> str: Y Z and map vertically - >>> mixed_keyword("college", "UNIVERSITY") # doctest: +NORMALIZE_WHITESPACE + >>> mixed_keyword("college", "UNIVERSITY", True) # doctest: +NORMALIZE_WHITESPACE {'A': 'C', 'B': 'A', 'C': 'I', 'D': 'P', 'E': 'U', 'F': 'Z', 'G': 'O', 'H': 'B', 'I': 'J', 'J': 'Q', 'K': 'V', 'L': 'L', 'M': 'D', 'N': 'K', 'O': 'R', 'P': 'W', 'Q': 'E', 'R': 'F', 'S': 'M', 'T': 'S', 'U': 'X', 'V': 'G', 'W': 'H', 'X': 'N', 'Y': 'T', 'Z': 'Y'} 'XKJGUFMJST' + + >>> mixed_keyword("college", "UNIVERSITY", False) # doctest: +NORMALIZE_WHITESPACE + 'XKJGUFMJST' """ - key = key.upper() - pt = pt.upper() - temp = [] - for i in key: - if i not in temp: - temp.append(i) - len_temp = len(temp) - # print(temp) - alpha = [] - modalpha = [] - for j in range(65, 91): - t = chr(j) - alpha.append(t) - if t not in temp: - temp.append(t) - # print(temp) - r = int(26 / 4) - # print(r) - k = 0 - for _ in range(r): - s = [] - for _ in range(len_temp): - s.append(temp[k]) - if k >= 25: - break - k += 1 - modalpha.append(s) - # print(modalpha) - d = {} - j = 0 - k = 0 - for j in range(len_temp): - for m in modalpha: - if not len(m) - 1 >= j: - break - d[alpha[k]] = m[j] - if not k < 25: + keyword = keyword.upper() + plaintext = plaintext.upper() + alphabet_set = set(alphabet) + + # create a list of unique characters in the keyword - their order matters + # it determines how we will map plaintext characters to the ciphertext + unique_chars = [] + for char in keyword: + if char in alphabet_set and char not in unique_chars: + unique_chars.append(char) + # the number of those unique characters will determine the number of rows + num_unique_chars_in_keyword = len(unique_chars) + + # create a shifted version of the alphabet + shifted_alphabet = unique_chars + [ + char for char in alphabet if char not in unique_chars + ] + + # create a modified alphabet by splitting the shifted alphabet into rows + modified_alphabet = [ + shifted_alphabet[k : k + num_unique_chars_in_keyword] + for k in range(0, 26, num_unique_chars_in_keyword) + ] + + # map the alphabet characters to the modified alphabet characters + # going 'vertically' through the modified alphabet - consider columns first + mapping = {} + letter_index = 0 + for column in range(num_unique_chars_in_keyword): + for row in modified_alphabet: + # if current row (the last one) is too short, break out of loop + if len(row) <= column: break - k += 1 - print(d) - cypher = "" - for i in pt: - cypher += d[i] - return cypher + + # map current letter to letter in modified alphabet + mapping[alphabet[letter_index]] = row[column] + letter_index += 1 + + if verbose: + print(mapping) + # create the encrypted text by mapping the plaintext to the modified alphabet + return "".join(mapping[char] if char in mapping else char for char in plaintext) if __name__ == "__main__": + # example use print(mixed_keyword("college", "UNIVERSITY")) From daa0c8f3d340485ce295570e6d76b38891e371bd Mon Sep 17 00:00:00 2001 From: Caeden Perelli-Harris Date: Sat, 10 Jun 2023 13:21:49 +0100 Subject: [PATCH 094/757] Create count negative numbers in matrix algorithm (#8813) * updating DIRECTORY.md * feat: Count negative numbers in sorted matrix * updating DIRECTORY.md * chore: Fix pre-commit * refactor: Combine functions into iteration * style: Reformat reference * feat: Add timings of each implementation * chore: Fix problems with algorithms-keeper bot * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * test: Remove doctest from benchmark function * Update matrix/count_negative_numbers_in_sorted_matrix.py Co-authored-by: Christian Clauss * Update matrix/count_negative_numbers_in_sorted_matrix.py Co-authored-by: Christian Clauss * Update matrix/count_negative_numbers_in_sorted_matrix.py Co-authored-by: Christian Clauss * Update matrix/count_negative_numbers_in_sorted_matrix.py Co-authored-by: Christian Clauss * Update matrix/count_negative_numbers_in_sorted_matrix.py Co-authored-by: Christian Clauss * Update matrix/count_negative_numbers_in_sorted_matrix.py Co-authored-by: Christian Clauss * refactor: Use sum instead of large iteration * refactor: Use len not sum * Update count_negative_numbers_in_sorted_matrix.py --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- DIRECTORY.md | 2 + ...count_negative_numbers_in_sorted_matrix.py | 151 ++++++++++++++++++ 2 files changed, 153 insertions(+) create mode 100644 matrix/count_negative_numbers_in_sorted_matrix.py diff --git a/DIRECTORY.md b/DIRECTORY.md index 6dac4a9a5783..8511c261a3d2 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -679,6 +679,7 @@ ## Matrix * [Binary Search Matrix](matrix/binary_search_matrix.py) * [Count Islands In Matrix](matrix/count_islands_in_matrix.py) + * [Count Negative Numbers In Sorted Matrix](matrix/count_negative_numbers_in_sorted_matrix.py) * [Count Paths](matrix/count_paths.py) * [Cramers Rule 2X2](matrix/cramers_rule_2x2.py) * [Inverse Of Matrix](matrix/inverse_of_matrix.py) @@ -753,6 +754,7 @@ * [Potential Energy](physics/potential_energy.py) * [Rms Speed Of Molecule](physics/rms_speed_of_molecule.py) * [Shear Stress](physics/shear_stress.py) + * [Speed Of Sound](physics/speed_of_sound.py) ## Project Euler * Problem 001 diff --git a/matrix/count_negative_numbers_in_sorted_matrix.py b/matrix/count_negative_numbers_in_sorted_matrix.py new file mode 100644 index 000000000000..2799ff3b45fe --- /dev/null +++ b/matrix/count_negative_numbers_in_sorted_matrix.py @@ -0,0 +1,151 @@ +""" +Given an matrix of numbers in which all rows and all columns are sorted in decreasing +order, return the number of negative numbers in grid. + +Reference: https://leetcode.com/problems/count-negative-numbers-in-a-sorted-matrix +""" + + +def generate_large_matrix() -> list[list[int]]: + """ + >>> generate_large_matrix() # doctest: +ELLIPSIS + [[1000, ..., -999], [999, ..., -1001], ..., [2, ..., -1998]] + """ + return [list(range(1000 - i, -1000 - i, -1)) for i in range(1000)] + + +grid = generate_large_matrix() +test_grids = ( + [[4, 3, 2, -1], [3, 2, 1, -1], [1, 1, -1, -2], [-1, -1, -2, -3]], + [[3, 2], [1, 0]], + [[7, 7, 6]], + [[7, 7, 6], [-1, -2, -3]], + grid, +) + + +def validate_grid(grid: list[list[int]]) -> None: + """ + Validate that the rows and columns of the grid is sorted in decreasing order. + >>> for grid in test_grids: + ... validate_grid(grid) + """ + assert all(row == sorted(row, reverse=True) for row in grid) + assert all(list(col) == sorted(col, reverse=True) for col in zip(*grid)) + + +def find_negative_index(array: list[int]) -> int: + """ + Find the smallest negative index + + >>> find_negative_index([0,0,0,0]) + 4 + >>> find_negative_index([4,3,2,-1]) + 3 + >>> find_negative_index([1,0,-1,-10]) + 2 + >>> find_negative_index([0,0,0,-1]) + 3 + >>> find_negative_index([11,8,7,-3,-5,-9]) + 3 + >>> find_negative_index([-1,-1,-2,-3]) + 0 + >>> find_negative_index([5,1,0]) + 3 + >>> find_negative_index([-5,-5,-5]) + 0 + >>> find_negative_index([0]) + 1 + >>> find_negative_index([]) + 0 + """ + left = 0 + right = len(array) - 1 + + # Edge cases such as no values or all numbers are negative. + if not array or array[0] < 0: + return 0 + + while right + 1 > left: + mid = (left + right) // 2 + num = array[mid] + + # Num must be negative and the index must be greater than or equal to 0. + if num < 0 and array[mid - 1] >= 0: + return mid + + if num >= 0: + left = mid + 1 + else: + right = mid - 1 + # No negative numbers so return the last index of the array + 1 which is the length. + return len(array) + + +def count_negatives_binary_search(grid: list[list[int]]) -> int: + """ + An O(m logn) solution that uses binary search in order to find the boundary between + positive and negative numbers + + >>> [count_negatives_binary_search(grid) for grid in test_grids] + [8, 0, 0, 3, 1498500] + """ + total = 0 + bound = len(grid[0]) + + for i in range(len(grid)): + bound = find_negative_index(grid[i][:bound]) + total += bound + return (len(grid) * len(grid[0])) - total + + +def count_negatives_brute_force(grid: list[list[int]]) -> int: + """ + This solution is O(n^2) because it iterates through every column and row. + + >>> [count_negatives_brute_force(grid) for grid in test_grids] + [8, 0, 0, 3, 1498500] + """ + return len([number for row in grid for number in row if number < 0]) + + +def count_negatives_brute_force_with_break(grid: list[list[int]]) -> int: + """ + Similar to the brute force solution above but uses break in order to reduce the + number of iterations. + + >>> [count_negatives_brute_force_with_break(grid) for grid in test_grids] + [8, 0, 0, 3, 1498500] + """ + total = 0 + for row in grid: + for i, number in enumerate(row): + if number < 0: + total += len(row) - i + break + return total + + +def benchmark() -> None: + """Benchmark our functions next to each other""" + from timeit import timeit + + print("Running benchmarks") + setup = ( + "from __main__ import count_negatives_binary_search, " + "count_negatives_brute_force, count_negatives_brute_force_with_break, grid" + ) + for func in ( + "count_negatives_binary_search", # took 0.7727 seconds + "count_negatives_brute_force_with_break", # took 4.6505 seconds + "count_negatives_brute_force", # took 12.8160 seconds + ): + time = timeit(f"{func}(grid=grid)", setup=setup, number=500) + print(f"{func}() took {time:0.4f} seconds") + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + benchmark() From 46379861257d43bb7140d261094bf17dc414950f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 13 Jun 2023 00:09:33 +0200 Subject: [PATCH 095/757] [pre-commit.ci] pre-commit autoupdate (#8817) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/charliermarsh/ruff-pre-commit: v0.0.270 → v0.0.272](https://github.com/charliermarsh/ruff-pre-commit/compare/v0.0.270...v0.0.272) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4c70ae219f74..1d4b73681108 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/charliermarsh/ruff-pre-commit - rev: v0.0.270 + rev: v0.0.272 hooks: - id: ruff From e6f89a6b89941ffed911e96362be3611a45420e7 Mon Sep 17 00:00:00 2001 From: Ilkin Mengusoglu <113149540+imengus@users.noreply.github.com> Date: Sun, 18 Jun 2023 17:00:02 +0100 Subject: [PATCH 096/757] Simplex algorithm (#8825) * feat: added simplex.py * added docstrings * Update linear_programming/simplex.py Co-authored-by: Caeden Perelli-Harris * Update linear_programming/simplex.py Co-authored-by: Caeden Perelli-Harris * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update linear_programming/simplex.py Co-authored-by: Caeden Perelli-Harris * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * ruff fix Co-authored by: CaedenPH * removed README to add in separate PR * Update linear_programming/simplex.py Co-authored-by: Tianyi Zheng * Update linear_programming/simplex.py Co-authored-by: Tianyi Zheng * fix class docstring * add comments --------- Co-authored-by: Caeden Perelli-Harris Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Tianyi Zheng --- linear_programming/simplex.py | 311 ++++++++++++++++++++++++++++++++++ 1 file changed, 311 insertions(+) create mode 100644 linear_programming/simplex.py diff --git a/linear_programming/simplex.py b/linear_programming/simplex.py new file mode 100644 index 000000000000..ba64add40b5f --- /dev/null +++ b/linear_programming/simplex.py @@ -0,0 +1,311 @@ +""" +Python implementation of the simplex algorithm for solving linear programs in +tabular form with +- `>=`, `<=`, and `=` constraints and +- each variable `x1, x2, ...>= 0`. + +See https://gist.github.com/imengus/f9619a568f7da5bc74eaf20169a24d98 for how to +convert linear programs to simplex tableaus, and the steps taken in the simplex +algorithm. + +Resources: +https://en.wikipedia.org/wiki/Simplex_algorithm +https://tinyurl.com/simplex4beginners +""" +from typing import Any + +import numpy as np + + +class Tableau: + """Operate on simplex tableaus + + >>> t = Tableau(np.array([[-1,-1,0,0,-1],[1,3,1,0,4],[3,1,0,1,4.]]), 2) + Traceback (most recent call last): + ... + ValueError: RHS must be > 0 + """ + + def __init__(self, tableau: np.ndarray, n_vars: int) -> None: + # Check if RHS is negative + if np.any(tableau[:, -1], where=tableau[:, -1] < 0): + raise ValueError("RHS must be > 0") + + self.tableau = tableau + self.n_rows, _ = tableau.shape + + # Number of decision variables x1, x2, x3... + self.n_vars = n_vars + + # Number of artificial variables to be minimised + self.n_art_vars = len(np.where(tableau[self.n_vars : -1] == -1)[0]) + + # 2 if there are >= or == constraints (nonstandard), 1 otherwise (std) + self.n_stages = (self.n_art_vars > 0) + 1 + + # Number of slack variables added to make inequalities into equalities + self.n_slack = self.n_rows - self.n_stages + + # Objectives for each stage + self.objectives = ["max"] + + # In two stage simplex, first minimise then maximise + if self.n_art_vars: + self.objectives.append("min") + + self.col_titles = [""] + + # Index of current pivot row and column + self.row_idx = None + self.col_idx = None + + # Does objective row only contain (non)-negative values? + self.stop_iter = False + + @staticmethod + def generate_col_titles(*args: int) -> list[str]: + """Generate column titles for tableau of specific dimensions + + >>> Tableau.generate_col_titles(2, 3, 1) + ['x1', 'x2', 's1', 's2', 's3', 'a1', 'RHS'] + + >>> Tableau.generate_col_titles() + Traceback (most recent call last): + ... + ValueError: Must provide n_vars, n_slack, and n_art_vars + >>> Tableau.generate_col_titles(-2, 3, 1) + Traceback (most recent call last): + ... + ValueError: All arguments must be non-negative integers + """ + if len(args) != 3: + raise ValueError("Must provide n_vars, n_slack, and n_art_vars") + + if not all(x >= 0 and isinstance(x, int) for x in args): + raise ValueError("All arguments must be non-negative integers") + + # decision | slack | artificial + string_starts = ["x", "s", "a"] + titles = [] + for i in range(3): + for j in range(args[i]): + titles.append(string_starts[i] + str(j + 1)) + titles.append("RHS") + return titles + + def find_pivot(self, tableau: np.ndarray) -> tuple[Any, Any]: + """Finds the pivot row and column. + >>> t = Tableau(np.array([[-2,1,0,0,0], [3,1,1,0,6], [1,2,0,1,7.]]), 2) + >>> t.find_pivot(t.tableau) + (1, 0) + """ + objective = self.objectives[-1] + + # Find entries of highest magnitude in objective rows + sign = (objective == "min") - (objective == "max") + col_idx = np.argmax(sign * tableau[0, : self.n_vars]) + + # Choice is only valid if below 0 for maximise, and above for minimise + if sign * self.tableau[0, col_idx] <= 0: + self.stop_iter = True + return 0, 0 + + # Pivot row is chosen as having the lowest quotient when elements of + # the pivot column divide the right-hand side + + # Slice excluding the objective rows + s = slice(self.n_stages, self.n_rows) + + # RHS + dividend = tableau[s, -1] + + # Elements of pivot column within slice + divisor = tableau[s, col_idx] + + # Array filled with nans + nans = np.full(self.n_rows - self.n_stages, np.nan) + + # If element in pivot column is greater than zeron_stages, return + # quotient or nan otherwise + quotients = np.divide(dividend, divisor, out=nans, where=divisor > 0) + + # Arg of minimum quotient excluding the nan values. n_stages is added + # to compensate for earlier exclusion of objective columns + row_idx = np.nanargmin(quotients) + self.n_stages + return row_idx, col_idx + + def pivot(self, tableau: np.ndarray, row_idx: int, col_idx: int) -> np.ndarray: + """Pivots on value on the intersection of pivot row and column. + + >>> t = Tableau(np.array([[-2,-3,0,0,0],[1,3,1,0,4],[3,1,0,1,4.]]), 2) + >>> t.pivot(t.tableau, 1, 0).tolist() + ... # doctest: +NORMALIZE_WHITESPACE + [[0.0, 3.0, 2.0, 0.0, 8.0], + [1.0, 3.0, 1.0, 0.0, 4.0], + [0.0, -8.0, -3.0, 1.0, -8.0]] + """ + # Avoid changes to original tableau + piv_row = tableau[row_idx].copy() + + piv_val = piv_row[col_idx] + + # Entry becomes 1 + piv_row *= 1 / piv_val + + # Variable in pivot column becomes basic, ie the only non-zero entry + for idx, coeff in enumerate(tableau[:, col_idx]): + tableau[idx] += -coeff * piv_row + tableau[row_idx] = piv_row + return tableau + + def change_stage(self, tableau: np.ndarray) -> np.ndarray: + """Exits first phase of the two-stage method by deleting artificial + rows and columns, or completes the algorithm if exiting the standard + case. + + >>> t = Tableau(np.array([ + ... [3, 3, -1, -1, 0, 0, 4], + ... [2, 1, 0, 0, 0, 0, 0.], + ... [1, 2, -1, 0, 1, 0, 2], + ... [2, 1, 0, -1, 0, 1, 2] + ... ]), 2) + >>> t.change_stage(t.tableau).tolist() + ... # doctest: +NORMALIZE_WHITESPACE + [[2.0, 1.0, 0.0, 0.0, 0.0, 0.0], + [1.0, 2.0, -1.0, 0.0, 1.0, 2.0], + [2.0, 1.0, 0.0, -1.0, 0.0, 2.0]] + """ + # Objective of original objective row remains + self.objectives.pop() + + if not self.objectives: + return tableau + + # Slice containing ids for artificial columns + s = slice(-self.n_art_vars - 1, -1) + + # Delete the artificial variable columns + tableau = np.delete(tableau, s, axis=1) + + # Delete the objective row of the first stage + tableau = np.delete(tableau, 0, axis=0) + + self.n_stages = 1 + self.n_rows -= 1 + self.n_art_vars = 0 + self.stop_iter = False + return tableau + + def run_simplex(self) -> dict[Any, Any]: + """Operate on tableau until objective function cannot be + improved further. + + # Standard linear program: + Max: x1 + x2 + ST: x1 + 3x2 <= 4 + 3x1 + x2 <= 4 + >>> Tableau(np.array([[-1,-1,0,0,0],[1,3,1,0,4],[3,1,0,1,4.]]), + ... 2).run_simplex() + {'P': 2.0, 'x1': 1.0, 'x2': 1.0} + + # Optimal tableau input: + >>> Tableau(np.array([ + ... [0, 0, 0.25, 0.25, 2], + ... [0, 1, 0.375, -0.125, 1], + ... [1, 0, -0.125, 0.375, 1] + ... ]), 2).run_simplex() + {'P': 2.0, 'x1': 1.0, 'x2': 1.0} + + # Non-standard: >= constraints + Max: 2x1 + 3x2 + x3 + ST: x1 + x2 + x3 <= 40 + 2x1 + x2 - x3 >= 10 + - x2 + x3 >= 10 + >>> Tableau(np.array([ + ... [2, 0, 0, 0, -1, -1, 0, 0, 20], + ... [-2, -3, -1, 0, 0, 0, 0, 0, 0], + ... [1, 1, 1, 1, 0, 0, 0, 0, 40], + ... [2, 1, -1, 0, -1, 0, 1, 0, 10], + ... [0, -1, 1, 0, 0, -1, 0, 1, 10.] + ... ]), 3).run_simplex() + {'P': 70.0, 'x1': 10.0, 'x2': 10.0, 'x3': 20.0} + + # Non standard: minimisation and equalities + Min: x1 + x2 + ST: 2x1 + x2 = 12 + 6x1 + 5x2 = 40 + >>> Tableau(np.array([ + ... [8, 6, 0, -1, 0, -1, 0, 0, 52], + ... [1, 1, 0, 0, 0, 0, 0, 0, 0], + ... [2, 1, 1, 0, 0, 0, 0, 0, 12], + ... [2, 1, 0, -1, 0, 0, 1, 0, 12], + ... [6, 5, 0, 0, 1, 0, 0, 0, 40], + ... [6, 5, 0, 0, 0, -1, 0, 1, 40.] + ... ]), 2).run_simplex() + {'P': 7.0, 'x1': 5.0, 'x2': 2.0} + """ + # Stop simplex algorithm from cycling. + for _ in range(100): + # Completion of each stage removes an objective. If both stages + # are complete, then no objectives are left + if not self.objectives: + self.col_titles = self.generate_col_titles( + self.n_vars, self.n_slack, self.n_art_vars + ) + + # Find the values of each variable at optimal solution + return self.interpret_tableau(self.tableau, self.col_titles) + + row_idx, col_idx = self.find_pivot(self.tableau) + + # If there are no more negative values in objective row + if self.stop_iter: + # Delete artificial variable columns and rows. Update attributes + self.tableau = self.change_stage(self.tableau) + else: + self.tableau = self.pivot(self.tableau, row_idx, col_idx) + return {} + + def interpret_tableau( + self, tableau: np.ndarray, col_titles: list[str] + ) -> dict[str, float]: + """Given the final tableau, add the corresponding values of the basic + decision variables to the `output_dict` + >>> tableau = np.array([ + ... [0,0,0.875,0.375,5], + ... [0,1,0.375,-0.125,1], + ... [1,0,-0.125,0.375,1] + ... ]) + >>> t = Tableau(tableau, 2) + >>> t.interpret_tableau(tableau, ["x1", "x2", "s1", "s2", "RHS"]) + {'P': 5.0, 'x1': 1.0, 'x2': 1.0} + """ + # P = RHS of final tableau + output_dict = {"P": abs(tableau[0, -1])} + + for i in range(self.n_vars): + # Gives ids of nonzero entries in the ith column + nonzero = np.nonzero(tableau[:, i]) + n_nonzero = len(nonzero[0]) + + # First entry in the nonzero ids + nonzero_rowidx = nonzero[0][0] + nonzero_val = tableau[nonzero_rowidx, i] + + # If there is only one nonzero value in column, which is one + if n_nonzero == nonzero_val == 1: + rhs_val = tableau[nonzero_rowidx, -1] + output_dict[col_titles[i]] = rhs_val + + # Check for basic variables + for title in col_titles: + # Don't add RHS or slack variables to output dict + if title[0] not in "R-s-a": + output_dict.setdefault(title, 0) + return output_dict + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From b0f871032e78dd1d2f2214acbaae2fac88fa55b0 Mon Sep 17 00:00:00 2001 From: Frank-1998 <77809242+Frank-1998@users.noreply.github.com> Date: Sun, 18 Jun 2023 10:30:06 -0600 Subject: [PATCH 097/757] Fix removing the root node in binary_search_tree.py removes the whole tree (#8752) * fix issue #8715 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- data_structures/binary_tree/binary_search_tree.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data_structures/binary_tree/binary_search_tree.py b/data_structures/binary_tree/binary_search_tree.py index cd88cc10e697..c72195424c7c 100644 --- a/data_structures/binary_tree/binary_search_tree.py +++ b/data_structures/binary_tree/binary_search_tree.py @@ -40,7 +40,7 @@ def __reassign_nodes(self, node: Node, new_children: Node | None) -> None: else: node.parent.left = new_children else: - self.root = None + self.root = new_children def is_right(self, node: Node) -> bool: if node.parent and node.parent.right: From ea6c6056cf2215358834710bf89422310f831178 Mon Sep 17 00:00:00 2001 From: Turro <42980188+smturro2@users.noreply.github.com> Date: Mon, 19 Jun 2023 06:46:29 -0500 Subject: [PATCH 098/757] Added apr_interest function to financial (#6025) * Added apr_interest function to financial * Update interest.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update financial/interest.py * float --------- Co-authored-by: Christian Clauss Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- financial/interest.py | 41 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/financial/interest.py b/financial/interest.py index c69c730457d9..33d02e27ccb3 100644 --- a/financial/interest.py +++ b/financial/interest.py @@ -4,7 +4,7 @@ def simple_interest( - principal: float, daily_interest_rate: float, days_between_payments: int + principal: float, daily_interest_rate: float, days_between_payments: float ) -> float: """ >>> simple_interest(18000.0, 0.06, 3) @@ -42,7 +42,7 @@ def simple_interest( def compound_interest( principal: float, nominal_annual_interest_rate_percentage: float, - number_of_compounding_periods: int, + number_of_compounding_periods: float, ) -> float: """ >>> compound_interest(10000.0, 0.05, 3) @@ -77,6 +77,43 @@ def compound_interest( ) +def apr_interest( + principal: float, + nominal_annual_percentage_rate: float, + number_of_years: float, +) -> float: + """ + >>> apr_interest(10000.0, 0.05, 3) + 1618.223072263547 + >>> apr_interest(10000.0, 0.05, 1) + 512.6749646744732 + >>> apr_interest(0.5, 0.05, 3) + 0.08091115361317736 + >>> apr_interest(10000.0, 0.06, -4) + Traceback (most recent call last): + ... + ValueError: number_of_years must be > 0 + >>> apr_interest(10000.0, -3.5, 3.0) + Traceback (most recent call last): + ... + ValueError: nominal_annual_percentage_rate must be >= 0 + >>> apr_interest(-5500.0, 0.01, 5) + Traceback (most recent call last): + ... + ValueError: principal must be > 0 + """ + if number_of_years <= 0: + raise ValueError("number_of_years must be > 0") + if nominal_annual_percentage_rate < 0: + raise ValueError("nominal_annual_percentage_rate must be >= 0") + if principal <= 0: + raise ValueError("principal must be > 0") + + return compound_interest( + principal, nominal_annual_percentage_rate / 365, number_of_years * 365 + ) + + if __name__ == "__main__": import doctest From 0dee4a402c85981af0c2d4c53af27a69a7eb91bf Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 20 Jun 2023 15:56:14 +0200 Subject: [PATCH 099/757] [pre-commit.ci] pre-commit autoupdate (#8827) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/codespell-project/codespell: v2.2.4 → v2.2.5](https://github.com/codespell-project/codespell/compare/v2.2.4...v2.2.5) - [github.com/tox-dev/pyproject-fmt: 0.11.2 → 0.12.0](https://github.com/tox-dev/pyproject-fmt/compare/0.11.2...0.12.0) * updating DIRECTORY.md --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- .pre-commit-config.yaml | 4 ++-- DIRECTORY.md | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1d4b73681108..591fd7819a5a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -26,14 +26,14 @@ repos: - id: black - repo: https://github.com/codespell-project/codespell - rev: v2.2.4 + rev: v2.2.5 hooks: - id: codespell additional_dependencies: - tomli - repo: https://github.com/tox-dev/pyproject-fmt - rev: "0.11.2" + rev: "0.12.0" hooks: - id: pyproject-fmt diff --git a/DIRECTORY.md b/DIRECTORY.md index 8511c261a3d2..6ec8d5111176 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -486,6 +486,9 @@ * [Test Linear Algebra](linear_algebra/src/test_linear_algebra.py) * [Transformations 2D](linear_algebra/src/transformations_2d.py) +## Linear Programming + * [Simplex](linear_programming/simplex.py) + ## Machine Learning * [Astar](machine_learning/astar.py) * [Data Transformations](machine_learning/data_transformations.py) From 07e68128883b84fb7e342c6bce88863a05fbbf62 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Tue, 20 Jun 2023 18:03:16 +0200 Subject: [PATCH 100/757] Update .pre-commit-config.yaml (#8828) * Update .pre-commit-config.yaml * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- pyproject.toml | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index a526196685f5..1dcce044a313 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,21 +1,3 @@ -[tool.pytest.ini_options] -markers = [ - "mat_ops: mark a test as utilizing matrix operations.", -] -addopts = [ - "--durations=10", - "--doctest-modules", - "--showlocals", -] - -[tool.coverage.report] -omit = [".env/*"] -sort = "Cover" - -[tool.codespell] -ignore-words-list = "3rt,ans,crate,damon,fo,followings,hist,iff,kwanza,mater,secant,som,sur,tim,zar" -skip = "./.*,*.json,ciphers/prehistoric_men.txt,project_euler/problem_022/p022_names.txt,pyproject.toml,strings/dictionary.txt,strings/words.txt" - [tool.ruff] ignore = [ # `ruff rule S101` for a description of that rule "ARG001", # Unused function argument `amount` -- FIX ME? @@ -131,3 +113,21 @@ max-args = 10 # default: 5 max-branches = 20 # default: 12 max-returns = 8 # default: 6 max-statements = 88 # default: 50 + +[tool.pytest.ini_options] +markers = [ + "mat_ops: mark a test as utilizing matrix operations.", +] +addopts = [ + "--durations=10", + "--doctest-modules", + "--showlocals", +] + +[tool.coverage.report] +omit = [".env/*"] +sort = "Cover" + +[tool.codespell] +ignore-words-list = "3rt,ans,crate,damon,fo,followings,hist,iff,kwanza,mater,secant,som,sur,tim,zar" +skip = "./.*,*.json,ciphers/prehistoric_men.txt,project_euler/problem_022/p022_names.txt,pyproject.toml,strings/dictionary.txt,strings/words.txt" From 5b0890bd833eb85c58fae9afc4984d520e7e2ad6 Mon Sep 17 00:00:00 2001 From: "Linus M. Henkel" <86628476+linushenkel@users.noreply.github.com> Date: Thu, 22 Jun 2023 13:49:09 +0200 Subject: [PATCH 101/757] Dijkstra algorithm with binary grid (#8802) * Create TestShiva * Delete TestShiva * Implementation of the Dijkstra-Algorithm in a binary grid * Update double_ended_queue.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update least_common_multiple.py * Update sol1.py * Update pyproject.toml * Update pyproject.toml * https://github.com/astral-sh/ruff-pre-commit v0.0.274 --------- Co-authored-by: ShivaDahal99 <130563462+ShivaDahal99@users.noreply.github.com> Co-authored-by: jlhuhn <134317018+jlhuhn@users.noreply.github.com> Co-authored-by: Christian Clauss Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 4 +- data_structures/queue/double_ended_queue.py | 4 +- graphs/dijkstra_binary_grid.py | 89 +++++++++++++++++++++ maths/least_common_multiple.py | 6 +- project_euler/problem_054/sol1.py | 18 ++--- pyproject.toml | 1 + 6 files changed, 106 insertions(+), 16 deletions(-) create mode 100644 graphs/dijkstra_binary_grid.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 591fd7819a5a..3d4cc4084ccf 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -15,8 +15,8 @@ repos: hooks: - id: auto-walrus - - repo: https://github.com/charliermarsh/ruff-pre-commit - rev: v0.0.272 + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.0.274 hooks: - id: ruff diff --git a/data_structures/queue/double_ended_queue.py b/data_structures/queue/double_ended_queue.py index 637b7f62fd2c..2472371b42fe 100644 --- a/data_structures/queue/double_ended_queue.py +++ b/data_structures/queue/double_ended_queue.py @@ -32,7 +32,7 @@ class Deque: the number of nodes """ - __slots__ = ["_front", "_back", "_len"] + __slots__ = ("_front", "_back", "_len") @dataclass class _Node: @@ -54,7 +54,7 @@ class _Iterator: the current node of the iteration. """ - __slots__ = ["_cur"] + __slots__ = "_cur" def __init__(self, cur: Deque._Node | None) -> None: self._cur = cur diff --git a/graphs/dijkstra_binary_grid.py b/graphs/dijkstra_binary_grid.py new file mode 100644 index 000000000000..c23d8234328a --- /dev/null +++ b/graphs/dijkstra_binary_grid.py @@ -0,0 +1,89 @@ +""" +This script implements the Dijkstra algorithm on a binary grid. +The grid consists of 0s and 1s, where 1 represents +a walkable node and 0 represents an obstacle. +The algorithm finds the shortest path from a start node to a destination node. +Diagonal movement can be allowed or disallowed. +""" + +from heapq import heappop, heappush + +import numpy as np + + +def dijkstra( + grid: np.ndarray, + source: tuple[int, int], + destination: tuple[int, int], + allow_diagonal: bool, +) -> tuple[float | int, list[tuple[int, int]]]: + """ + Implements Dijkstra's algorithm on a binary grid. + + Args: + grid (np.ndarray): A 2D numpy array representing the grid. + 1 represents a walkable node and 0 represents an obstacle. + source (Tuple[int, int]): A tuple representing the start node. + destination (Tuple[int, int]): A tuple representing the + destination node. + allow_diagonal (bool): A boolean determining whether + diagonal movements are allowed. + + Returns: + Tuple[Union[float, int], List[Tuple[int, int]]]: + The shortest distance from the start node to the destination node + and the shortest path as a list of nodes. + + >>> dijkstra(np.array([[1, 1, 1], [0, 1, 0], [0, 1, 1]]), (0, 0), (2, 2), False) + (4.0, [(0, 0), (0, 1), (1, 1), (2, 1), (2, 2)]) + + >>> dijkstra(np.array([[1, 1, 1], [0, 1, 0], [0, 1, 1]]), (0, 0), (2, 2), True) + (2.0, [(0, 0), (1, 1), (2, 2)]) + + >>> dijkstra(np.array([[1, 1, 1], [0, 0, 1], [0, 1, 1]]), (0, 0), (2, 2), False) + (4.0, [(0, 0), (0, 1), (0, 2), (1, 2), (2, 2)]) + """ + rows, cols = grid.shape + dx = [-1, 1, 0, 0] + dy = [0, 0, -1, 1] + if allow_diagonal: + dx += [-1, -1, 1, 1] + dy += [-1, 1, -1, 1] + + queue, visited = [(0, source)], set() + matrix = np.full((rows, cols), np.inf) + matrix[source] = 0 + predecessors = np.empty((rows, cols), dtype=object) + predecessors[source] = None + + while queue: + (dist, (x, y)) = heappop(queue) + if (x, y) in visited: + continue + visited.add((x, y)) + + if (x, y) == destination: + path = [] + while (x, y) != source: + path.append((x, y)) + x, y = predecessors[x, y] + path.append(source) # add the source manually + path.reverse() + return matrix[destination], path + + for i in range(len(dx)): + nx, ny = x + dx[i], y + dy[i] + if 0 <= nx < rows and 0 <= ny < cols: + next_node = grid[nx][ny] + if next_node == 1 and matrix[nx, ny] > dist + 1: + heappush(queue, (dist + 1, (nx, ny))) + matrix[nx, ny] = dist + 1 + predecessors[nx, ny] = (x, y) + + return np.inf, [] + + +if __name__ == "__main__": + import doctest + + doctest.testmod() diff --git a/maths/least_common_multiple.py b/maths/least_common_multiple.py index 621d93720c41..10cc63ac7990 100644 --- a/maths/least_common_multiple.py +++ b/maths/least_common_multiple.py @@ -67,7 +67,7 @@ def benchmark(): class TestLeastCommonMultiple(unittest.TestCase): - test_inputs = [ + test_inputs = ( (10, 20), (13, 15), (4, 31), @@ -77,8 +77,8 @@ class TestLeastCommonMultiple(unittest.TestCase): (12, 25), (10, 25), (6, 9), - ] - expected_results = [20, 195, 124, 210, 1462, 60, 300, 50, 18] + ) + expected_results = (20, 195, 124, 210, 1462, 60, 300, 50, 18) def test_lcm_function(self): for i, (first_num, second_num) in enumerate(self.test_inputs): diff --git a/project_euler/problem_054/sol1.py b/project_euler/problem_054/sol1.py index 74409f32c712..86dfa5edd2f5 100644 --- a/project_euler/problem_054/sol1.py +++ b/project_euler/problem_054/sol1.py @@ -47,18 +47,18 @@ class PokerHand: """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 + 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 + hand: a 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. + player's hand is better than the opponent's hand. hand_name(): Returns a string made up of two parts: hand name and high card. @@ -66,11 +66,11 @@ class PokerHand: Supported operators: Rich comparison operators: <, >, <=, >=, ==, != - Supported builtin methods and functions: + Supported built-in methods and functions: list.sort(), sorted() """ - _HAND_NAME = [ + _HAND_NAME = ( "High card", "One pair", "Two pairs", @@ -81,10 +81,10 @@ class PokerHand: "Four of a kind", "Straight flush", "Royal flush", - ] + ) - _CARD_NAME = [ - "", # placeholder as lists are zero indexed + _CARD_NAME = ( + "", # placeholder as tuples are zero-indexed "One", "Two", "Three", @@ -99,7 +99,7 @@ class PokerHand: "Queen", "King", "Ace", - ] + ) def __init__(self, hand: str) -> None: """ diff --git a/pyproject.toml b/pyproject.toml index 1dcce044a313..4f21a95190da 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -103,6 +103,7 @@ max-complexity = 17 # default: 10 "machine_learning/linear_discriminant_analysis.py" = ["ARG005"] "machine_learning/sequential_minimum_optimization.py" = ["SIM115"] "matrix/sherman_morrison.py" = ["SIM103", "SIM114"] +"other/l*u_cache.py" = ["RUF012"] "physics/newtons_second_law_of_motion.py" = ["BLE001"] "project_euler/problem_099/sol1.py" = ["SIM115"] "sorts/external_sort.py" = ["SIM115"] From 5ffe601c86a9b44691a4dce37480c6d904102d49 Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Thu, 22 Jun 2023 05:24:34 -0700 Subject: [PATCH 102/757] Fix `mypy` errors in `maths/sigmoid_linear_unit.py` (#8786) * updating DIRECTORY.md * Fix mypy errors in sigmoid_linear_unit.py * updating DIRECTORY.md * updating DIRECTORY.md --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- maths/sigmoid_linear_unit.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/maths/sigmoid_linear_unit.py b/maths/sigmoid_linear_unit.py index a8ada10dd8ec..0ee09bf82d38 100644 --- a/maths/sigmoid_linear_unit.py +++ b/maths/sigmoid_linear_unit.py @@ -17,7 +17,7 @@ import numpy as np -def sigmoid(vector: np.array) -> np.array: +def sigmoid(vector: np.ndarray) -> np.ndarray: """ Mathematical function sigmoid takes a vector x of K real numbers as input and returns 1/ (1 + e^-x). @@ -29,17 +29,15 @@ def sigmoid(vector: np.array) -> np.array: return 1 / (1 + np.exp(-vector)) -def sigmoid_linear_unit(vector: np.array) -> np.array: +def sigmoid_linear_unit(vector: np.ndarray) -> np.ndarray: """ Implements the Sigmoid Linear Unit (SiLU) or swish function Parameters: - vector (np.array): A numpy array consisting of real - values. + vector (np.ndarray): A numpy array consisting of real values Returns: - swish_vec (np.array): The input numpy array, after applying - swish. + swish_vec (np.ndarray): The input numpy array, after applying swish Examples: >>> sigmoid_linear_unit(np.array([-1.0, 1.0, 2.0])) From f54a9668103e560f20b50559fb54ac38a74d1fe8 Mon Sep 17 00:00:00 2001 From: Jan-Lukas Huhn <134317018+jlhuhn@users.noreply.github.com> Date: Thu, 22 Jun 2023 14:31:48 +0200 Subject: [PATCH 103/757] Energy conversions (#8801) * Create TestShiva * Delete TestShiva * Create energy_conversions.py * Update conversions/energy_conversions.py Co-authored-by: Caeden Perelli-Harris --------- Co-authored-by: ShivaDahal99 <130563462+ShivaDahal99@users.noreply.github.com> Co-authored-by: Caeden Perelli-Harris --- conversions/energy_conversions.py | 114 ++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 conversions/energy_conversions.py diff --git a/conversions/energy_conversions.py b/conversions/energy_conversions.py new file mode 100644 index 000000000000..51de6b313928 --- /dev/null +++ b/conversions/energy_conversions.py @@ -0,0 +1,114 @@ +""" +Conversion of energy units. + +Available units: joule, kilojoule, megajoule, gigajoule,\ + wattsecond, watthour, kilowatthour, newtonmeter, calorie_nutr,\ + kilocalorie_nutr, electronvolt, britishthermalunit_it, footpound + +USAGE : +-> Import this file into their respective project. +-> Use the function energy_conversion() for conversion of energy units. +-> Parameters : + -> from_type : From which type you want to convert + -> to_type : To which type you want to convert + -> value : the value which you want to convert + +REFERENCES : +-> Wikipedia reference: https://en.wikipedia.org/wiki/Units_of_energy +-> Wikipedia reference: https://en.wikipedia.org/wiki/Joule +-> Wikipedia reference: https://en.wikipedia.org/wiki/Kilowatt-hour +-> Wikipedia reference: https://en.wikipedia.org/wiki/Newton-metre +-> Wikipedia reference: https://en.wikipedia.org/wiki/Calorie +-> Wikipedia reference: https://en.wikipedia.org/wiki/Electronvolt +-> Wikipedia reference: https://en.wikipedia.org/wiki/British_thermal_unit +-> Wikipedia reference: https://en.wikipedia.org/wiki/Foot-pound_(energy) +-> Unit converter reference: https://www.unitconverters.net/energy-converter.html +""" + +ENERGY_CONVERSION: dict[str, float] = { + "joule": 1.0, + "kilojoule": 1_000, + "megajoule": 1_000_000, + "gigajoule": 1_000_000_000, + "wattsecond": 1.0, + "watthour": 3_600, + "kilowatthour": 3_600_000, + "newtonmeter": 1.0, + "calorie_nutr": 4_186.8, + "kilocalorie_nutr": 4_186_800.00, + "electronvolt": 1.602_176_634e-19, + "britishthermalunit_it": 1_055.055_85, + "footpound": 1.355_818, +} + + +def energy_conversion(from_type: str, to_type: str, value: float) -> float: + """ + Conversion of energy units. + >>> energy_conversion("joule", "joule", 1) + 1.0 + >>> energy_conversion("joule", "kilojoule", 1) + 0.001 + >>> energy_conversion("joule", "megajoule", 1) + 1e-06 + >>> energy_conversion("joule", "gigajoule", 1) + 1e-09 + >>> energy_conversion("joule", "wattsecond", 1) + 1.0 + >>> energy_conversion("joule", "watthour", 1) + 0.0002777777777777778 + >>> energy_conversion("joule", "kilowatthour", 1) + 2.7777777777777776e-07 + >>> energy_conversion("joule", "newtonmeter", 1) + 1.0 + >>> energy_conversion("joule", "calorie_nutr", 1) + 0.00023884589662749592 + >>> energy_conversion("joule", "kilocalorie_nutr", 1) + 2.388458966274959e-07 + >>> energy_conversion("joule", "electronvolt", 1) + 6.241509074460763e+18 + >>> energy_conversion("joule", "britishthermalunit_it", 1) + 0.0009478171226670134 + >>> energy_conversion("joule", "footpound", 1) + 0.7375621211696556 + >>> energy_conversion("joule", "megajoule", 1000) + 0.001 + >>> energy_conversion("calorie_nutr", "kilocalorie_nutr", 1000) + 1.0 + >>> energy_conversion("kilowatthour", "joule", 10) + 36000000.0 + >>> energy_conversion("britishthermalunit_it", "footpound", 1) + 778.1692306784539 + >>> energy_conversion("watthour", "joule", "a") # doctest: +ELLIPSIS + Traceback (most recent call last): + ... + TypeError: unsupported operand type(s) for /: 'str' and 'float' + >>> energy_conversion("wrongunit", "joule", 1) # doctest: +ELLIPSIS + Traceback (most recent call last): + ... + ValueError: Incorrect 'from_type' or 'to_type' value: 'wrongunit', 'joule' + Valid values are: joule, ... footpound + >>> energy_conversion("joule", "wrongunit", 1) # doctest: +ELLIPSIS + Traceback (most recent call last): + ... + ValueError: Incorrect 'from_type' or 'to_type' value: 'joule', 'wrongunit' + Valid values are: joule, ... footpound + >>> energy_conversion("123", "abc", 1) # doctest: +ELLIPSIS + Traceback (most recent call last): + ... + ValueError: Incorrect 'from_type' or 'to_type' value: '123', 'abc' + Valid values are: joule, ... footpound + """ + if to_type not in ENERGY_CONVERSION or from_type not in ENERGY_CONVERSION: + msg = ( + f"Incorrect 'from_type' or 'to_type' value: {from_type!r}, {to_type!r}\n" + f"Valid values are: {', '.join(ENERGY_CONVERSION)}" + ) + raise ValueError(msg) + return value * ENERGY_CONVERSION[from_type] / ENERGY_CONVERSION[to_type] + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 331585f3f866e210e23d11700b09a8770a1c2490 Mon Sep 17 00:00:00 2001 From: Himanshu Tomar Date: Fri, 23 Jun 2023 13:56:05 +0530 Subject: [PATCH 104/757] Algorithm: Calculating Product Sum from a Special Array with Nested Structures (#8761) * Added minimum waiting time problem solution using greedy algorithm * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * ruff --fix * Add type hints * Added two more doc test * Removed unnecessary comments * updated type hints * Updated the code as per the code review * Added recursive algo to calculate product sum from an array * Added recursive algo to calculate product sum from an array * Update doc string * Added doctest for product_sum function * Updated the code and added more doctests * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Added more test coverage for product_sum method * Update product_sum.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- DIRECTORY.md | 1 + data_structures/arrays/product_sum.py | 98 +++++++++++++++++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 data_structures/arrays/product_sum.py diff --git a/DIRECTORY.md b/DIRECTORY.md index 6ec8d5111176..83389dab1f56 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -166,6 +166,7 @@ * Arrays * [Permutations](data_structures/arrays/permutations.py) * [Prefix Sum](data_structures/arrays/prefix_sum.py) + * [Product Sum Array](data_structures/arrays/product_sum.py) * Binary Tree * [Avl Tree](data_structures/binary_tree/avl_tree.py) * [Basic Binary Tree](data_structures/binary_tree/basic_binary_tree.py) diff --git a/data_structures/arrays/product_sum.py b/data_structures/arrays/product_sum.py new file mode 100644 index 000000000000..4fb906f369ab --- /dev/null +++ b/data_structures/arrays/product_sum.py @@ -0,0 +1,98 @@ +""" +Calculate the Product Sum from a Special Array. +reference: https://dev.to/sfrasica/algorithms-product-sum-from-an-array-dc6 + +Python doctests can be run with the following command: +python -m doctest -v product_sum.py + +Calculate the product sum of a "special" array which can contain integers or nested +arrays. The product sum is obtained by adding all elements and multiplying by their +respective depths. + +For example, in the array [x, y], the product sum is (x + y). In the array [x, [y, z]], +the product sum is x + 2 * (y + z). In the array [x, [y, [z]]], +the product sum is x + 2 * (y + 3z). + +Example Input: +[5, 2, [-7, 1], 3, [6, [-13, 8], 4]] +Output: 12 + +""" + + +def product_sum(arr: list[int | list], depth: int) -> int: + """ + Recursively calculates the product sum of an array. + + The product sum of an array is defined as the sum of its elements multiplied by + their respective depths. If an element is a list, its product sum is calculated + recursively by multiplying the sum of its elements with its depth plus one. + + Args: + arr: The array of integers and nested lists. + depth: The current depth level. + + Returns: + int: The product sum of the array. + + Examples: + >>> product_sum([1, 2, 3], 1) + 6 + >>> product_sum([-1, 2, [-3, 4]], 2) + 8 + >>> product_sum([1, 2, 3], -1) + -6 + >>> product_sum([1, 2, 3], 0) + 0 + >>> product_sum([1, 2, 3], 7) + 42 + >>> product_sum((1, 2, 3), 7) + 42 + >>> product_sum({1, 2, 3}, 7) + 42 + >>> product_sum([1, -1], 1) + 0 + >>> product_sum([1, -2], 1) + -1 + >>> product_sum([-3.5, [1, [0.5]]], 1) + 1.5 + + """ + total_sum = 0 + for ele in arr: + total_sum += product_sum(ele, depth + 1) if isinstance(ele, list) else ele + return total_sum * depth + + +def product_sum_array(array: list[int | list]) -> int: + """ + Calculates the product sum of an array. + + Args: + array (List[Union[int, List]]): The array of integers and nested lists. + + Returns: + int: The product sum of the array. + + Examples: + >>> product_sum_array([1, 2, 3]) + 6 + >>> product_sum_array([1, [2, 3]]) + 11 + >>> product_sum_array([1, [2, [3, 4]]]) + 47 + >>> product_sum_array([0]) + 0 + >>> product_sum_array([-3.5, [1, [0.5]]]) + 1.5 + >>> product_sum_array([1, -2]) + -1 + + """ + return product_sum(array, 1) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 267a8b72f97762383e7c313ed20df859115e2815 Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Fri, 23 Jun 2023 06:56:58 -0700 Subject: [PATCH 105/757] Clarify how to add issue numbers in PR template and CONTRIBUTING.md (#8833) * updating DIRECTORY.md * Clarify wording in PR template * Clarify CONTRIBUTING.md wording about adding issue numbers * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Add suggested change from review to CONTRIBUTING.md Co-authored-by: Christian Clauss * Incorporate review edit to CONTRIBUTING.md Co-authored-by: Christian Clauss --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- .github/pull_request_template.md | 2 +- CONTRIBUTING.md | 7 ++++++- DIRECTORY.md | 2 ++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index b3ba8baf9c34..1f9797fae038 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -17,4 +17,4 @@ * [ ] All function parameters and return values are annotated with Python [type hints](https://docs.python.org/3/library/typing.html). * [ ] All functions have [doctests](https://docs.python.org/3/library/doctest.html) that pass the automated testing. * [ ] All new algorithms include at least one URL that points to Wikipedia or another similar explanation. -* [ ] If this pull request resolves one or more open issues then the commit message contains `Fixes: #{$ISSUE_NO}`. +* [ ] If this pull request resolves one or more open issues then the description above includes the issue number(s) with a [closing keyword](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue): "Fixes #ISSUE-NUMBER". diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2bb0c2e39eee..618cca868d83 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -25,7 +25,12 @@ We appreciate any contribution, from fixing a grammar mistake in a comment to im Your contribution will be tested by our [automated testing on GitHub Actions](https://github.com/TheAlgorithms/Python/actions) to save time and mental energy. After you have submitted your pull request, you should see the GitHub Actions tests start to run at the bottom of your submission page. If those tests fail, then click on the ___details___ button try to read through the GitHub Actions output to understand the failure. If you do not understand, please leave a comment on your submission page and a community member will try to help. -Please help us keep our issue list small by adding fixes: #{$ISSUE_NO} to the commit message of pull requests that resolve open issues. GitHub will use this tag to auto-close the issue when the PR is merged. +Please help us keep our issue list small by adding `Fixes #{$ISSUE_NUMBER}` to the description of pull requests that resolve open issues. +For example, if your pull request fixes issue #10, then please add the following to its description: +``` +Fixes #10 +``` +GitHub will use this tag to [auto-close the issue](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue) if and when the PR is merged. #### What is an Algorithm? diff --git a/DIRECTORY.md b/DIRECTORY.md index 83389dab1f56..1414aacf95f7 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -146,6 +146,7 @@ * [Decimal To Binary Recursion](conversions/decimal_to_binary_recursion.py) * [Decimal To Hexadecimal](conversions/decimal_to_hexadecimal.py) * [Decimal To Octal](conversions/decimal_to_octal.py) + * [Energy Conversions](conversions/energy_conversions.py) * [Excel Title To Column](conversions/excel_title_to_column.py) * [Hex To Bin](conversions/hex_to_bin.py) * [Hexadecimal To Decimal](conversions/hexadecimal_to_decimal.py) @@ -411,6 +412,7 @@ * [Dijkstra 2](graphs/dijkstra_2.py) * [Dijkstra Algorithm](graphs/dijkstra_algorithm.py) * [Dijkstra Alternate](graphs/dijkstra_alternate.py) + * [Dijkstra Binary Grid](graphs/dijkstra_binary_grid.py) * [Dinic](graphs/dinic.py) * [Directed And Undirected (Weighted) Graph](graphs/directed_and_undirected_(weighted)_graph.py) * [Edmonds Karp Multiple Source And Sink](graphs/edmonds_karp_multiple_source_and_sink.py) From 3bfa89dacf877b1d7a62b14f82d54e8de99a838e Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Sun, 25 Jun 2023 18:28:01 +0200 Subject: [PATCH 106/757] GitHub Actions build: Add more tests (#8837) * GitHub Actions build: Add more tests Re-enable some tests that were disabled in #6591. Fixes #8818 * updating DIRECTORY.md * TODO: Re-enable quantum tests * fails: pytest quantum/bb84.py quantum/q_fourier_transform.py --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- .github/workflows/build.yml | 7 +++---- DIRECTORY.md | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6b9cc890b6af..5229edaf8659 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -22,11 +22,10 @@ jobs: python -m pip install --upgrade pip setuptools six wheel python -m pip install pytest-cov -r requirements.txt - name: Run tests - # See: #6591 for re-enabling tests on Python v3.11 + # TODO: #8818 Re-enable quantum tests run: pytest - --ignore=computer_vision/cnn_classification.py - --ignore=machine_learning/lstm/lstm_prediction.py - --ignore=quantum/ + --ignore=quantum/bb84.py + --ignore=quantum/q_fourier_transform.py --ignore=project_euler/ --ignore=scripts/validate_solutions.py --cov-report=term-missing:skip-covered diff --git a/DIRECTORY.md b/DIRECTORY.md index 1414aacf95f7..0c21b9537fc1 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -167,7 +167,7 @@ * Arrays * [Permutations](data_structures/arrays/permutations.py) * [Prefix Sum](data_structures/arrays/prefix_sum.py) - * [Product Sum Array](data_structures/arrays/product_sum.py) + * [Product Sum](data_structures/arrays/product_sum.py) * Binary Tree * [Avl Tree](data_structures/binary_tree/avl_tree.py) * [Basic Binary Tree](data_structures/binary_tree/basic_binary_tree.py) From d764eec655c1c51f5ef3490d27ea72430191a000 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Mon, 26 Jun 2023 05:24:50 +0200 Subject: [PATCH 107/757] Fix failing pytest quantum/bb84.py (#8838) * Fix failing pytest quantum/bb84.py * Update bb84.py test results to match current qiskit --- .github/workflows/build.yml | 1 - quantum/bb84.py | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5229edaf8659..fc8cb636979e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -24,7 +24,6 @@ jobs: - name: Run tests # TODO: #8818 Re-enable quantum tests run: pytest - --ignore=quantum/bb84.py --ignore=quantum/q_fourier_transform.py --ignore=project_euler/ --ignore=scripts/validate_solutions.py diff --git a/quantum/bb84.py b/quantum/bb84.py index 60d64371fe63..e90a11c2aef3 100644 --- a/quantum/bb84.py +++ b/quantum/bb84.py @@ -64,10 +64,10 @@ def bb84(key_len: int = 8, seed: int | None = None) -> str: key: The key generated using BB84 protocol. >>> bb84(16, seed=0) - '1101101100010000' + '0111110111010010' >>> bb84(8, seed=0) - '01011011' + '10110001' """ # Set up the random number generator. rng = np.random.default_rng(seed=seed) From 62dcbea943e8cc4ea4d83eff115c4e6f6a4808af Mon Sep 17 00:00:00 2001 From: duongoku Date: Mon, 26 Jun 2023 14:39:18 +0700 Subject: [PATCH 108/757] Add power sum problem (#8832) * Add powersum problem * Add doctest * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Add more doctests * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Add more doctests * Improve paramater name * Fix line too long * Remove global variables * Apply suggestions from code review * Apply suggestions from code review --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- backtracking/power_sum.py | 93 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 backtracking/power_sum.py diff --git a/backtracking/power_sum.py b/backtracking/power_sum.py new file mode 100644 index 000000000000..fcf1429f8570 --- /dev/null +++ b/backtracking/power_sum.py @@ -0,0 +1,93 @@ +""" +Problem source: https://www.hackerrank.com/challenges/the-power-sum/problem +Find the number of ways that a given integer X, can be expressed as the sum +of the Nth powers of unique, natural numbers. For example, if X=13 and N=2. +We have to find all combinations of unique squares adding up to 13. +The only solution is 2^2+3^2. Constraints: 1<=X<=1000, 2<=N<=10. +""" + +from math import pow + + +def backtrack( + needed_sum: int, + power: int, + current_number: int, + current_sum: int, + solutions_count: int, +) -> tuple[int, int]: + """ + >>> backtrack(13, 2, 1, 0, 0) + (0, 1) + >>> backtrack(100, 2, 1, 0, 0) + (0, 3) + >>> backtrack(100, 3, 1, 0, 0) + (0, 1) + >>> backtrack(800, 2, 1, 0, 0) + (0, 561) + >>> backtrack(1000, 10, 1, 0, 0) + (0, 0) + >>> backtrack(400, 2, 1, 0, 0) + (0, 55) + >>> backtrack(50, 1, 1, 0, 0) + (0, 3658) + """ + if current_sum == needed_sum: + # If the sum of the powers is equal to needed_sum, then we have a solution. + solutions_count += 1 + return current_sum, solutions_count + + i_to_n = int(pow(current_number, power)) + if current_sum + i_to_n <= needed_sum: + # If the sum of the powers is less than needed_sum, then continue adding powers. + current_sum += i_to_n + current_sum, solutions_count = backtrack( + needed_sum, power, current_number + 1, current_sum, solutions_count + ) + current_sum -= i_to_n + if i_to_n < needed_sum: + # If the power of i is less than needed_sum, then try with the next power. + current_sum, solutions_count = backtrack( + needed_sum, power, current_number + 1, current_sum, solutions_count + ) + return current_sum, solutions_count + + +def solve(needed_sum: int, power: int) -> int: + """ + >>> solve(13, 2) + 1 + >>> solve(100, 2) + 3 + >>> solve(100, 3) + 1 + >>> solve(800, 2) + 561 + >>> solve(1000, 10) + 0 + >>> solve(400, 2) + 55 + >>> solve(50, 1) + Traceback (most recent call last): + ... + ValueError: Invalid input + needed_sum must be between 1 and 1000, power between 2 and 10. + >>> solve(-10, 5) + Traceback (most recent call last): + ... + ValueError: Invalid input + needed_sum must be between 1 and 1000, power between 2 and 10. + """ + if not (1 <= needed_sum <= 1000 and 2 <= power <= 10): + raise ValueError( + "Invalid input\n" + "needed_sum must be between 1 and 1000, power between 2 and 10." + ) + + return backtrack(needed_sum, power, 1, 0, 0)[1] # Return the solutions_count + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 69f20033e55ae62c337e2fb2146aea5fabf3e5a0 Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Mon, 26 Jun 2023 02:15:31 -0700 Subject: [PATCH 109/757] Remove duplicate implementation of Collatz sequence (#8836) * updating DIRECTORY.md * Remove duplicate implementation of Collatz sequence * updating DIRECTORY.md * Add suggestions from PR review --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 1 - maths/3n_plus_1.py | 151 -------------------------------------- maths/collatz_sequence.py | 69 +++++++++++------ 3 files changed, 46 insertions(+), 175 deletions(-) delete mode 100644 maths/3n_plus_1.py diff --git a/DIRECTORY.md b/DIRECTORY.md index 0c21b9537fc1..1e0e450bca2b 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -522,7 +522,6 @@ * [Xgboost Regressor](machine_learning/xgboost_regressor.py) ## Maths - * [3N Plus 1](maths/3n_plus_1.py) * [Abs](maths/abs.py) * [Add](maths/add.py) * [Addition Without Arithmetic](maths/addition_without_arithmetic.py) diff --git a/maths/3n_plus_1.py b/maths/3n_plus_1.py deleted file mode 100644 index f9f6dfeb9faa..000000000000 --- a/maths/3n_plus_1.py +++ /dev/null @@ -1,151 +0,0 @@ -from __future__ import annotations - - -def n31(a: int) -> tuple[list[int], int]: - """ - Returns the Collatz sequence and its length of any positive integer. - >>> n31(4) - ([4, 2, 1], 3) - """ - - if not isinstance(a, int): - msg = f"Must be int, not {type(a).__name__}" - raise TypeError(msg) - if a < 1: - msg = f"Given integer must be positive, not {a}" - raise ValueError(msg) - - path = [a] - while a != 1: - if a % 2 == 0: - a //= 2 - else: - a = 3 * a + 1 - path.append(a) - return path, len(path) - - -def test_n31(): - """ - >>> test_n31() - """ - assert n31(4) == ([4, 2, 1], 3) - assert n31(11) == ([11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1], 15) - assert n31(31) == ( - [ - 31, - 94, - 47, - 142, - 71, - 214, - 107, - 322, - 161, - 484, - 242, - 121, - 364, - 182, - 91, - 274, - 137, - 412, - 206, - 103, - 310, - 155, - 466, - 233, - 700, - 350, - 175, - 526, - 263, - 790, - 395, - 1186, - 593, - 1780, - 890, - 445, - 1336, - 668, - 334, - 167, - 502, - 251, - 754, - 377, - 1132, - 566, - 283, - 850, - 425, - 1276, - 638, - 319, - 958, - 479, - 1438, - 719, - 2158, - 1079, - 3238, - 1619, - 4858, - 2429, - 7288, - 3644, - 1822, - 911, - 2734, - 1367, - 4102, - 2051, - 6154, - 3077, - 9232, - 4616, - 2308, - 1154, - 577, - 1732, - 866, - 433, - 1300, - 650, - 325, - 976, - 488, - 244, - 122, - 61, - 184, - 92, - 46, - 23, - 70, - 35, - 106, - 53, - 160, - 80, - 40, - 20, - 10, - 5, - 16, - 8, - 4, - 2, - 1, - ], - 107, - ) - - -if __name__ == "__main__": - num = 4 - path, length = n31(num) - print(f"The Collatz sequence of {num} took {length} steps. \nPath: {path}") diff --git a/maths/collatz_sequence.py b/maths/collatz_sequence.py index 7b3636de69f4..4f3aa5582731 100644 --- a/maths/collatz_sequence.py +++ b/maths/collatz_sequence.py @@ -1,43 +1,66 @@ +""" +The Collatz conjecture is a famous unsolved problem in mathematics. Given a starting +positive integer, define the following sequence: +- If the current term n is even, then the next term is n/2. +- If the current term n is odd, then the next term is 3n + 1. +The conjecture claims that this sequence will always reach 1 for any starting number. + +Other names for this problem include the 3n + 1 problem, the Ulam conjecture, Kakutani's +problem, the Thwaites conjecture, Hasse's algorithm, the Syracuse problem, and the +hailstone sequence. + +Reference: https://en.wikipedia.org/wiki/Collatz_conjecture +""" + from __future__ import annotations +from collections.abc import Generator -def collatz_sequence(n: int) -> list[int]: + +def collatz_sequence(n: int) -> Generator[int, None, None]: """ - Collatz conjecture: start with any positive integer n. The next term is - obtained as follows: - If n term is even, the next term is: n / 2 . - If n is odd, the next term is: 3 * n + 1. - - The conjecture states the sequence will always reach 1 for any starting value n. - Example: - >>> collatz_sequence(2.1) + Generate the Collatz sequence starting at n. + >>> tuple(collatz_sequence(2.1)) Traceback (most recent call last): ... - Exception: Sequence only defined for natural numbers - >>> collatz_sequence(0) + Exception: Sequence only defined for positive integers + >>> tuple(collatz_sequence(0)) Traceback (most recent call last): ... - Exception: Sequence only defined for natural numbers - >>> collatz_sequence(43) # doctest: +NORMALIZE_WHITESPACE - [43, 130, 65, 196, 98, 49, 148, 74, 37, 112, 56, 28, 14, 7, - 22, 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1] + Exception: Sequence only defined for positive integers + >>> tuple(collatz_sequence(4)) + (4, 2, 1) + >>> tuple(collatz_sequence(11)) + (11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1) + >>> tuple(collatz_sequence(31)) # doctest: +NORMALIZE_WHITESPACE + (31, 94, 47, 142, 71, 214, 107, 322, 161, 484, 242, 121, 364, 182, 91, 274, 137, + 412, 206, 103, 310, 155, 466, 233, 700, 350, 175, 526, 263, 790, 395, 1186, 593, + 1780, 890, 445, 1336, 668, 334, 167, 502, 251, 754, 377, 1132, 566, 283, 850, 425, + 1276, 638, 319, 958, 479, 1438, 719, 2158, 1079, 3238, 1619, 4858, 2429, 7288, 3644, + 1822, 911, 2734, 1367, 4102, 2051, 6154, 3077, 9232, 4616, 2308, 1154, 577, 1732, + 866, 433, 1300, 650, 325, 976, 488, 244, 122, 61, 184, 92, 46, 23, 70, 35, 106, 53, + 160, 80, 40, 20, 10, 5, 16, 8, 4, 2, 1) + >>> tuple(collatz_sequence(43)) # doctest: +NORMALIZE_WHITESPACE + (43, 130, 65, 196, 98, 49, 148, 74, 37, 112, 56, 28, 14, 7, 22, 11, 34, 17, 52, 26, + 13, 40, 20, 10, 5, 16, 8, 4, 2, 1) """ - if not isinstance(n, int) or n < 1: - raise Exception("Sequence only defined for natural numbers") + raise Exception("Sequence only defined for positive integers") - sequence = [n] + yield n while n != 1: - n = 3 * n + 1 if n & 1 else n // 2 - sequence.append(n) - return sequence + if n % 2 == 0: + n //= 2 + else: + n = 3 * n + 1 + yield n def main(): n = 43 - sequence = collatz_sequence(n) + sequence = tuple(collatz_sequence(n)) print(sequence) - print(f"collatz sequence from {n} took {len(sequence)} steps.") + print(f"Collatz sequence from {n} took {len(sequence)} steps.") if __name__ == "__main__": From 929d3d9219020d2978d5560e3b931df69a6f2d50 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 27 Jun 2023 07:23:54 +0200 Subject: [PATCH 110/757] [pre-commit.ci] pre-commit autoupdate (#8842) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/astral-sh/ruff-pre-commit: v0.0.274 → v0.0.275](https://github.com/astral-sh/ruff-pre-commit/compare/v0.0.274...v0.0.275) - [github.com/tox-dev/pyproject-fmt: 0.12.0 → 0.12.1](https://github.com/tox-dev/pyproject-fmt/compare/0.12.0...0.12.1) - [github.com/pre-commit/mirrors-mypy: v1.3.0 → v1.4.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.3.0...v1.4.1) * updating DIRECTORY.md --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- .pre-commit-config.yaml | 6 +++--- DIRECTORY.md | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3d4cc4084ccf..1d92d2ff31c1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.0.274 + rev: v0.0.275 hooks: - id: ruff @@ -33,7 +33,7 @@ repos: - tomli - repo: https://github.com/tox-dev/pyproject-fmt - rev: "0.12.0" + rev: "0.12.1" hooks: - id: pyproject-fmt @@ -51,7 +51,7 @@ repos: - id: validate-pyproject - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.3.0 + rev: v1.4.1 hooks: - id: mypy args: diff --git a/DIRECTORY.md b/DIRECTORY.md index 1e0e450bca2b..d25d665ef28b 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -29,6 +29,7 @@ * [Minmax](backtracking/minmax.py) * [N Queens](backtracking/n_queens.py) * [N Queens Math](backtracking/n_queens_math.py) + * [Power Sum](backtracking/power_sum.py) * [Rat In Maze](backtracking/rat_in_maze.py) * [Sudoku](backtracking/sudoku.py) * [Sum Of Subsets](backtracking/sum_of_subsets.py) From c9ee6ed1887fadd25c1c43c31ed55a99b2be5f24 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 4 Jul 2023 00:20:35 +0200 Subject: [PATCH 111/757] [pre-commit.ci] pre-commit autoupdate (#8853) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/astral-sh/ruff-pre-commit: v0.0.275 → v0.0.276](https://github.com/astral-sh/ruff-pre-commit/compare/v0.0.275...v0.0.276) * Update double_ended_queue.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update double_ended_queue.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- .pre-commit-config.yaml | 2 +- data_structures/queue/double_ended_queue.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1d92d2ff31c1..42ebeed14fa9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.0.275 + rev: v0.0.276 hooks: - id: ruff diff --git a/data_structures/queue/double_ended_queue.py b/data_structures/queue/double_ended_queue.py index 2472371b42fe..44dc863b9a4e 100644 --- a/data_structures/queue/double_ended_queue.py +++ b/data_structures/queue/double_ended_queue.py @@ -54,7 +54,7 @@ class _Iterator: the current node of the iteration. """ - __slots__ = "_cur" + __slots__ = ("_cur",) def __init__(self, cur: Deque._Node | None) -> None: self._cur = cur From a0eec90466beeb3b6ce0f7afd905f96454e9b14c Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Tue, 11 Jul 2023 02:44:12 -0700 Subject: [PATCH 112/757] Consolidate duplicate implementations of max subarray (#8849) * Remove max subarray sum duplicate implementations * updating DIRECTORY.md * Rename max_sum_contiguous_subsequence.py * Fix typo in dynamic_programming/max_subarray_sum.py * Remove duplicate divide and conquer max subarray * updating DIRECTORY.md --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 8 +- divide_and_conquer/max_subarray.py | 112 ++++++++++++++++++ divide_and_conquer/max_subarray_sum.py | 78 ------------ dynamic_programming/max_sub_array.py | 93 --------------- dynamic_programming/max_subarray_sum.py | 60 ++++++++++ .../max_sum_contiguous_subsequence.py | 20 ---- maths/kadanes.py | 63 ---------- maths/largest_subarray_sum.py | 21 ---- other/maximum_subarray.py | 32 ----- 9 files changed, 174 insertions(+), 313 deletions(-) create mode 100644 divide_and_conquer/max_subarray.py delete mode 100644 divide_and_conquer/max_subarray_sum.py delete mode 100644 dynamic_programming/max_sub_array.py create mode 100644 dynamic_programming/max_subarray_sum.py delete mode 100644 dynamic_programming/max_sum_contiguous_subsequence.py delete mode 100644 maths/kadanes.py delete mode 100644 maths/largest_subarray_sum.py delete mode 100644 other/maximum_subarray.py diff --git a/DIRECTORY.md b/DIRECTORY.md index d25d665ef28b..77938f45011b 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -293,7 +293,7 @@ * [Inversions](divide_and_conquer/inversions.py) * [Kth Order Statistic](divide_and_conquer/kth_order_statistic.py) * [Max Difference Pair](divide_and_conquer/max_difference_pair.py) - * [Max Subarray Sum](divide_and_conquer/max_subarray_sum.py) + * [Max Subarray](divide_and_conquer/max_subarray.py) * [Mergesort](divide_and_conquer/mergesort.py) * [Peak](divide_and_conquer/peak.py) * [Power](divide_and_conquer/power.py) @@ -324,8 +324,7 @@ * [Matrix Chain Order](dynamic_programming/matrix_chain_order.py) * [Max Non Adjacent Sum](dynamic_programming/max_non_adjacent_sum.py) * [Max Product Subarray](dynamic_programming/max_product_subarray.py) - * [Max Sub Array](dynamic_programming/max_sub_array.py) - * [Max Sum Contiguous Subsequence](dynamic_programming/max_sum_contiguous_subsequence.py) + * [Max Subarray Sum](dynamic_programming/max_subarray_sum.py) * [Min Distance Up Bottom](dynamic_programming/min_distance_up_bottom.py) * [Minimum Coin Change](dynamic_programming/minimum_coin_change.py) * [Minimum Cost Path](dynamic_programming/minimum_cost_path.py) @@ -591,12 +590,10 @@ * [Is Square Free](maths/is_square_free.py) * [Jaccard Similarity](maths/jaccard_similarity.py) * [Juggler Sequence](maths/juggler_sequence.py) - * [Kadanes](maths/kadanes.py) * [Karatsuba](maths/karatsuba.py) * [Krishnamurthy Number](maths/krishnamurthy_number.py) * [Kth Lexicographic Permutation](maths/kth_lexicographic_permutation.py) * [Largest Of Very Large Numbers](maths/largest_of_very_large_numbers.py) - * [Largest Subarray Sum](maths/largest_subarray_sum.py) * [Least Common Multiple](maths/least_common_multiple.py) * [Line Length](maths/line_length.py) * [Liouville Lambda](maths/liouville_lambda.py) @@ -733,7 +730,6 @@ * [Linear Congruential Generator](other/linear_congruential_generator.py) * [Lru Cache](other/lru_cache.py) * [Magicdiamondpattern](other/magicdiamondpattern.py) - * [Maximum Subarray](other/maximum_subarray.py) * [Maximum Subsequence](other/maximum_subsequence.py) * [Nested Brackets](other/nested_brackets.py) * [Number Container System](other/number_container_system.py) diff --git a/divide_and_conquer/max_subarray.py b/divide_and_conquer/max_subarray.py new file mode 100644 index 000000000000..851ef621a24c --- /dev/null +++ b/divide_and_conquer/max_subarray.py @@ -0,0 +1,112 @@ +""" +The maximum subarray problem is the task of finding the continuous subarray that has the +maximum sum within a given array of numbers. For example, given the array +[-2, 1, -3, 4, -1, 2, 1, -5, 4], the contiguous subarray with the maximum sum is +[4, -1, 2, 1], which has a sum of 6. + +This divide-and-conquer algorithm finds the maximum subarray in O(n log n) time. +""" +from __future__ import annotations + +import time +from collections.abc import Sequence +from random import randint + +from matplotlib import pyplot as plt + + +def max_subarray( + arr: Sequence[float], low: int, high: int +) -> tuple[int | None, int | None, float]: + """ + Solves the maximum subarray problem using divide and conquer. + :param arr: the given array of numbers + :param low: the start index + :param high: the end index + :return: the start index of the maximum subarray, the end index of the + maximum subarray, and the maximum subarray sum + + >>> nums = [-2, 1, -3, 4, -1, 2, 1, -5, 4] + >>> max_subarray(nums, 0, len(nums) - 1) + (3, 6, 6) + >>> nums = [2, 8, 9] + >>> max_subarray(nums, 0, len(nums) - 1) + (0, 2, 19) + >>> nums = [0, 0] + >>> max_subarray(nums, 0, len(nums) - 1) + (0, 0, 0) + >>> nums = [-1.0, 0.0, 1.0] + >>> max_subarray(nums, 0, len(nums) - 1) + (2, 2, 1.0) + >>> nums = [-2, -3, -1, -4, -6] + >>> max_subarray(nums, 0, len(nums) - 1) + (2, 2, -1) + >>> max_subarray([], 0, 0) + (None, None, 0) + """ + if not arr: + return None, None, 0 + if low == high: + return low, high, arr[low] + + mid = (low + high) // 2 + left_low, left_high, left_sum = max_subarray(arr, low, mid) + right_low, right_high, right_sum = max_subarray(arr, mid + 1, high) + cross_left, cross_right, cross_sum = max_cross_sum(arr, low, mid, high) + if left_sum >= right_sum and left_sum >= cross_sum: + return left_low, left_high, left_sum + elif right_sum >= left_sum and right_sum >= cross_sum: + return right_low, right_high, right_sum + return cross_left, cross_right, cross_sum + + +def max_cross_sum( + arr: Sequence[float], low: int, mid: int, high: int +) -> tuple[int, int, float]: + left_sum, max_left = float("-inf"), -1 + right_sum, max_right = float("-inf"), -1 + + summ: int | float = 0 + for i in range(mid, low - 1, -1): + summ += arr[i] + if summ > left_sum: + left_sum = summ + max_left = i + + summ = 0 + for i in range(mid + 1, high + 1): + summ += arr[i] + if summ > right_sum: + right_sum = summ + max_right = i + + return max_left, max_right, (left_sum + right_sum) + + +def time_max_subarray(input_size: int) -> float: + arr = [randint(1, input_size) for _ in range(input_size)] + start = time.time() + max_subarray(arr, 0, input_size - 1) + end = time.time() + return end - start + + +def plot_runtimes() -> None: + input_sizes = [10, 100, 1000, 10000, 50000, 100000, 200000, 300000, 400000, 500000] + runtimes = [time_max_subarray(input_size) for input_size in input_sizes] + print("No of Inputs\t\tTime Taken") + for input_size, runtime in zip(input_sizes, runtimes): + print(input_size, "\t\t", runtime) + plt.plot(input_sizes, runtimes) + plt.xlabel("Number of Inputs") + plt.ylabel("Time taken in seconds") + plt.show() + + +if __name__ == "__main__": + """ + A random simulation of this algorithm. + """ + from doctest import testmod + + testmod() diff --git a/divide_and_conquer/max_subarray_sum.py b/divide_and_conquer/max_subarray_sum.py deleted file mode 100644 index f23e81719025..000000000000 --- a/divide_and_conquer/max_subarray_sum.py +++ /dev/null @@ -1,78 +0,0 @@ -""" -Given a array of length n, max_subarray_sum() finds -the maximum of sum of contiguous sub-array using divide and conquer method. - -Time complexity : O(n log n) - -Ref : INTRODUCTION TO ALGORITHMS THIRD EDITION -(section : 4, sub-section : 4.1, page : 70) - -""" - - -def max_sum_from_start(array): - """This function finds the maximum contiguous sum of array from 0 index - - Parameters : - array (list[int]) : given array - - Returns : - max_sum (int) : maximum contiguous sum of array from 0 index - - """ - array_sum = 0 - max_sum = float("-inf") - for num in array: - array_sum += num - if array_sum > max_sum: - max_sum = array_sum - return max_sum - - -def max_cross_array_sum(array, left, mid, right): - """This function finds the maximum contiguous sum of left and right arrays - - Parameters : - array, left, mid, right (list[int], int, int, int) - - Returns : - (int) : maximum of sum of contiguous sum of left and right arrays - - """ - - max_sum_of_left = max_sum_from_start(array[left : mid + 1][::-1]) - max_sum_of_right = max_sum_from_start(array[mid + 1 : right + 1]) - return max_sum_of_left + max_sum_of_right - - -def max_subarray_sum(array, left, right): - """Maximum contiguous sub-array sum, using divide and conquer method - - Parameters : - array, left, right (list[int], int, int) : - given array, current left index and current right index - - Returns : - int : maximum of sum of contiguous sub-array - - """ - - # base case: array has only one element - if left == right: - return array[right] - - # Recursion - mid = (left + right) // 2 - left_half_sum = max_subarray_sum(array, left, mid) - right_half_sum = max_subarray_sum(array, mid + 1, right) - cross_sum = max_cross_array_sum(array, left, mid, right) - return max(left_half_sum, right_half_sum, cross_sum) - - -if __name__ == "__main__": - array = [-2, -5, 6, -2, -3, 1, 5, -6] - array_length = len(array) - print( - "Maximum sum of contiguous subarray:", - max_subarray_sum(array, 0, array_length - 1), - ) diff --git a/dynamic_programming/max_sub_array.py b/dynamic_programming/max_sub_array.py deleted file mode 100644 index 07717fba4172..000000000000 --- a/dynamic_programming/max_sub_array.py +++ /dev/null @@ -1,93 +0,0 @@ -""" -author : Mayank Kumar Jha (mk9440) -""" -from __future__ import annotations - - -def find_max_sub_array(a, low, high): - if low == high: - return low, high, a[low] - else: - mid = (low + high) // 2 - left_low, left_high, left_sum = find_max_sub_array(a, low, mid) - right_low, right_high, right_sum = find_max_sub_array(a, mid + 1, high) - cross_left, cross_right, cross_sum = find_max_cross_sum(a, low, mid, high) - if left_sum >= right_sum and left_sum >= cross_sum: - return left_low, left_high, left_sum - elif right_sum >= left_sum and right_sum >= cross_sum: - return right_low, right_high, right_sum - else: - return cross_left, cross_right, cross_sum - - -def find_max_cross_sum(a, low, mid, high): - left_sum, max_left = -999999999, -1 - right_sum, max_right = -999999999, -1 - summ = 0 - for i in range(mid, low - 1, -1): - summ += a[i] - if summ > left_sum: - left_sum = summ - max_left = i - summ = 0 - for i in range(mid + 1, high + 1): - summ += a[i] - if summ > right_sum: - right_sum = summ - max_right = i - return max_left, max_right, (left_sum + right_sum) - - -def max_sub_array(nums: list[int]) -> int: - """ - Finds the contiguous subarray which has the largest sum and return its sum. - - >>> max_sub_array([-2, 1, -3, 4, -1, 2, 1, -5, 4]) - 6 - - An empty (sub)array has sum 0. - >>> max_sub_array([]) - 0 - - If all elements are negative, the largest subarray would be the empty array, - having the sum 0. - >>> max_sub_array([-1, -2, -3]) - 0 - >>> max_sub_array([5, -2, -3]) - 5 - >>> max_sub_array([31, -41, 59, 26, -53, 58, 97, -93, -23, 84]) - 187 - """ - best = 0 - current = 0 - for i in nums: - current += i - current = max(current, 0) - best = max(best, current) - return best - - -if __name__ == "__main__": - """ - A random simulation of this algorithm. - """ - import time - from random import randint - - from matplotlib import pyplot as plt - - inputs = [10, 100, 1000, 10000, 50000, 100000, 200000, 300000, 400000, 500000] - tim = [] - for i in inputs: - li = [randint(1, i) for j in range(i)] - strt = time.time() - (find_max_sub_array(li, 0, len(li) - 1)) - end = time.time() - tim.append(end - strt) - print("No of Inputs Time Taken") - for i in range(len(inputs)): - print(inputs[i], "\t\t", tim[i]) - plt.plot(inputs, tim) - plt.xlabel("Number of Inputs") - plt.ylabel("Time taken in seconds ") - plt.show() diff --git a/dynamic_programming/max_subarray_sum.py b/dynamic_programming/max_subarray_sum.py new file mode 100644 index 000000000000..c76943472b97 --- /dev/null +++ b/dynamic_programming/max_subarray_sum.py @@ -0,0 +1,60 @@ +""" +The maximum subarray sum problem is the task of finding the maximum sum that can be +obtained from a contiguous subarray within a given array of numbers. For example, given +the array [-2, 1, -3, 4, -1, 2, 1, -5, 4], the contiguous subarray with the maximum sum +is [4, -1, 2, 1], so the maximum subarray sum is 6. + +Kadane's algorithm is a simple dynamic programming algorithm that solves the maximum +subarray sum problem in O(n) time and O(1) space. + +Reference: https://en.wikipedia.org/wiki/Maximum_subarray_problem +""" +from collections.abc import Sequence + + +def max_subarray_sum( + arr: Sequence[float], allow_empty_subarrays: bool = False +) -> float: + """ + Solves the maximum subarray sum problem using Kadane's algorithm. + :param arr: the given array of numbers + :param allow_empty_subarrays: if True, then the algorithm considers empty subarrays + + >>> max_subarray_sum([2, 8, 9]) + 19 + >>> max_subarray_sum([0, 0]) + 0 + >>> max_subarray_sum([-1.0, 0.0, 1.0]) + 1.0 + >>> max_subarray_sum([1, 2, 3, 4, -2]) + 10 + >>> max_subarray_sum([-2, 1, -3, 4, -1, 2, 1, -5, 4]) + 6 + >>> max_subarray_sum([2, 3, -9, 8, -2]) + 8 + >>> max_subarray_sum([-2, -3, -1, -4, -6]) + -1 + >>> max_subarray_sum([-2, -3, -1, -4, -6], allow_empty_subarrays=True) + 0 + >>> max_subarray_sum([]) + 0 + """ + if not arr: + return 0 + + max_sum = 0 if allow_empty_subarrays else float("-inf") + curr_sum = 0.0 + for num in arr: + curr_sum = max(0 if allow_empty_subarrays else num, curr_sum + num) + max_sum = max(max_sum, curr_sum) + + return max_sum + + +if __name__ == "__main__": + from doctest import testmod + + testmod() + + nums = [-2, 1, -3, 4, -1, 2, 1, -5, 4] + print(f"{max_subarray_sum(nums) = }") diff --git a/dynamic_programming/max_sum_contiguous_subsequence.py b/dynamic_programming/max_sum_contiguous_subsequence.py deleted file mode 100644 index bac592370c5d..000000000000 --- a/dynamic_programming/max_sum_contiguous_subsequence.py +++ /dev/null @@ -1,20 +0,0 @@ -def max_subarray_sum(nums: list) -> int: - """ - >>> max_subarray_sum([6 , 9, -1, 3, -7, -5, 10]) - 17 - """ - if not nums: - return 0 - n = len(nums) - - res, s, s_pre = nums[0], nums[0], nums[0] - for i in range(1, n): - s = max(nums[i], s_pre + nums[i]) - s_pre = s - res = max(res, s) - return res - - -if __name__ == "__main__": - nums = [6, 9, -1, 3, -7, -5, 10] - print(max_subarray_sum(nums)) diff --git a/maths/kadanes.py b/maths/kadanes.py deleted file mode 100644 index c2ea53a6cc84..000000000000 --- a/maths/kadanes.py +++ /dev/null @@ -1,63 +0,0 @@ -""" -Kadane's algorithm to get maximum subarray sum -https://medium.com/@rsinghal757/kadanes-algorithm-dynamic-programming-how-and-why-does-it-work-3fd8849ed73d -https://en.wikipedia.org/wiki/Maximum_subarray_problem -""" -test_data: tuple = ([-2, -8, -9], [2, 8, 9], [-1, 0, 1], [0, 0], []) - - -def negative_exist(arr: list) -> int: - """ - >>> negative_exist([-2,-8,-9]) - -2 - >>> [negative_exist(arr) for arr in test_data] - [-2, 0, 0, 0, 0] - """ - arr = arr or [0] - max_number = arr[0] - for i in arr: - if i >= 0: - return 0 - elif max_number <= i: - max_number = i - return max_number - - -def kadanes(arr: list) -> int: - """ - If negative_exist() returns 0 than this function will execute - else it will return the value return by negative_exist function - - For example: arr = [2, 3, -9, 8, -2] - Initially we set value of max_sum to 0 and max_till_element to 0 than when - max_sum is less than max_till particular element it will assign that value to - max_sum and when value of max_till_sum is less than 0 it will assign 0 to i - and after that whole process, return the max_sum - So the output for above arr is 8 - - >>> kadanes([2, 3, -9, 8, -2]) - 8 - >>> [kadanes(arr) for arr in test_data] - [-2, 19, 1, 0, 0] - """ - max_sum = negative_exist(arr) - if max_sum < 0: - return max_sum - - max_sum = 0 - max_till_element = 0 - - for i in arr: - max_till_element += i - max_sum = max(max_sum, max_till_element) - max_till_element = max(max_till_element, 0) - return max_sum - - -if __name__ == "__main__": - try: - print("Enter integer values sepatated by spaces") - arr = [int(x) for x in input().split()] - print(f"Maximum subarray sum of {arr} is {kadanes(arr)}") - except ValueError: - print("Please enter integer values.") diff --git a/maths/largest_subarray_sum.py b/maths/largest_subarray_sum.py deleted file mode 100644 index 90f92c7127bf..000000000000 --- a/maths/largest_subarray_sum.py +++ /dev/null @@ -1,21 +0,0 @@ -from sys import maxsize - - -def max_sub_array_sum(a: list, size: int = 0): - """ - >>> max_sub_array_sum([-13, -3, -25, -20, -3, -16, -23, -12, -5, -22, -15, -4, -7]) - -3 - """ - size = size or len(a) - max_so_far = -maxsize - 1 - max_ending_here = 0 - for i in range(0, size): - max_ending_here = max_ending_here + a[i] - max_so_far = max(max_so_far, max_ending_here) - max_ending_here = max(max_ending_here, 0) - return max_so_far - - -if __name__ == "__main__": - a = [-13, -3, -25, -20, 1, -16, -23, -12, -5, -22, -15, -4, -7] - print(("Maximum contiguous sum is", max_sub_array_sum(a, len(a)))) diff --git a/other/maximum_subarray.py b/other/maximum_subarray.py deleted file mode 100644 index 1c8c8cabcd2d..000000000000 --- a/other/maximum_subarray.py +++ /dev/null @@ -1,32 +0,0 @@ -from collections.abc import Sequence - - -def max_subarray_sum(nums: Sequence[int]) -> int: - """Return the maximum possible sum amongst all non - empty subarrays. - - Raises: - ValueError: when nums is empty. - - >>> max_subarray_sum([1,2,3,4,-2]) - 10 - >>> max_subarray_sum([-2,1,-3,4,-1,2,1,-5,4]) - 6 - """ - if not nums: - raise ValueError("Input sequence should not be empty") - - curr_max = ans = nums[0] - nums_len = len(nums) - - for i in range(1, nums_len): - num = nums[i] - curr_max = max(curr_max + num, num) - ans = max(curr_max, ans) - - return ans - - -if __name__ == "__main__": - n = int(input("Enter number of elements : ").strip()) - array = list(map(int, input("\nEnter the numbers : ").strip().split()))[:n] - print(max_subarray_sum(array)) From 44b1bcc7c7e0f15385530bf54c59ad4eb86fef0b Mon Sep 17 00:00:00 2001 From: Caeden Perelli-Harris Date: Tue, 11 Jul 2023 10:51:21 +0100 Subject: [PATCH 113/757] Fix failing tests from ruff/newton_raphson (ignore S307 "possibly insecure function") (#8862) * chore: Fix failing tests (ignore S307 "possibly insecure function") * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix: Move noqa back to right line --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- arithmetic_analysis/newton_raphson.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/arithmetic_analysis/newton_raphson.py b/arithmetic_analysis/newton_raphson.py index aee2f07e5743..1b90ad4177f6 100644 --- a/arithmetic_analysis/newton_raphson.py +++ b/arithmetic_analysis/newton_raphson.py @@ -25,9 +25,11 @@ def newton_raphson( """ x = a while True: - x = Decimal(x) - (Decimal(eval(func)) / Decimal(eval(str(diff(func))))) + x = Decimal(x) - ( + Decimal(eval(func)) / Decimal(eval(str(diff(func)))) # noqa: S307 + ) # This number dictates the accuracy of the answer - if abs(eval(func)) < precision: + if abs(eval(func)) < precision: # noqa: S307 return float(x) From f614ed72170011d2d439f7901e1c8daa7deac8c4 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 11 Jul 2023 11:55:32 +0200 Subject: [PATCH 114/757] [pre-commit.ci] pre-commit autoupdate (#8860) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.0.276 → v0.0.277](https://github.com/astral-sh/ruff-pre-commit/compare/v0.0.276...v0.0.277) - [github.com/tox-dev/pyproject-fmt: 0.12.1 → 0.13.0](https://github.com/tox-dev/pyproject-fmt/compare/0.12.1...0.13.0) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 42ebeed14fa9..bf30703bdffc 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.0.276 + rev: v0.0.277 hooks: - id: ruff @@ -33,7 +33,7 @@ repos: - tomli - repo: https://github.com/tox-dev/pyproject-fmt - rev: "0.12.1" + rev: "0.13.0" hooks: - id: pyproject-fmt From 5aefc00f0f1c692ce772ddbc616d7cd91233236b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 18 Jul 2023 09:58:22 +0530 Subject: [PATCH 115/757] [pre-commit.ci] pre-commit autoupdate (#8872) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.0.277 → v0.0.278](https://github.com/astral-sh/ruff-pre-commit/compare/v0.0.277...v0.0.278) - [github.com/psf/black: 23.3.0 → 23.7.0](https://github.com/psf/black/compare/23.3.0...23.7.0) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index bf30703bdffc..13b955dd374f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,12 +16,12 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.0.277 + rev: v0.0.278 hooks: - id: ruff - repo: https://github.com/psf/black - rev: 23.3.0 + rev: 23.7.0 hooks: - id: black From 93fb169627ea9fe43436a312fdfa751818808180 Mon Sep 17 00:00:00 2001 From: Caeden Perelli-Harris Date: Sat, 22 Jul 2023 13:05:10 +0300 Subject: [PATCH 116/757] [Upgrade Ruff] Fix all errors raised from ruff (#8879) * chore: Fix tests * chore: Fix failing ruff * chore: Fix ruff errors * chore: Fix ruff errors * chore: Fix ruff errors * chore: Fix ruff errors * chore: Fix ruff errors * chore: Fix ruff errors * chore: Fix ruff errors * chore: Fix ruff errors * chore: Fix ruff errors * chore: Fix ruff errors * chore: Fix ruff errors * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * chore: Fix ruff errors * chore: Fix ruff errors * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update cellular_automata/game_of_life.py Co-authored-by: Christian Clauss * chore: Update ruff version in pre-commit * chore: Fix ruff errors * Update edmonds_karp_multiple_source_and_sink.py * Update factorial.py * Update primelib.py * Update min_cost_string_conversion.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- .pre-commit-config.yaml | 2 +- cellular_automata/game_of_life.py | 2 +- data_structures/binary_tree/red_black_tree.py | 2 +- data_structures/trie/radix_tree.py | 4 ++-- divide_and_conquer/convex_hull.py | 2 +- ...directed_and_undirected_(weighted)_graph.py | 18 +++++++++--------- .../edmonds_karp_multiple_source_and_sink.py | 2 +- maths/factorial.py | 2 +- maths/primelib.py | 2 +- other/davisb_putnamb_logemannb_loveland.py | 2 +- project_euler/problem_009/sol3.py | 16 ++++++++++------ quantum/ripple_adder_classic.py | 2 +- strings/min_cost_string_conversion.py | 2 +- web_programming/convert_number_to_words.py | 4 +--- 14 files changed, 32 insertions(+), 30 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 13b955dd374f..5adf12cc70c5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.0.278 + rev: v0.0.280 hooks: - id: ruff diff --git a/cellular_automata/game_of_life.py b/cellular_automata/game_of_life.py index 3382af7b5db6..b69afdce03eb 100644 --- a/cellular_automata/game_of_life.py +++ b/cellular_automata/game_of_life.py @@ -98,7 +98,7 @@ def __judge_point(pt: bool, neighbours: list[list[bool]]) -> bool: if pt: if alive < 2: state = False - elif alive == 2 or alive == 3: + elif alive in {2, 3}: state = True elif alive > 3: state = False diff --git a/data_structures/binary_tree/red_black_tree.py b/data_structures/binary_tree/red_black_tree.py index 3ebc8d63939b..4ebe0e927ca0 100644 --- a/data_structures/binary_tree/red_black_tree.py +++ b/data_structures/binary_tree/red_black_tree.py @@ -152,7 +152,7 @@ def _insert_repair(self) -> None: self.grandparent.color = 1 self.grandparent._insert_repair() - def remove(self, label: int) -> RedBlackTree: + def remove(self, label: int) -> RedBlackTree: # noqa: PLR0912 """Remove label from this tree.""" if self.label == label: if self.left and self.right: diff --git a/data_structures/trie/radix_tree.py b/data_structures/trie/radix_tree.py index 66890346ec2b..cf2f25c29f13 100644 --- a/data_structures/trie/radix_tree.py +++ b/data_structures/trie/radix_tree.py @@ -156,7 +156,7 @@ def delete(self, word: str) -> bool: del self.nodes[word[0]] # We merge the current node with its only child if len(self.nodes) == 1 and not self.is_leaf: - merging_node = list(self.nodes.values())[0] + merging_node = next(iter(self.nodes.values())) self.is_leaf = merging_node.is_leaf self.prefix += merging_node.prefix self.nodes = merging_node.nodes @@ -165,7 +165,7 @@ def delete(self, word: str) -> bool: incoming_node.is_leaf = False # If there is 1 edge, we merge it with its child else: - merging_node = list(incoming_node.nodes.values())[0] + merging_node = next(iter(incoming_node.nodes.values())) incoming_node.is_leaf = merging_node.is_leaf incoming_node.prefix += merging_node.prefix incoming_node.nodes = merging_node.nodes diff --git a/divide_and_conquer/convex_hull.py b/divide_and_conquer/convex_hull.py index 1ad933417da6..1d1bf301def5 100644 --- a/divide_and_conquer/convex_hull.py +++ b/divide_and_conquer/convex_hull.py @@ -266,7 +266,7 @@ def convex_hull_bf(points: list[Point]) -> list[Point]: points_left_of_ij = points_right_of_ij = False ij_part_of_convex_hull = True for k in range(n): - if k != i and k != j: + if k not in {i, j}: det_k = _det(points[i], points[j], points[k]) if det_k > 0: diff --git a/graphs/directed_and_undirected_(weighted)_graph.py b/graphs/directed_and_undirected_(weighted)_graph.py index b29485031083..8ca645fdace8 100644 --- a/graphs/directed_and_undirected_(weighted)_graph.py +++ b/graphs/directed_and_undirected_(weighted)_graph.py @@ -39,7 +39,7 @@ def dfs(self, s=-2, d=-1): stack = [] visited = [] if s == -2: - s = list(self.graph)[0] + s = next(iter(self.graph)) stack.append(s) visited.append(s) ss = s @@ -87,7 +87,7 @@ def bfs(self, s=-2): d = deque() visited = [] if s == -2: - s = list(self.graph)[0] + s = next(iter(self.graph)) d.append(s) visited.append(s) while d: @@ -114,7 +114,7 @@ def topological_sort(self, s=-2): stack = [] visited = [] if s == -2: - s = list(self.graph)[0] + s = next(iter(self.graph)) stack.append(s) visited.append(s) ss = s @@ -146,7 +146,7 @@ def topological_sort(self, s=-2): def cycle_nodes(self): stack = [] visited = [] - s = list(self.graph)[0] + s = next(iter(self.graph)) stack.append(s) visited.append(s) parent = -2 @@ -199,7 +199,7 @@ def cycle_nodes(self): def has_cycle(self): stack = [] visited = [] - s = list(self.graph)[0] + s = next(iter(self.graph)) stack.append(s) visited.append(s) parent = -2 @@ -305,7 +305,7 @@ def dfs(self, s=-2, d=-1): stack = [] visited = [] if s == -2: - s = list(self.graph)[0] + s = next(iter(self.graph)) stack.append(s) visited.append(s) ss = s @@ -353,7 +353,7 @@ def bfs(self, s=-2): d = deque() visited = [] if s == -2: - s = list(self.graph)[0] + s = next(iter(self.graph)) d.append(s) visited.append(s) while d: @@ -371,7 +371,7 @@ def degree(self, u): def cycle_nodes(self): stack = [] visited = [] - s = list(self.graph)[0] + s = next(iter(self.graph)) stack.append(s) visited.append(s) parent = -2 @@ -424,7 +424,7 @@ def cycle_nodes(self): def has_cycle(self): stack = [] visited = [] - s = list(self.graph)[0] + s = next(iter(self.graph)) stack.append(s) visited.append(s) parent = -2 diff --git a/graphs/edmonds_karp_multiple_source_and_sink.py b/graphs/edmonds_karp_multiple_source_and_sink.py index d0610804109f..5c774f4b812b 100644 --- a/graphs/edmonds_karp_multiple_source_and_sink.py +++ b/graphs/edmonds_karp_multiple_source_and_sink.py @@ -113,7 +113,7 @@ def _algorithm(self): vertices_list = [ i for i in range(self.verticies_count) - if i != self.source_index and i != self.sink_index + if i not in {self.source_index, self.sink_index} ] # move through list diff --git a/maths/factorial.py b/maths/factorial.py index bbf0efc011d8..18cacdef9b1f 100644 --- a/maths/factorial.py +++ b/maths/factorial.py @@ -55,7 +55,7 @@ def factorial_recursive(n: int) -> int: raise ValueError("factorial() only accepts integral values") if n < 0: raise ValueError("factorial() not defined for negative values") - return 1 if n == 0 or n == 1 else n * factorial(n - 1) + return 1 if n in {0, 1} else n * factorial(n - 1) if __name__ == "__main__": diff --git a/maths/primelib.py b/maths/primelib.py index 81d5737063f0..28b5aee9dcc8 100644 --- a/maths/primelib.py +++ b/maths/primelib.py @@ -154,7 +154,7 @@ def prime_factorization(number): quotient = number - if number == 0 or number == 1: + if number in {0, 1}: ans.append(number) # if 'number' not prime then builds the prime factorization of 'number' diff --git a/other/davisb_putnamb_logemannb_loveland.py b/other/davisb_putnamb_logemannb_loveland.py index a1bea5b3992e..f5fb103ba528 100644 --- a/other/davisb_putnamb_logemannb_loveland.py +++ b/other/davisb_putnamb_logemannb_loveland.py @@ -253,7 +253,7 @@ def find_unit_clauses( unit_symbols = [] for clause in clauses: if len(clause) == 1: - unit_symbols.append(list(clause.literals.keys())[0]) + unit_symbols.append(next(iter(clause.literals.keys()))) else: f_count, n_count = 0, 0 for literal, value in clause.literals.items(): diff --git a/project_euler/problem_009/sol3.py b/project_euler/problem_009/sol3.py index d299f821d4f6..37340d3063bb 100644 --- a/project_euler/problem_009/sol3.py +++ b/project_euler/problem_009/sol3.py @@ -28,12 +28,16 @@ def solution() -> int: 31875000 """ - return [ - a * b * (1000 - a - b) - for a in range(1, 999) - for b in range(a, 999) - if (a * a + b * b == (1000 - a - b) ** 2) - ][0] + return next( + iter( + [ + a * b * (1000 - a - b) + for a in range(1, 999) + for b in range(a, 999) + if (a * a + b * b == (1000 - a - b) ** 2) + ] + ) + ) if __name__ == "__main__": diff --git a/quantum/ripple_adder_classic.py b/quantum/ripple_adder_classic.py index b604395bc583..2284141ccac2 100644 --- a/quantum/ripple_adder_classic.py +++ b/quantum/ripple_adder_classic.py @@ -107,7 +107,7 @@ def ripple_adder( res = qiskit.execute(circuit, backend, shots=1).result() # The result is in binary. Convert it back to int - return int(list(res.get_counts())[0], 2) + return int(next(iter(res.get_counts())), 2) if __name__ == "__main__": diff --git a/strings/min_cost_string_conversion.py b/strings/min_cost_string_conversion.py index 089c2532f900..0fad0b88c370 100644 --- a/strings/min_cost_string_conversion.py +++ b/strings/min_cost_string_conversion.py @@ -61,7 +61,7 @@ def assemble_transformation(ops: list[list[str]], i: int, j: int) -> list[str]: if i == 0 and j == 0: return [] else: - if ops[i][j][0] == "C" or ops[i][j][0] == "R": + if ops[i][j][0] in {"C", "R"}: seq = assemble_transformation(ops, i - 1, j - 1) seq.append(ops[i][j]) return seq diff --git a/web_programming/convert_number_to_words.py b/web_programming/convert_number_to_words.py index 1e293df9660c..dac9e3e38e7c 100644 --- a/web_programming/convert_number_to_words.py +++ b/web_programming/convert_number_to_words.py @@ -90,9 +90,7 @@ def convert(number: int) -> str: else: addition = "" if counter in placevalue: - if current == 0 and ((temp_num % 100) // 10) == 0: - addition = "" - else: + if current != 0 and ((temp_num % 100) // 10) != 0: addition = placevalue[counter] if ((temp_num % 100) // 10) == 1: words = teens[current] + addition + words From f7531d9874e0dd3682bf0ed7ae408927e1fae472 Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Sat, 22 Jul 2023 03:11:04 -0700 Subject: [PATCH 117/757] Add note in `CONTRIBUTING.md` about not asking to be assigned to issues (#8871) * Add note in CONTRIBUTING.md about not asking to be assigned to issues Add a paragraph to CONTRIBUTING.md explicitly asking contributors to not ask to be assigned to issues * Update CONTRIBUTING.md * Update CONTRIBUTING.md --------- Co-authored-by: Christian Clauss --- CONTRIBUTING.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 618cca868d83..4a1bb652738f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -25,6 +25,8 @@ We appreciate any contribution, from fixing a grammar mistake in a comment to im Your contribution will be tested by our [automated testing on GitHub Actions](https://github.com/TheAlgorithms/Python/actions) to save time and mental energy. After you have submitted your pull request, you should see the GitHub Actions tests start to run at the bottom of your submission page. If those tests fail, then click on the ___details___ button try to read through the GitHub Actions output to understand the failure. If you do not understand, please leave a comment on your submission page and a community member will try to help. +If you are interested in resolving an [open issue](https://github.com/TheAlgorithms/Python/issues), simply make a pull request with your proposed fix. __We do not assign issues in this repo__ so please do not ask for permission to work on an issue. + Please help us keep our issue list small by adding `Fixes #{$ISSUE_NUMBER}` to the description of pull requests that resolve open issues. For example, if your pull request fixes issue #10, then please add the following to its description: ``` From 9e08c7726dee5b18585a76e54c71922ca96c0b3a Mon Sep 17 00:00:00 2001 From: Caeden Perelli-Harris Date: Sat, 22 Jul 2023 13:34:19 +0300 Subject: [PATCH 118/757] Small docstring time complexity fix in number_container _system (#8875) * fix: Write time is O(log n) not O(n log n) * chore: Update pre-commit ruff version * revert: Undo previous commit --- other/number_container_system.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/other/number_container_system.py b/other/number_container_system.py index f547bc8a229e..6c95dd0a3544 100644 --- a/other/number_container_system.py +++ b/other/number_container_system.py @@ -1,6 +1,6 @@ """ A number container system that uses binary search to delete and insert values into -arrays with O(n logn) write times and O(1) read times. +arrays with O(log n) write times and O(1) read times. This container system holds integers at indexes. From a03b739d23b59890b59d2d2288ebaa56e3be47ce Mon Sep 17 00:00:00 2001 From: Sangmin Jeon Date: Mon, 24 Jul 2023 18:29:05 +0900 Subject: [PATCH 119/757] Fix `radix_tree.py` insertion fail in ["*X", "*XX"] cases (#8870) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix insertion fail in ["*X", "*XX"] cases Consider a word, and a copy of that word, but with the last letter repeating twice. (e.g., ["ABC", "ABCC"]) When adding the second word's last letter, it only compares the previous word's prefix—the last letter of the word already in the Radix Tree: 'C'—and the letter to be added—the last letter of the word we're currently adding: 'C'. So it wrongly passes the "Case 1" check, marks the current node as a leaf node when it already was, then returns when there's still one more letter to add. The issue arises because `prefix` includes the letter of the node itself. (e.g., `nodes: {'C' : RadixNode()}, is_leaf: True, prefix: 'C'`) It can be easily fixed by simply adding the `is_leaf` check, asking if there are more letters to be added. - Test Case: `"A AA AAA AAAA"` - Fixed correct output: ``` Words: ['A', 'AA', 'AAA', 'AAAA'] Tree: - A (leaf) -- A (leaf) --- A (leaf) ---- A (leaf) ``` - Current incorrect output: ``` Words: ['A', 'AA', 'AAA', 'AAAA'] Tree: - A (leaf) -- AA (leaf) --- A (leaf) ``` *N.B.* This passed test cases for [Croatian Open Competition in Informatics 2012/2013 Contest #3 Task 5 HERKABE](https://hsin.hr/coci/archive/2012_2013/) * Add a doctest for previous fix * improve doctest readability --- data_structures/trie/radix_tree.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/data_structures/trie/radix_tree.py b/data_structures/trie/radix_tree.py index cf2f25c29f13..fadc50cb49a7 100644 --- a/data_structures/trie/radix_tree.py +++ b/data_structures/trie/radix_tree.py @@ -54,10 +54,17 @@ def insert(self, word: str) -> None: word (str): word to insert >>> RadixNode("myprefix").insert("mystring") + + >>> root = RadixNode() + >>> root.insert_many(['myprefix', 'myprefixA', 'myprefixAA']) + >>> root.print_tree() + - myprefix (leaf) + -- A (leaf) + --- A (leaf) """ # Case 1: If the word is the prefix of the node # Solution: We set the current node as leaf - if self.prefix == word: + if self.prefix == word and not self.is_leaf: self.is_leaf = True # Case 2: The node has no edges that have a prefix to the word From b77e6adf3abba674eb83ab7c0182bd6c89c08891 Mon Sep 17 00:00:00 2001 From: HManiac74 <63391783+HManiac74@users.noreply.github.com> Date: Tue, 25 Jul 2023 22:23:20 +0200 Subject: [PATCH 120/757] Add Docker devcontainer configuration files (#8887) * Added Docker container configuration files * Update Dockerfile Copy and install requirements * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Updated Docker devcontainer configuration * Update requierements.txt * Update Dockerfile * Update Dockerfile * Update .devcontainer/devcontainer.json Co-authored-by: Christian Clauss * Update Dockerfile * Update Dockerfile. Add linebreak --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- .devcontainer/Dockerfile | 6 +++++ .devcontainer/devcontainer.json | 42 +++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 .devcontainer/Dockerfile create mode 100644 .devcontainer/devcontainer.json diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 000000000000..27b25c09b1c9 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,6 @@ +# https://github.com/microsoft/vscode-dev-containers/blob/main/containers/python-3/README.md +ARG VARIANT=3.11-bookworm +FROM mcr.microsoft.com/vscode/devcontainers/python:${VARIANT} +COPY requirements.txt /tmp/pip-tmp/ +RUN python3 -m pip install --upgrade pip \ + && python3 -m pip install --no-cache-dir install ruff -r /tmp/pip-tmp/requirements.txt diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 000000000000..c5a855b2550c --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,42 @@ +{ + "name": "Python 3", + "build": { + "dockerfile": "Dockerfile", + "context": "..", + "args": { + // Update 'VARIANT' to pick a Python version: 3, 3.10, 3.9, 3.8, 3.7, 3.6 + // Append -bullseye or -buster to pin to an OS version. + // Use -bullseye variants on local on arm64/Apple Silicon. + "VARIANT": "3.11-bookworm", + } + }, + + // Configure tool-specific properties. + "customizations": { + // Configure properties specific to VS Code. + "vscode": { + // Set *default* container specific settings.json values on container create. + "settings": { + "python.defaultInterpreterPath": "/usr/local/bin/python", + "python.linting.enabled": true, + "python.formatting.blackPath": "/usr/local/py-utils/bin/black", + "python.linting.mypyPath": "/usr/local/py-utils/bin/mypy" + }, + + // Add the IDs of extensions you want installed when the container is created. + "extensions": [ + "ms-python.python", + "ms-python.vscode-pylance" + ] + } + }, + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [], + + // Use 'postCreateCommand' to run commands after the container is created. + // "postCreateCommand": "pip3 install --user -r requirements.txt", + + // Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. + "remoteUser": "vscode" +} From dbaff345724040b270b3097cb02759f36ce0ef46 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Fri, 28 Jul 2023 18:53:09 +0200 Subject: [PATCH 121/757] Fix ruff rules ISC flake8-implicit-str-concat (#8892) --- ciphers/diffie_hellman.py | 244 ++++++++++++------------- compression/burrows_wheeler.py | 2 +- neural_network/input_data.py | 4 +- pyproject.toml | 2 +- strings/is_srilankan_phone_number.py | 4 +- web_programming/world_covid19_stats.py | 5 +- 6 files changed, 128 insertions(+), 133 deletions(-) diff --git a/ciphers/diffie_hellman.py b/ciphers/diffie_hellman.py index cd40a6b9c3b3..aec7fb3eaf17 100644 --- a/ciphers/diffie_hellman.py +++ b/ciphers/diffie_hellman.py @@ -10,13 +10,13 @@ 5: { "prime": int( "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" - + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" - + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" - + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" - + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" - + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" - + "83655D23DCA3AD961C62F356208552BB9ED529077096966D" - + "670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF", + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" + "83655D23DCA3AD961C62F356208552BB9ED529077096966D" + "670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF", base=16, ), "generator": 2, @@ -25,16 +25,16 @@ 14: { "prime": int( "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" - + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" - + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" - + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" - + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" - + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" - + "83655D23DCA3AD961C62F356208552BB9ED529077096966D" - + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" - + "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" - + "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" - + "15728E5A8AACAA68FFFFFFFFFFFFFFFF", + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" + "83655D23DCA3AD961C62F356208552BB9ED529077096966D" + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" + "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" + "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" + "15728E5A8AACAA68FFFFFFFFFFFFFFFF", base=16, ), "generator": 2, @@ -43,21 +43,21 @@ 15: { "prime": int( "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" - + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" - + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" - + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" - + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" - + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" - + "83655D23DCA3AD961C62F356208552BB9ED529077096966D" - + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" - + "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" - + "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" - + "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" - + "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" - + "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" - + "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" - + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" - + "43DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF", + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" + "83655D23DCA3AD961C62F356208552BB9ED529077096966D" + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" + "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" + "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" + "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" + "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" + "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" + "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" + "43DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF", base=16, ), "generator": 2, @@ -66,27 +66,27 @@ 16: { "prime": int( "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" - + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" - + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" - + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" - + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" - + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" - + "83655D23DCA3AD961C62F356208552BB9ED529077096966D" - + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" - + "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" - + "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" - + "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" - + "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" - + "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" - + "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" - + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" - + "43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" - + "88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA" - + "2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" - + "287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED" - + "1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" - + "93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199" - + "FFFFFFFFFFFFFFFF", + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" + "83655D23DCA3AD961C62F356208552BB9ED529077096966D" + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" + "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" + "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" + "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" + "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" + "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" + "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" + "43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" + "88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA" + "2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" + "287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED" + "1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" + "93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199" + "FFFFFFFFFFFFFFFF", base=16, ), "generator": 2, @@ -95,33 +95,33 @@ 17: { "prime": int( "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08" - + "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B" - + "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9" - + "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6" - + "49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8" - + "FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D" - + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C" - + "180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718" - + "3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D" - + "04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D" - + "B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226" - + "1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C" - + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC" - + "E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26" - + "99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB" - + "04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2" - + "233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127" - + "D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492" - + "36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406" - + "AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918" - + "DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B33205151" - + "2BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03" - + "F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97F" - + "BEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA" - + "CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58B" - + "B7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632" - + "387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E" - + "6DCC4024FFFFFFFFFFFFFFFF", + "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B" + "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9" + "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6" + "49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8" + "FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D" + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C" + "180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718" + "3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D" + "04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D" + "B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226" + "1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C" + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC" + "E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26" + "99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB" + "04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2" + "233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127" + "D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492" + "36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406" + "AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918" + "DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B33205151" + "2BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03" + "F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97F" + "BEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA" + "CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58B" + "B7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632" + "387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E" + "6DCC4024FFFFFFFFFFFFFFFF", base=16, ), "generator": 2, @@ -130,48 +130,48 @@ 18: { "prime": int( "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" - + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" - + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" - + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" - + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" - + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" - + "83655D23DCA3AD961C62F356208552BB9ED529077096966D" - + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" - + "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" - + "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" - + "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" - + "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" - + "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" - + "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" - + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" - + "43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" - + "88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA" - + "2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" - + "287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED" - + "1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" - + "93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492" - + "36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BD" - + "F8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831" - + "179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1B" - + "DB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF" - + "5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6" - + "D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F3" - + "23A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA" - + "CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE328" - + "06A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55C" - + "DA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE" - + "12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E4" - + "38777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300" - + "741FA7BF8AFC47ED2576F6936BA424663AAB639C5AE4F568" - + "3423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD9" - + "22222E04A4037C0713EB57A81A23F0C73473FC646CEA306B" - + "4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A" - + "062B3CF5B3A278A66D2A13F83F44F82DDF310EE074AB6A36" - + "4597E899A0255DC164F31CC50846851DF9AB48195DED7EA1" - + "B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F92" - + "4009438B481C6CD7889A002ED5EE382BC9190DA6FC026E47" - + "9558E4475677E9AA9E3050E2765694DFC81F56E880B96E71" - + "60C980DD98EDD3DFFFFFFFFFFFFFFFFF", + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" + "83655D23DCA3AD961C62F356208552BB9ED529077096966D" + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" + "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" + "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" + "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" + "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" + "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" + "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" + "43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" + "88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA" + "2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" + "287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED" + "1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" + "93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492" + "36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BD" + "F8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831" + "179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1B" + "DB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF" + "5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6" + "D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F3" + "23A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA" + "CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE328" + "06A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55C" + "DA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE" + "12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E4" + "38777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300" + "741FA7BF8AFC47ED2576F6936BA424663AAB639C5AE4F568" + "3423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD9" + "22222E04A4037C0713EB57A81A23F0C73473FC646CEA306B" + "4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A" + "062B3CF5B3A278A66D2A13F83F44F82DDF310EE074AB6A36" + "4597E899A0255DC164F31CC50846851DF9AB48195DED7EA1" + "B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F92" + "4009438B481C6CD7889A002ED5EE382BC9190DA6FC026E47" + "9558E4475677E9AA9E3050E2765694DFC81F56E880B96E71" + "60C980DD98EDD3DFFFFFFFFFFFFFFFFF", base=16, ), "generator": 2, diff --git a/compression/burrows_wheeler.py b/compression/burrows_wheeler.py index 0916b8a654d2..52bb045d9398 100644 --- a/compression/burrows_wheeler.py +++ b/compression/burrows_wheeler.py @@ -150,7 +150,7 @@ def reverse_bwt(bwt_string: str, idx_original_string: int) -> str: raise ValueError("The parameter idx_original_string must not be lower than 0.") if idx_original_string >= len(bwt_string): raise ValueError( - "The parameter idx_original_string must be lower than" " len(bwt_string)." + "The parameter idx_original_string must be lower than len(bwt_string)." ) ordered_rotations = [""] * len(bwt_string) diff --git a/neural_network/input_data.py b/neural_network/input_data.py index 94c018ece9ba..a58e64907e45 100644 --- a/neural_network/input_data.py +++ b/neural_network/input_data.py @@ -263,9 +263,7 @@ def _maybe_download(filename, work_directory, source_url): return filepath -@deprecated( - None, "Please use alternatives such as:" " tensorflow_datasets.load('mnist')" -) +@deprecated(None, "Please use alternatives such as: tensorflow_datasets.load('mnist')") def read_data_sets( train_dir, fake_data=False, diff --git a/pyproject.toml b/pyproject.toml index 4f21a95190da..f9091fb8578d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -49,6 +49,7 @@ select = [ # https://beta.ruff.rs/docs/rules "ICN", # flake8-import-conventions "INP", # flake8-no-pep420 "INT", # flake8-gettext + "ISC", # flake8-implicit-str-concat "N", # pep8-naming "NPY", # NumPy-specific rules "PGH", # pygrep-hooks @@ -72,7 +73,6 @@ select = [ # https://beta.ruff.rs/docs/rules # "DJ", # flake8-django # "ERA", # eradicate -- DO NOT FIX # "FBT", # flake8-boolean-trap # FIX ME - # "ISC", # flake8-implicit-str-concat # FIX ME # "PD", # pandas-vet # "PT", # flake8-pytest-style # "PTH", # flake8-use-pathlib # FIX ME diff --git a/strings/is_srilankan_phone_number.py b/strings/is_srilankan_phone_number.py index 7bded93f7f1d..6456f85e1a3d 100644 --- a/strings/is_srilankan_phone_number.py +++ b/strings/is_srilankan_phone_number.py @@ -22,9 +22,7 @@ def is_sri_lankan_phone_number(phone: str) -> bool: False """ - pattern = re.compile( - r"^(?:0|94|\+94|0{2}94)" r"7(0|1|2|4|5|6|7|8)" r"(-| |)" r"\d{7}$" - ) + pattern = re.compile(r"^(?:0|94|\+94|0{2}94)7(0|1|2|4|5|6|7|8)(-| |)\d{7}$") return bool(re.search(pattern, phone)) diff --git a/web_programming/world_covid19_stats.py b/web_programming/world_covid19_stats.py index 1dd1ff6d188e..ca81abdc4ce9 100644 --- a/web_programming/world_covid19_stats.py +++ b/web_programming/world_covid19_stats.py @@ -22,6 +22,5 @@ def world_covid19_stats(url: str = "https://www.worldometers.info/coronavirus") if __name__ == "__main__": - print("\033[1m" + "COVID-19 Status of the World" + "\033[0m\n") - for key, value in world_covid19_stats().items(): - print(f"{key}\n{value}\n") + print("\033[1m COVID-19 Status of the World \033[0m\n") + print("\n".join(f"{key}\n{value}" for key, value in world_covid19_stats().items())) From 46454e204cc587d1ef044e4b1a11050c30aab4f6 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Fri, 28 Jul 2023 18:54:45 +0200 Subject: [PATCH 122/757] [skip-ci] In .devcontainer/Dockerfile: pipx install pre-commit ruff (#8893) [skip-ci] In .devcontainer/Dockerfile: pipx install pre-commit ruff --- .devcontainer/Dockerfile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 27b25c09b1c9..b5a5347c66b0 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -3,4 +3,6 @@ ARG VARIANT=3.11-bookworm FROM mcr.microsoft.com/vscode/devcontainers/python:${VARIANT} COPY requirements.txt /tmp/pip-tmp/ RUN python3 -m pip install --upgrade pip \ - && python3 -m pip install --no-cache-dir install ruff -r /tmp/pip-tmp/requirements.txt + && python3 -m pip install --no-cache-dir install -r /tmp/pip-tmp/requirements.txt \ + && pipx install pre-commit ruff \ + && pre-commit install From 4a83e3f0b1b2a3b414134c3498e57c0fea3b9fcf Mon Sep 17 00:00:00 2001 From: Caeden Perelli-Harris Date: Fri, 28 Jul 2023 21:12:31 +0300 Subject: [PATCH 123/757] Fix failing build due to missing requirement (#8900) * feat(cellular_automata): Create wa-tor algorithm * updating DIRECTORY.md * chore(quality): Implement algo-keeper bot changes * build: Fix broken ci * git rm cellular_automata/wa_tor.py * updating DIRECTORY.md --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: Christian Clauss --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index acfbc823e77f..2702523d542e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,6 +9,7 @@ pandas pillow projectq qiskit +qiskit-aer requests rich scikit-fuzzy From e406801f9e3967ff0533dfe8cb98a3249db48d33 Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Fri, 28 Jul 2023 11:17:46 -0700 Subject: [PATCH 124/757] Reimplement polynomial_regression.py (#8889) * Reimplement polynomial_regression.py Rename machine_learning/polymonial_regression.py to machine_learning/polynomial_regression.py Reimplement machine_learning/polynomial_regression.py using numpy because the old original implementation was just a how-to on doing polynomial regression using sklearn Add detailed function documentation, doctests, and algorithm explanation * updating DIRECTORY.md * Fix matrix formatting in docstrings * Try to fix failing doctest * Debugging failing doctest * Fix failing doctest attempt 2 * Remove unnecessary return value descriptions in docstrings * Readd placeholder doctest for main function * Fix typo in algorithm description --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 2 +- machine_learning/polymonial_regression.py | 44 ----- machine_learning/polynomial_regression.py | 213 ++++++++++++++++++++++ 3 files changed, 214 insertions(+), 45 deletions(-) delete mode 100644 machine_learning/polymonial_regression.py create mode 100644 machine_learning/polynomial_regression.py diff --git a/DIRECTORY.md b/DIRECTORY.md index 77938f45011b..133a1ab019d8 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -511,7 +511,7 @@ * Lstm * [Lstm Prediction](machine_learning/lstm/lstm_prediction.py) * [Multilayer Perceptron Classifier](machine_learning/multilayer_perceptron_classifier.py) - * [Polymonial Regression](machine_learning/polymonial_regression.py) + * [Polynomial Regression](machine_learning/polynomial_regression.py) * [Scoring Functions](machine_learning/scoring_functions.py) * [Self Organizing Map](machine_learning/self_organizing_map.py) * [Sequential Minimum Optimization](machine_learning/sequential_minimum_optimization.py) diff --git a/machine_learning/polymonial_regression.py b/machine_learning/polymonial_regression.py deleted file mode 100644 index 487fb814526f..000000000000 --- a/machine_learning/polymonial_regression.py +++ /dev/null @@ -1,44 +0,0 @@ -import pandas as pd -from matplotlib import pyplot as plt -from sklearn.linear_model import LinearRegression - -# Splitting the dataset into the Training set and Test set -from sklearn.model_selection import train_test_split - -# Fitting Polynomial Regression to the dataset -from sklearn.preprocessing import PolynomialFeatures - -# Importing the dataset -dataset = pd.read_csv( - "https://s3.us-west-2.amazonaws.com/public.gamelab.fun/dataset/" - "position_salaries.csv" -) -X = dataset.iloc[:, 1:2].values -y = dataset.iloc[:, 2].values - - -X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0) - - -poly_reg = PolynomialFeatures(degree=4) -X_poly = poly_reg.fit_transform(X) -pol_reg = LinearRegression() -pol_reg.fit(X_poly, y) - - -# Visualizing the Polymonial Regression results -def viz_polymonial(): - plt.scatter(X, y, color="red") - plt.plot(X, pol_reg.predict(poly_reg.fit_transform(X)), color="blue") - plt.title("Truth or Bluff (Linear Regression)") - plt.xlabel("Position level") - plt.ylabel("Salary") - plt.show() - - -if __name__ == "__main__": - viz_polymonial() - - # Predicting a new result with Polymonial Regression - pol_reg.predict(poly_reg.fit_transform([[5.5]])) - # output should be 132148.43750003 diff --git a/machine_learning/polynomial_regression.py b/machine_learning/polynomial_regression.py new file mode 100644 index 000000000000..5bafea96f41e --- /dev/null +++ b/machine_learning/polynomial_regression.py @@ -0,0 +1,213 @@ +""" +Polynomial regression is a type of regression analysis that models the relationship +between a predictor x and the response y as an mth-degree polynomial: + +y = β₀ + β₁x + β₂x² + ... + βₘxᵐ + ε + +By treating x, x², ..., xᵐ as distinct variables, we see that polynomial regression is a +special case of multiple linear regression. Therefore, we can use ordinary least squares +(OLS) estimation to estimate the vector of model parameters β = (β₀, β₁, β₂, ..., βₘ) +for polynomial regression: + +β = (XᵀX)⁻¹Xᵀy = X⁺y + +where X is the design matrix, y is the response vector, and X⁺ denotes the Moore–Penrose +pseudoinverse of X. In the case of polynomial regression, the design matrix is + + |1 x₁ x₁² ⋯ x₁ᵐ| +X = |1 x₂ x₂² ⋯ x₂ᵐ| + |⋮ ⋮ ⋮ ⋱ ⋮ | + |1 xₙ xₙ² ⋯ xₙᵐ| + +In OLS estimation, inverting XᵀX to compute X⁺ can be very numerically unstable. This +implementation sidesteps this need to invert XᵀX by computing X⁺ using singular value +decomposition (SVD): + +β = VΣ⁺Uᵀy + +where UΣVᵀ is an SVD of X. + +References: + - https://en.wikipedia.org/wiki/Polynomial_regression + - https://en.wikipedia.org/wiki/Moore%E2%80%93Penrose_inverse + - https://en.wikipedia.org/wiki/Numerical_methods_for_linear_least_squares + - https://en.wikipedia.org/wiki/Singular_value_decomposition +""" + +import matplotlib.pyplot as plt +import numpy as np + + +class PolynomialRegression: + __slots__ = "degree", "params" + + def __init__(self, degree: int) -> None: + """ + @raises ValueError: if the polynomial degree is negative + """ + if degree < 0: + raise ValueError("Polynomial degree must be non-negative") + + self.degree = degree + self.params = None + + @staticmethod + def _design_matrix(data: np.ndarray, degree: int) -> np.ndarray: + """ + Constructs a polynomial regression design matrix for the given input data. For + input data x = (x₁, x₂, ..., xₙ) and polynomial degree m, the design matrix is + the Vandermonde matrix + + |1 x₁ x₁² ⋯ x₁ᵐ| + X = |1 x₂ x₂² ⋯ x₂ᵐ| + |⋮ ⋮ ⋮ ⋱ ⋮ | + |1 xₙ xₙ² ⋯ xₙᵐ| + + Reference: https://en.wikipedia.org/wiki/Vandermonde_matrix + + @param data: the input predictor values x, either for model fitting or for + prediction + @param degree: the polynomial degree m + @returns: the Vandermonde matrix X (see above) + @raises ValueError: if input data is not N x 1 + + >>> x = np.array([0, 1, 2]) + >>> PolynomialRegression._design_matrix(x, degree=0) + array([[1], + [1], + [1]]) + >>> PolynomialRegression._design_matrix(x, degree=1) + array([[1, 0], + [1, 1], + [1, 2]]) + >>> PolynomialRegression._design_matrix(x, degree=2) + array([[1, 0, 0], + [1, 1, 1], + [1, 2, 4]]) + >>> PolynomialRegression._design_matrix(x, degree=3) + array([[1, 0, 0, 0], + [1, 1, 1, 1], + [1, 2, 4, 8]]) + >>> PolynomialRegression._design_matrix(np.array([[0, 0], [0 , 0]]), degree=3) + Traceback (most recent call last): + ... + ValueError: Data must have dimensions N x 1 + """ + rows, *remaining = data.shape + if remaining: + raise ValueError("Data must have dimensions N x 1") + + return np.vander(data, N=degree + 1, increasing=True) + + def fit(self, x_train: np.ndarray, y_train: np.ndarray) -> None: + """ + Computes the polynomial regression model parameters using ordinary least squares + (OLS) estimation: + + β = (XᵀX)⁻¹Xᵀy = X⁺y + + where X⁺ denotes the Moore–Penrose pseudoinverse of the design matrix X. This + function computes X⁺ using singular value decomposition (SVD). + + References: + - https://en.wikipedia.org/wiki/Moore%E2%80%93Penrose_inverse + - https://en.wikipedia.org/wiki/Singular_value_decomposition + - https://en.wikipedia.org/wiki/Multicollinearity + + @param x_train: the predictor values x for model fitting + @param y_train: the response values y for model fitting + @raises ArithmeticError: if X isn't full rank, then XᵀX is singular and β + doesn't exist + + >>> x = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) + >>> y = x**3 - 2 * x**2 + 3 * x - 5 + >>> poly_reg = PolynomialRegression(degree=3) + >>> poly_reg.fit(x, y) + >>> poly_reg.params + array([-5., 3., -2., 1.]) + >>> poly_reg = PolynomialRegression(degree=20) + >>> poly_reg.fit(x, y) + Traceback (most recent call last): + ... + ArithmeticError: Design matrix is not full rank, can't compute coefficients + + Make sure errors don't grow too large: + >>> coefs = np.array([-250, 50, -2, 36, 20, -12, 10, 2, -1, -15, 1]) + >>> y = PolynomialRegression._design_matrix(x, len(coefs) - 1) @ coefs + >>> poly_reg = PolynomialRegression(degree=len(coefs) - 1) + >>> poly_reg.fit(x, y) + >>> np.allclose(poly_reg.params, coefs, atol=10e-3) + True + """ + X = PolynomialRegression._design_matrix(x_train, self.degree) # noqa: N806 + _, cols = X.shape + if np.linalg.matrix_rank(X) < cols: + raise ArithmeticError( + "Design matrix is not full rank, can't compute coefficients" + ) + + # np.linalg.pinv() computes the Moore–Penrose pseudoinverse using SVD + self.params = np.linalg.pinv(X) @ y_train + + def predict(self, data: np.ndarray) -> np.ndarray: + """ + Computes the predicted response values y for the given input data by + constructing the design matrix X and evaluating y = Xβ. + + @param data: the predictor values x for prediction + @returns: the predicted response values y = Xβ + @raises ArithmeticError: if this function is called before the model + parameters are fit + + >>> x = np.array([0, 1, 2, 3, 4]) + >>> y = x**3 - 2 * x**2 + 3 * x - 5 + >>> poly_reg = PolynomialRegression(degree=3) + >>> poly_reg.fit(x, y) + >>> poly_reg.predict(np.array([-1])) + array([-11.]) + >>> poly_reg.predict(np.array([-2])) + array([-27.]) + >>> poly_reg.predict(np.array([6])) + array([157.]) + >>> PolynomialRegression(degree=3).predict(x) + Traceback (most recent call last): + ... + ArithmeticError: Predictor hasn't been fit yet + """ + if self.params is None: + raise ArithmeticError("Predictor hasn't been fit yet") + + return PolynomialRegression._design_matrix(data, self.degree) @ self.params + + +def main() -> None: + """ + Fit a polynomial regression model to predict fuel efficiency using seaborn's mpg + dataset + + >>> pass # Placeholder, function is only for demo purposes + """ + import seaborn as sns + + mpg_data = sns.load_dataset("mpg") + + poly_reg = PolynomialRegression(degree=2) + poly_reg.fit(mpg_data.weight, mpg_data.mpg) + + weight_sorted = np.sort(mpg_data.weight) + predictions = poly_reg.predict(weight_sorted) + + plt.scatter(mpg_data.weight, mpg_data.mpg, color="gray", alpha=0.5) + plt.plot(weight_sorted, predictions, color="red", linewidth=3) + plt.title("Predicting Fuel Efficiency Using Polynomial Regression") + plt.xlabel("Weight (lbs)") + plt.ylabel("Fuel Efficiency (mpg)") + plt.show() + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + + main() From a0b642cfe58c215b8ead3f2a40655e144e07aacc Mon Sep 17 00:00:00 2001 From: Alex Bernhardt <54606095+FatAnorexic@users.noreply.github.com> Date: Fri, 28 Jul 2023 14:30:05 -0400 Subject: [PATCH 125/757] Physics/basic orbital capture (#8857) * Added file basic_orbital_capture * updating DIRECTORY.md * added second source * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fixed spelling errors * accepted changes * updating DIRECTORY.md * corrected spelling error * Added file basic_orbital_capture * added second source * fixed spelling errors * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * applied changes * reviewed and checked file * added doctest * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * removed redundant constnant * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * added scipy imports * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * added doctests to capture_radii and scipy const * fixed conflicts * finalizing file. Added tests * Update physics/basic_orbital_capture.py --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- DIRECTORY.md | 1 + physics/basic_orbital_capture.py | 178 +++++++++++++++++++++++++++++++ 2 files changed, 179 insertions(+) create mode 100644 physics/basic_orbital_capture.py diff --git a/DIRECTORY.md b/DIRECTORY.md index 133a1ab019d8..29514579ceb0 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -741,6 +741,7 @@ ## Physics * [Archimedes Principle](physics/archimedes_principle.py) + * [Basic Orbital Capture](physics/basic_orbital_capture.py) * [Casimir Effect](physics/casimir_effect.py) * [Centripetal Force](physics/centripetal_force.py) * [Grahams Law](physics/grahams_law.py) diff --git a/physics/basic_orbital_capture.py b/physics/basic_orbital_capture.py new file mode 100644 index 000000000000..eeb45e60240c --- /dev/null +++ b/physics/basic_orbital_capture.py @@ -0,0 +1,178 @@ +from math import pow, sqrt + +from scipy.constants import G, c, pi + +""" +These two functions will return the radii of impact for a target object +of mass M and radius R as well as it's effective cross sectional area σ(sigma). +That is to say any projectile with velocity v passing within σ, will impact the +target object with mass M. The derivation of which is given at the bottom +of this file. + +The derivation shows that a projectile does not need to aim directly at the target +body in order to hit it, as R_capture>R_target. Astronomers refer to the effective +cross section for capture as σ=π*R_capture**2. + +This algorithm does not account for an N-body problem. + +""" + + +def capture_radii( + target_body_radius: float, target_body_mass: float, projectile_velocity: float +) -> float: + """ + Input Params: + ------------- + target_body_radius: Radius of the central body SI units: meters | m + target_body_mass: Mass of the central body SI units: kilograms | kg + projectile_velocity: Velocity of object moving toward central body + SI units: meters/second | m/s + Returns: + -------- + >>> capture_radii(6.957e8, 1.99e30, 25000.0) + 17209590691.0 + >>> capture_radii(-6.957e8, 1.99e30, 25000.0) + Traceback (most recent call last): + ... + ValueError: Radius cannot be less than 0 + >>> capture_radii(6.957e8, -1.99e30, 25000.0) + Traceback (most recent call last): + ... + ValueError: Mass cannot be less than 0 + >>> capture_radii(6.957e8, 1.99e30, c+1) + Traceback (most recent call last): + ... + ValueError: Cannot go beyond speed of light + + Returned SI units: + ------------------ + meters | m + """ + + if target_body_mass < 0: + raise ValueError("Mass cannot be less than 0") + if target_body_radius < 0: + raise ValueError("Radius cannot be less than 0") + if projectile_velocity > c: + raise ValueError("Cannot go beyond speed of light") + + escape_velocity_squared = (2 * G * target_body_mass) / target_body_radius + capture_radius = target_body_radius * sqrt( + 1 + escape_velocity_squared / pow(projectile_velocity, 2) + ) + return round(capture_radius, 0) + + +def capture_area(capture_radius: float) -> float: + """ + Input Param: + ------------ + capture_radius: The radius of orbital capture and impact for a central body of + mass M and a projectile moving towards it with velocity v + SI units: meters | m + Returns: + -------- + >>> capture_area(17209590691) + 9.304455331329126e+20 + >>> capture_area(-1) + Traceback (most recent call last): + ... + ValueError: Cannot have a capture radius less than 0 + + Returned SI units: + ------------------ + meters*meters | m**2 + """ + + if capture_radius < 0: + raise ValueError("Cannot have a capture radius less than 0") + sigma = pi * pow(capture_radius, 2) + return round(sigma, 0) + + +if __name__ == "__main__": + from doctest import testmod + + testmod() + +""" +Derivation: + +Let: Mt=target mass, Rt=target radius, v=projectile_velocity, + r_0=radius of projectile at instant 0 to CM of target + v_p=v at closest approach, + r_p=radius from projectile to target CM at closest approach, + R_capture= radius of impact for projectile with velocity v + +(1)At time=0 the projectile's energy falling from infinity| E=K+U=0.5*m*(v**2)+0 + + E_initial=0.5*m*(v**2) + +(2)at time=0 the angular momentum of the projectile relative to CM target| + L_initial=m*r_0*v*sin(Θ)->m*r_0*v*(R_capture/r_0)->m*v*R_capture + + L_i=m*v*R_capture + +(3)The energy of the projectile at closest approach will be its kinetic energy + at closest approach plus gravitational potential energy(-(GMm)/R)| + E_p=K_p+U_p->E_p=0.5*m*(v_p**2)-(G*Mt*m)/r_p + + E_p=0.0.5*m*(v_p**2)-(G*Mt*m)/r_p + +(4)The angular momentum of the projectile relative to the target at closest + approach will be L_p=m*r_p*v_p*sin(Θ), however relative to the target Θ=90° + sin(90°)=1| + + L_p=m*r_p*v_p +(5)Using conservation of angular momentum and energy, we can write a quadratic + equation that solves for r_p| + + (a) + Ei=Ep-> 0.5*m*(v**2)=0.5*m*(v_p**2)-(G*Mt*m)/r_p-> v**2=v_p**2-(2*G*Mt)/r_p + + (b) + Li=Lp-> m*v*R_capture=m*r_p*v_p-> v*R_capture=r_p*v_p-> v_p=(v*R_capture)/r_p + + (c) b plugs int a| + v**2=((v*R_capture)/r_p)**2-(2*G*Mt)/r_p-> + + v**2-(v**2)*(R_c**2)/(r_p**2)+(2*G*Mt)/r_p=0-> + + (v**2)*(r_p**2)+2*G*Mt*r_p-(v**2)*(R_c**2)=0 + + (d) Using the quadratic formula, we'll solve for r_p then rearrange to solve to + R_capture + + r_p=(-2*G*Mt ± sqrt(4*G^2*Mt^2+ 4(v^4*R_c^2)))/(2*v^2)-> + + r_p=(-G*Mt ± sqrt(G^2*Mt+v^4*R_c^2))/v^2-> + + r_p<0 is something we can ignore, as it has no physical meaning for our purposes.-> + + r_p=(-G*Mt)/v^2 + sqrt(G^2*Mt^2/v^4 + R_c^2) + + (e)We are trying to solve for R_c. We are looking for impact, so we want r_p=Rt + + Rt + G*Mt/v^2 = sqrt(G^2*Mt^2/v^4 + R_c^2)-> + + (Rt + G*Mt/v^2)^2 = G^2*Mt^2/v^4 + R_c^2-> + + Rt^2 + 2*G*Mt*Rt/v^2 + G^2*Mt^2/v^4 = G^2*Mt^2/v^4 + R_c^2-> + + Rt**2 + 2*G*Mt*Rt/v**2 = R_c**2-> + + Rt**2 * (1 + 2*G*Mt/Rt *1/v**2) = R_c**2-> + + escape velocity = sqrt(2GM/R)= v_escape**2=2GM/R-> + + Rt**2 * (1 + v_esc**2/v**2) = R_c**2-> + +(6) + R_capture = Rt * sqrt(1 + v_esc**2/v**2) + +Source: Problem Set 3 #8 c.Fall_2017|Honors Astronomy|Professor Rachel Bezanson + +Source #2: http://www.nssc.ac.cn/wxzygx/weixin/201607/P020160718380095698873.pdf + 8.8 Planetary Rendezvous: Pg.368 +""" From 0ef930697632a1f05dbbd956c4ccab0473025f5b Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Fri, 28 Jul 2023 13:08:40 -0700 Subject: [PATCH 126/757] Disable quantum/quantum_random.py (attempt 2) (#8902) * Disable quantum/quantum_random.py Temporarily disable quantum/quantum_random.py because it produces an illegal instruction error that causes all builds to fail * updating DIRECTORY.md * Disable quantum/quantum_random.py attempt 2 --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 1 - quantum/{quantum_random.py => quantum_random.py.DISABLED.txt} | 0 2 files changed, 1 deletion(-) rename quantum/{quantum_random.py => quantum_random.py.DISABLED.txt} (100%) diff --git a/DIRECTORY.md b/DIRECTORY.md index 29514579ceb0..af150b12984b 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -1063,7 +1063,6 @@ * [Q Fourier Transform](quantum/q_fourier_transform.py) * [Q Full Adder](quantum/q_full_adder.py) * [Quantum Entanglement](quantum/quantum_entanglement.py) - * [Quantum Random](quantum/quantum_random.py) * [Quantum Teleportation](quantum/quantum_teleportation.py) * [Ripple Adder Classic](quantum/ripple_adder_classic.py) * [Single Qubit Measure](quantum/single_qubit_measure.py) diff --git a/quantum/quantum_random.py b/quantum/quantum_random.py.DISABLED.txt similarity index 100% rename from quantum/quantum_random.py rename to quantum/quantum_random.py.DISABLED.txt From 2cfef0913a36e967d828881386ae78457cf65f33 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira Date: Sat, 29 Jul 2023 19:03:43 +0200 Subject: [PATCH 127/757] Fix greyscale computation and inverted coords (#8905) * Fix greyscale computation and inverted coords * Fix test * Add test cases * Add reference to the greyscaling formula --------- Co-authored-by: Colin Leroy-Mira --- digital_image_processing/dithering/burkes.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/digital_image_processing/dithering/burkes.py b/digital_image_processing/dithering/burkes.py index 0804104abe58..35aedc16d404 100644 --- a/digital_image_processing/dithering/burkes.py +++ b/digital_image_processing/dithering/burkes.py @@ -39,9 +39,18 @@ def __init__(self, input_img, threshold: int): def get_greyscale(cls, blue: int, green: int, red: int) -> float: """ >>> Burkes.get_greyscale(3, 4, 5) - 3.753 + 4.185 + >>> Burkes.get_greyscale(0, 0, 0) + 0.0 + >>> Burkes.get_greyscale(255, 255, 255) + 255.0 """ - return 0.114 * blue + 0.587 * green + 0.2126 * red + """ + Formula from https://en.wikipedia.org/wiki/HSL_and_HSV + cf Lightness section, and Fig 13c. + We use the first of four possible. + """ + return 0.114 * blue + 0.587 * green + 0.299 * red def process(self) -> None: for y in range(self.height): @@ -49,10 +58,10 @@ def process(self) -> None: greyscale = int(self.get_greyscale(*self.input_img[y][x])) if self.threshold > greyscale + self.error_table[y][x]: self.output_img[y][x] = (0, 0, 0) - current_error = greyscale + self.error_table[x][y] + current_error = greyscale + self.error_table[y][x] else: self.output_img[y][x] = (255, 255, 255) - current_error = greyscale + self.error_table[x][y] - 255 + current_error = greyscale + self.error_table[y][x] - 255 """ Burkes error propagation (`*` is current pixel): From d31750adece86ebf39a09dd3adb2039098f58586 Mon Sep 17 00:00:00 2001 From: Yatharth Mathur <31852880+yatharthmathur@users.noreply.github.com> Date: Sun, 30 Jul 2023 02:27:45 -0700 Subject: [PATCH 128/757] Pythonic implementation of LRU Cache (#4630) * Added a more pythonic implementation of LRU_Cache.[#4628] * Added test cases and doctest * Fixed doc tests * Added more tests in doctests and fixed return types fixes [#4628] * better doctests * added doctests to main() * Added dutch_national_flag.py in sorts. fixing [#4636] * Delete dutch_national_flag.py incorrect commit * Update lru_cache_pythonic.py * Remove pontification --------- Co-authored-by: Christian Clauss --- other/lru_cache_pythonic.py | 113 ++++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 other/lru_cache_pythonic.py diff --git a/other/lru_cache_pythonic.py b/other/lru_cache_pythonic.py new file mode 100644 index 000000000000..425691ef18cf --- /dev/null +++ b/other/lru_cache_pythonic.py @@ -0,0 +1,113 @@ +""" +This implementation of LRU Cache uses the in-built Python dictionary (dict) which from +Python 3.6 onward maintain the insertion order of keys and ensures O(1) operations on +insert, delete and access. https://docs.python.org/3/library/stdtypes.html#typesmapping +""" +from typing import Any, Hashable + + +class LRUCache(dict): + def __init__(self, capacity: int) -> None: + """ + Initialize an LRU Cache with given capacity. + capacity : int -> the capacity of the LRU Cache + >>> cache = LRUCache(2) + >>> cache + {} + """ + self.remaining: int = capacity + + def get(self, key: Hashable) -> Any: + """ + This method returns the value associated with the key. + key : A hashable object that is mapped to a value in the LRU cache. + return -> Any object that has been stored as a value in the LRU cache. + + >>> cache = LRUCache(2) + >>> cache.put(1,1) + >>> cache.get(1) + 1 + >>> cache.get(2) + Traceback (most recent call last): + ... + KeyError: '2 not found.' + """ + if key not in self: + raise KeyError(f"{key} not found.") + val = self.pop(key) # Pop the key-value and re-insert to maintain the order + self[key] = val + return val + + def put(self, key: Hashable, value: Any) -> None: + """ + This method puts the value associated with the key provided in the LRU cache. + key : A hashable object that is mapped to a value in the LRU cache. + value: Any object that is to be associated with the key in the LRU cache. + >>> cache = LRUCache(2) + >>> cache.put(3,3) + >>> cache + {3: 3} + >>> cache.put(2,2) + >>> cache + {3: 3, 2: 2} + """ + # To pop the last value inside of the LRU cache + if key in self: + self.pop(key) + self[key] = value + return + + if self.remaining > 0: + self.remaining -= 1 + # To pop the least recently used item from the dictionary + else: + self.pop(next(iter(self))) + self[key] = value + + +def main() -> None: + """Example test case with LRU_Cache of size 2 + >>> main() + 1 + Key=2 not found in cache + Key=1 not found in cache + 3 + 4 + """ + cache = LRUCache(2) # Creates an LRU cache with size 2 + cache.put(1, 1) # cache = {1:1} + cache.put(2, 2) # cache = {1:1, 2:2} + try: + print(cache.get(1)) # Prints 1 + except KeyError: + print("Key not found in cache") + cache.put( + 3, 3 + ) # cache = {1:1, 3:3} key=2 is evicted because it wasn't used recently + try: + print(cache.get(2)) + except KeyError: + print("Key=2 not found in cache") # Prints key not found + cache.put( + 4, 4 + ) # cache = {4:4, 3:3} key=1 is evicted because it wasn't used recently + try: + print(cache.get(1)) + except KeyError: + print("Key=1 not found in cache") # Prints key not found + try: + print(cache.get(3)) # Prints value 3 + except KeyError: + print("Key not found in cache") + + try: + print(cache.get(4)) # Prints value 4 + except KeyError: + print("Key not found in cache") + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + main() From 8b831cb60003443c9967ac8a33df4151dc883484 Mon Sep 17 00:00:00 2001 From: Bazif Rasool <45148731+Bazifrasool@users.noreply.github.com> Date: Sun, 30 Jul 2023 20:30:58 +0530 Subject: [PATCH 129/757] Added Altitude Pressure equation (#8909) * Added Altitude Pressure equation * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Removed trailing whitespaces * Removed pylint * Fix lru_cache_pythonic.py * Fixed spellings * Fix again lru_cache_pythonic.py * Update .vscode/settings.json Co-authored-by: Christian Clauss * Third fix lru_cache_pythonic.py * Update .vscode/settings.json Co-authored-by: Christian Clauss * 4th fix lru_cache_pythonic.py * Update physics/altitude_pressure.py Co-authored-by: Christian Clauss * lru_cache_pythonic.py: def get(self, key: Any, /) -> Any | None: * Delete lru_cache_pythonic.py * Added positive and negative pressure test cases * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- other/lru_cache_pythonic.py | 113 ----------------------------------- physics/altitude_pressure.py | 52 ++++++++++++++++ 2 files changed, 52 insertions(+), 113 deletions(-) delete mode 100644 other/lru_cache_pythonic.py create mode 100644 physics/altitude_pressure.py diff --git a/other/lru_cache_pythonic.py b/other/lru_cache_pythonic.py deleted file mode 100644 index 425691ef18cf..000000000000 --- a/other/lru_cache_pythonic.py +++ /dev/null @@ -1,113 +0,0 @@ -""" -This implementation of LRU Cache uses the in-built Python dictionary (dict) which from -Python 3.6 onward maintain the insertion order of keys and ensures O(1) operations on -insert, delete and access. https://docs.python.org/3/library/stdtypes.html#typesmapping -""" -from typing import Any, Hashable - - -class LRUCache(dict): - def __init__(self, capacity: int) -> None: - """ - Initialize an LRU Cache with given capacity. - capacity : int -> the capacity of the LRU Cache - >>> cache = LRUCache(2) - >>> cache - {} - """ - self.remaining: int = capacity - - def get(self, key: Hashable) -> Any: - """ - This method returns the value associated with the key. - key : A hashable object that is mapped to a value in the LRU cache. - return -> Any object that has been stored as a value in the LRU cache. - - >>> cache = LRUCache(2) - >>> cache.put(1,1) - >>> cache.get(1) - 1 - >>> cache.get(2) - Traceback (most recent call last): - ... - KeyError: '2 not found.' - """ - if key not in self: - raise KeyError(f"{key} not found.") - val = self.pop(key) # Pop the key-value and re-insert to maintain the order - self[key] = val - return val - - def put(self, key: Hashable, value: Any) -> None: - """ - This method puts the value associated with the key provided in the LRU cache. - key : A hashable object that is mapped to a value in the LRU cache. - value: Any object that is to be associated with the key in the LRU cache. - >>> cache = LRUCache(2) - >>> cache.put(3,3) - >>> cache - {3: 3} - >>> cache.put(2,2) - >>> cache - {3: 3, 2: 2} - """ - # To pop the last value inside of the LRU cache - if key in self: - self.pop(key) - self[key] = value - return - - if self.remaining > 0: - self.remaining -= 1 - # To pop the least recently used item from the dictionary - else: - self.pop(next(iter(self))) - self[key] = value - - -def main() -> None: - """Example test case with LRU_Cache of size 2 - >>> main() - 1 - Key=2 not found in cache - Key=1 not found in cache - 3 - 4 - """ - cache = LRUCache(2) # Creates an LRU cache with size 2 - cache.put(1, 1) # cache = {1:1} - cache.put(2, 2) # cache = {1:1, 2:2} - try: - print(cache.get(1)) # Prints 1 - except KeyError: - print("Key not found in cache") - cache.put( - 3, 3 - ) # cache = {1:1, 3:3} key=2 is evicted because it wasn't used recently - try: - print(cache.get(2)) - except KeyError: - print("Key=2 not found in cache") # Prints key not found - cache.put( - 4, 4 - ) # cache = {4:4, 3:3} key=1 is evicted because it wasn't used recently - try: - print(cache.get(1)) - except KeyError: - print("Key=1 not found in cache") # Prints key not found - try: - print(cache.get(3)) # Prints value 3 - except KeyError: - print("Key not found in cache") - - try: - print(cache.get(4)) # Prints value 4 - except KeyError: - print("Key not found in cache") - - -if __name__ == "__main__": - import doctest - - doctest.testmod() - main() diff --git a/physics/altitude_pressure.py b/physics/altitude_pressure.py new file mode 100644 index 000000000000..65307d223fa7 --- /dev/null +++ b/physics/altitude_pressure.py @@ -0,0 +1,52 @@ +""" +Title : Calculate altitude using Pressure + +Description : + The below algorithm approximates the altitude using Barometric formula + + +""" + + +def get_altitude_at_pressure(pressure: float) -> float: + """ + This method calculates the altitude from Pressure wrt to + Sea level pressure as reference .Pressure is in Pascals + https://en.wikipedia.org/wiki/Pressure_altitude + https://community.bosch-sensortec.com/t5/Question-and-answers/How-to-calculate-the-altitude-from-the-pressure-sensor-data/qaq-p/5702 + + H = 44330 * [1 - (P/p0)^(1/5.255) ] + + Where : + H = altitude (m) + P = measured pressure + p0 = reference pressure at sea level 101325 Pa + + Examples: + >>> get_altitude_at_pressure(pressure=100_000) + 105.47836610778828 + >>> get_altitude_at_pressure(pressure=101_325) + 0.0 + >>> get_altitude_at_pressure(pressure=80_000) + 1855.873388064995 + >>> get_altitude_at_pressure(pressure=201_325) + Traceback (most recent call last): + ... + ValueError: Value Higher than Pressure at Sea Level ! + >>> get_altitude_at_pressure(pressure=-80_000) + Traceback (most recent call last): + ... + ValueError: Atmospheric Pressure can not be negative ! + """ + + if pressure > 101325: + raise ValueError("Value Higher than Pressure at Sea Level !") + if pressure < 0: + raise ValueError("Atmospheric Pressure can not be negative !") + return 44_330 * (1 - (pressure / 101_325) ** (1 / 5.5255)) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From d4f2873e39f041513aa9f5c287ec9b46e2236dad Mon Sep 17 00:00:00 2001 From: AmirSoroush Date: Mon, 31 Jul 2023 03:54:15 +0300 Subject: [PATCH 130/757] add reverse_inorder traversal to binary_tree_traversals.py (#8726) * add reverse_inorder traversal to binary_tree_traversals.py * Apply suggestions from code review Co-authored-by: Tianyi Zheng --------- Co-authored-by: Tianyi Zheng --- .../binary_tree/binary_tree_traversals.py | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/data_structures/binary_tree/binary_tree_traversals.py b/data_structures/binary_tree/binary_tree_traversals.py index 71a895e76ce4..2afb7604f9c6 100644 --- a/data_structures/binary_tree/binary_tree_traversals.py +++ b/data_structures/binary_tree/binary_tree_traversals.py @@ -58,6 +58,19 @@ def inorder(root: Node | None) -> list[int]: return [*inorder(root.left), root.data, *inorder(root.right)] if root else [] +def reverse_inorder(root: Node | None) -> list[int]: + """ + Reverse in-order traversal visits right subtree, root node, left subtree. + >>> reverse_inorder(make_tree()) + [3, 1, 5, 2, 4] + """ + return ( + [*reverse_inorder(root.right), root.data, *reverse_inorder(root.left)] + if root + else [] + ) + + def height(root: Node | None) -> int: """ Recursive function for calculating the height of the binary tree. @@ -161,15 +174,12 @@ def zigzag(root: Node | None) -> Sequence[Node | None] | list[Any]: def main() -> None: # Main function for testing. - """ - Create binary tree. - """ + # Create binary tree. root = make_tree() - """ - All Traversals of the binary are as follows: - """ + # All Traversals of the binary are as follows: print(f"In-order Traversal: {inorder(root)}") + print(f"Reverse In-order Traversal: {reverse_inorder(root)}") print(f"Pre-order Traversal: {preorder(root)}") print(f"Post-order Traversal: {postorder(root)}", "\n") From 4710e51deb2dc07e32884391a36d40e08398e6be Mon Sep 17 00:00:00 2001 From: David Leal Date: Sun, 30 Jul 2023 19:15:30 -0600 Subject: [PATCH 131/757] chore: use newest Discord invite link (#8696) * updating DIRECTORY.md * chore: use newest Discord invite link --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index bf6e0ed3cf75..d8eba4e016fa 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Contributions Welcome - + Discord chat @@ -42,7 +42,7 @@ Read through our [Contribution Guidelines](CONTRIBUTING.md) before you contribut ## Community Channels -We are on [Discord](https://discord.gg/c7MnfGFGa6) and [Gitter](https://gitter.im/TheAlgorithms/community)! Community channels are a great way for you to ask questions and get help. Please join us! +We are on [Discord](https://the-algorithms.com/discord) and [Gitter](https://gitter.im/TheAlgorithms/community)! Community channels are a great way for you to ask questions and get help. Please join us! ## List of Algorithms From 8cce9cf066396bb220515c03849fbc1a16d800d0 Mon Sep 17 00:00:00 2001 From: Almas Bekbayev <121730304+bekbayev@users.noreply.github.com> Date: Mon, 31 Jul 2023 07:32:05 +0600 Subject: [PATCH 132/757] Fix linear_search docstring return value (#8644) --- searches/linear_search.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/searches/linear_search.py b/searches/linear_search.py index 777080d14e36..ba6e81d6bae4 100644 --- a/searches/linear_search.py +++ b/searches/linear_search.py @@ -15,7 +15,7 @@ def linear_search(sequence: list, target: int) -> int: :param sequence: a collection with comparable items (as sorted items not required in Linear Search) :param target: item value to search - :return: index of found item or None if item is not found + :return: index of found item or -1 if item is not found Examples: >>> linear_search([0, 5, 7, 10, 15], 0) From 384c407a265ac44d15eecdd339bb154147cda4f8 Mon Sep 17 00:00:00 2001 From: AmirSoroush Date: Mon, 31 Jul 2023 05:07:35 +0300 Subject: [PATCH 133/757] Enhance the implementation of Queue using list (#8608) * enhance the implementation of queue using list * enhance readability of queue_on_list.py * rename 'queue_on_list' to 'queue_by_list' to match the class name --- data_structures/queue/queue_by_list.py | 141 +++++++++++++++++++++++++ data_structures/queue/queue_on_list.py | 52 --------- 2 files changed, 141 insertions(+), 52 deletions(-) create mode 100644 data_structures/queue/queue_by_list.py delete mode 100644 data_structures/queue/queue_on_list.py diff --git a/data_structures/queue/queue_by_list.py b/data_structures/queue/queue_by_list.py new file mode 100644 index 000000000000..4b05be9fd08e --- /dev/null +++ b/data_structures/queue/queue_by_list.py @@ -0,0 +1,141 @@ +"""Queue represented by a Python list""" + +from collections.abc import Iterable +from typing import Generic, TypeVar + +_T = TypeVar("_T") + + +class QueueByList(Generic[_T]): + def __init__(self, iterable: Iterable[_T] | None = None) -> None: + """ + >>> QueueByList() + Queue(()) + >>> QueueByList([10, 20, 30]) + Queue((10, 20, 30)) + >>> QueueByList((i**2 for i in range(1, 4))) + Queue((1, 4, 9)) + """ + self.entries: list[_T] = list(iterable or []) + + def __len__(self) -> int: + """ + >>> len(QueueByList()) + 0 + >>> from string import ascii_lowercase + >>> len(QueueByList(ascii_lowercase)) + 26 + >>> queue = QueueByList() + >>> for i in range(1, 11): + ... queue.put(i) + >>> len(queue) + 10 + >>> for i in range(2): + ... queue.get() + 1 + 2 + >>> len(queue) + 8 + """ + + return len(self.entries) + + def __repr__(self) -> str: + """ + >>> queue = QueueByList() + >>> queue + Queue(()) + >>> str(queue) + 'Queue(())' + >>> queue.put(10) + >>> queue + Queue((10,)) + >>> queue.put(20) + >>> queue.put(30) + >>> queue + Queue((10, 20, 30)) + """ + + return f"Queue({tuple(self.entries)})" + + def put(self, item: _T) -> None: + """Put `item` to the Queue + + >>> queue = QueueByList() + >>> queue.put(10) + >>> queue.put(20) + >>> len(queue) + 2 + >>> queue + Queue((10, 20)) + """ + + self.entries.append(item) + + def get(self) -> _T: + """ + Get `item` from the Queue + + >>> queue = QueueByList((10, 20, 30)) + >>> queue.get() + 10 + >>> queue.put(40) + >>> queue.get() + 20 + >>> queue.get() + 30 + >>> len(queue) + 1 + >>> queue.get() + 40 + >>> queue.get() + Traceback (most recent call last): + ... + IndexError: Queue is empty + """ + + if not self.entries: + raise IndexError("Queue is empty") + return self.entries.pop(0) + + def rotate(self, rotation: int) -> None: + """Rotate the items of the Queue `rotation` times + + >>> queue = QueueByList([10, 20, 30, 40]) + >>> queue + Queue((10, 20, 30, 40)) + >>> queue.rotate(1) + >>> queue + Queue((20, 30, 40, 10)) + >>> queue.rotate(2) + >>> queue + Queue((40, 10, 20, 30)) + """ + + put = self.entries.append + get = self.entries.pop + + for _ in range(rotation): + put(get(0)) + + def get_front(self) -> _T: + """Get the front item from the Queue + + >>> queue = QueueByList((10, 20, 30)) + >>> queue.get_front() + 10 + >>> queue + Queue((10, 20, 30)) + >>> queue.get() + 10 + >>> queue.get_front() + 20 + """ + + return self.entries[0] + + +if __name__ == "__main__": + from doctest import testmod + + testmod() diff --git a/data_structures/queue/queue_on_list.py b/data_structures/queue/queue_on_list.py deleted file mode 100644 index 71fca6b2f5f4..000000000000 --- a/data_structures/queue/queue_on_list.py +++ /dev/null @@ -1,52 +0,0 @@ -"""Queue represented by a Python list""" - - -class Queue: - def __init__(self): - self.entries = [] - self.length = 0 - self.front = 0 - - def __str__(self): - printed = "<" + str(self.entries)[1:-1] + ">" - return printed - - """Enqueues {@code item} - @param item - item to enqueue""" - - def put(self, item): - self.entries.append(item) - self.length = self.length + 1 - - """Dequeues {@code item} - @requirement: |self.length| > 0 - @return dequeued - item that was dequeued""" - - def get(self): - self.length = self.length - 1 - dequeued = self.entries[self.front] - # self.front-=1 - # self.entries = self.entries[self.front:] - self.entries = self.entries[1:] - return dequeued - - """Rotates the queue {@code rotation} times - @param rotation - number of times to rotate queue""" - - def rotate(self, rotation): - for _ in range(rotation): - self.put(self.get()) - - """Enqueues {@code item} - @return item at front of self.entries""" - - def get_front(self): - return self.entries[0] - - """Returns the length of this.entries""" - - def size(self): - return self.length From 629eb86ce0d30dd6031fa482f4a477ac3df345ab Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Sun, 30 Jul 2023 22:23:23 -0700 Subject: [PATCH 134/757] Fix merge conflicts to merge change from #5080 (#8911) * Input for user choose his Collatz sequence Now the user can tell the algorithm what number he wants to run on the Collatz Sequence. * updating DIRECTORY.md --------- Co-authored-by: Hugo Folloni Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 1 + maths/collatz_sequence.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index af150b12984b..aa9bd313b898 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -740,6 +740,7 @@ * [Tower Of Hanoi](other/tower_of_hanoi.py) ## Physics + * [Altitude Pressure](physics/altitude_pressure.py) * [Archimedes Principle](physics/archimedes_principle.py) * [Basic Orbital Capture](physics/basic_orbital_capture.py) * [Casimir Effect](physics/casimir_effect.py) diff --git a/maths/collatz_sequence.py b/maths/collatz_sequence.py index 4f3aa5582731..b47017146a1e 100644 --- a/maths/collatz_sequence.py +++ b/maths/collatz_sequence.py @@ -57,7 +57,7 @@ def collatz_sequence(n: int) -> Generator[int, None, None]: def main(): - n = 43 + n = int(input("Your number: ")) sequence = tuple(collatz_sequence(n)) print(sequence) print(f"Collatz sequence from {n} took {len(sequence)} steps.") From 0b0214c42f563e7af885058c0e3a32d292f7f1da Mon Sep 17 00:00:00 2001 From: roger-sato Date: Tue, 1 Aug 2023 03:46:30 +0900 Subject: [PATCH 135/757] Handle empty input case in Segment Tree build process (#8718) --- data_structures/binary_tree/segment_tree.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/data_structures/binary_tree/segment_tree.py b/data_structures/binary_tree/segment_tree.py index b0580386954a..5f822407d8cb 100644 --- a/data_structures/binary_tree/segment_tree.py +++ b/data_structures/binary_tree/segment_tree.py @@ -7,7 +7,8 @@ def __init__(self, a): self.st = [0] * ( 4 * self.N ) # approximate the overall size of segment tree with array N - self.build(1, 0, self.N - 1) + if self.N: + self.build(1, 0, self.N - 1) def left(self, idx): return idx * 2 From 90a8e6e0d210a5c526c8f485fa825e1649d217e2 Mon Sep 17 00:00:00 2001 From: Dylan Buchi Date: Mon, 31 Jul 2023 15:50:00 -0300 Subject: [PATCH 136/757] Update `sorts/bubble_sort.py` (#5802) * Add missing type annotations in bubble_sort.py * Refactor bubble_sort function --- sorts/bubble_sort.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/sorts/bubble_sort.py b/sorts/bubble_sort.py index aef2da272bd0..7da4362a5b97 100644 --- a/sorts/bubble_sort.py +++ b/sorts/bubble_sort.py @@ -1,4 +1,7 @@ -def bubble_sort(collection): +from typing import Any + + +def bubble_sort(collection: list[Any]) -> list[Any]: """Pure implementation of bubble sort algorithm in Python :param collection: some mutable ordered collection with heterogeneous @@ -28,9 +31,9 @@ def bubble_sort(collection): True """ length = len(collection) - for i in range(length - 1): + for i in reversed(range(length)): swapped = False - for j in range(length - 1 - i): + for j in range(i): if collection[j] > collection[j + 1]: swapped = True collection[j], collection[j + 1] = collection[j + 1], collection[j] From 5cf34d901e32b65425103309bbad0068b1851238 Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Mon, 31 Jul 2023 13:53:26 -0700 Subject: [PATCH 137/757] Ruff fixes (#8913) * updating DIRECTORY.md * Fix ruff error in eulerian_path_and_circuit_for_undirected_graph.py * Fix ruff error in newtons_second_law_of_motion.py --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 2 +- graphs/eulerian_path_and_circuit_for_undirected_graph.py | 2 +- physics/newtons_second_law_of_motion.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index aa9bd313b898..fdcf0ceedf1f 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -236,8 +236,8 @@ * [Double Ended Queue](data_structures/queue/double_ended_queue.py) * [Linked Queue](data_structures/queue/linked_queue.py) * [Priority Queue Using List](data_structures/queue/priority_queue_using_list.py) + * [Queue By List](data_structures/queue/queue_by_list.py) * [Queue By Two Stacks](data_structures/queue/queue_by_two_stacks.py) - * [Queue On List](data_structures/queue/queue_on_list.py) * [Queue On Pseudo Stack](data_structures/queue/queue_on_pseudo_stack.py) * Stacks * [Balanced Parentheses](data_structures/stacks/balanced_parentheses.py) diff --git a/graphs/eulerian_path_and_circuit_for_undirected_graph.py b/graphs/eulerian_path_and_circuit_for_undirected_graph.py index 6c43c5d3e6e3..6b4ea8e21e8b 100644 --- a/graphs/eulerian_path_and_circuit_for_undirected_graph.py +++ b/graphs/eulerian_path_and_circuit_for_undirected_graph.py @@ -20,7 +20,7 @@ def check_circuit_or_path(graph, max_node): odd_degree_nodes = 0 odd_node = -1 for i in range(max_node): - if i not in graph.keys(): + if i not in graph: continue if len(graph[i]) % 2 == 1: odd_degree_nodes += 1 diff --git a/physics/newtons_second_law_of_motion.py b/physics/newtons_second_law_of_motion.py index cb53f8f6571f..53fab6ce78b9 100644 --- a/physics/newtons_second_law_of_motion.py +++ b/physics/newtons_second_law_of_motion.py @@ -60,7 +60,7 @@ def newtons_second_law_of_motion(mass: float, acceleration: float) -> float: >>> newtons_second_law_of_motion(2.0, 1) 2.0 """ - force = float() + force = 0.0 try: force = mass * acceleration except Exception: From f8fe72dc378232107100acc1924fef31b1198124 Mon Sep 17 00:00:00 2001 From: "Minha, Jeong" Date: Tue, 1 Aug 2023 06:24:12 +0900 Subject: [PATCH 138/757] Update game_of_life.py (#4921) * Update game_of_life.py docstring error fix delete no reason delete next_gen_canvas code(local variable) * Update cellular_automata/game_of_life.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: Tianyi Zheng Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- cellular_automata/game_of_life.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/cellular_automata/game_of_life.py b/cellular_automata/game_of_life.py index b69afdce03eb..d691a2b73af0 100644 --- a/cellular_automata/game_of_life.py +++ b/cellular_automata/game_of_life.py @@ -10,7 +10,7 @@ - 3.5 Usage: - - $python3 game_o_life + - $python3 game_of_life Game-Of-Life Rules: @@ -52,7 +52,8 @@ def seed(canvas: list[list[bool]]) -> None: def run(canvas: list[list[bool]]) -> list[list[bool]]: - """This function runs the rules of game through all points, and changes their + """ + This function runs the rules of game through all points, and changes their status accordingly.(in the same canvas) @Args: -- @@ -60,7 +61,7 @@ def run(canvas: list[list[bool]]) -> list[list[bool]]: @returns: -- - None + canvas of population after one step """ current_canvas = np.array(canvas) next_gen_canvas = np.array(create_canvas(current_canvas.shape[0])) @@ -70,10 +71,7 @@ def run(canvas: list[list[bool]]) -> list[list[bool]]: pt, current_canvas[r - 1 : r + 2, c - 1 : c + 2] ) - current_canvas = next_gen_canvas - del next_gen_canvas # cleaning memory as we move on. - return_canvas: list[list[bool]] = current_canvas.tolist() - return return_canvas + return next_gen_canvas.tolist() def __judge_point(pt: bool, neighbours: list[list[bool]]) -> bool: From f7c5e55609afa1e4e7ae2ee3f442bbd5d0b43b8a Mon Sep 17 00:00:00 2001 From: Jan Wojciechowski <96974442+yanvoi@users.noreply.github.com> Date: Tue, 1 Aug 2023 05:02:49 +0200 Subject: [PATCH 139/757] Window closing fix (#8625) * The window will now remain open after the fractal is finished being drawn, and will only close upon your click. * Update fractals/sierpinski_triangle.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: Tianyi Zheng Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- fractals/sierpinski_triangle.py | 1 + 1 file changed, 1 insertion(+) diff --git a/fractals/sierpinski_triangle.py b/fractals/sierpinski_triangle.py index c28ec00b27fe..45f7ab84cfff 100644 --- a/fractals/sierpinski_triangle.py +++ b/fractals/sierpinski_triangle.py @@ -82,3 +82,4 @@ def triangle( vertices = [(-175, -125), (0, 175), (175, -125)] # vertices of triangle triangle(vertices[0], vertices[1], vertices[2], int(sys.argv[1])) + turtle.Screen().exitonclick() From c9a7234a954dd280dc8192ae77a564e647d013d4 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 1 Aug 2023 09:26:23 +0530 Subject: [PATCH 140/757] [pre-commit.ci] pre-commit autoupdate (#8914) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.0.280 → v0.0.281](https://github.com/astral-sh/ruff-pre-commit/compare/v0.0.280...v0.0.281) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5adf12cc70c5..e158bd8d6879 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.0.280 + rev: v0.0.281 hooks: - id: ruff From ce218c57f1f494cfca69bc01ba660c97385e5330 Mon Sep 17 00:00:00 2001 From: AmirSoroush Date: Tue, 1 Aug 2023 21:23:34 +0300 Subject: [PATCH 141/757] =?UTF-8?q?fixes=20#8673;=20Add=20operator's=20ass?= =?UTF-8?q?ociativity=20check=20for=20stacks/infix=5Fto=5Fp=E2=80=A6=20(#8?= =?UTF-8?q?674)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fixes #8673; Add operator's associativity check for stacks/infix_to_postfix_conversion.py * fix ruff N806 in stacks/infix_to_postfix_conversion.py * Update data_structures/stacks/infix_to_postfix_conversion.py Co-authored-by: Tianyi Zheng * Update data_structures/stacks/infix_to_postfix_conversion.py Co-authored-by: Tianyi Zheng --------- Co-authored-by: Tianyi Zheng --- .../stacks/infix_to_postfix_conversion.py | 50 +++++++++++++++++-- 1 file changed, 47 insertions(+), 3 deletions(-) diff --git a/data_structures/stacks/infix_to_postfix_conversion.py b/data_structures/stacks/infix_to_postfix_conversion.py index 9017443091cf..e697061937c9 100644 --- a/data_structures/stacks/infix_to_postfix_conversion.py +++ b/data_structures/stacks/infix_to_postfix_conversion.py @@ -4,9 +4,26 @@ https://en.wikipedia.org/wiki/Shunting-yard_algorithm """ +from typing import Literal + from .balanced_parentheses import balanced_parentheses from .stack import Stack +PRECEDENCES: dict[str, int] = { + "+": 1, + "-": 1, + "*": 2, + "/": 2, + "^": 3, +} +ASSOCIATIVITIES: dict[str, Literal["LR", "RL"]] = { + "+": "LR", + "-": "LR", + "*": "LR", + "/": "LR", + "^": "RL", +} + def precedence(char: str) -> int: """ @@ -14,7 +31,15 @@ def precedence(char: str) -> int: order of operation. https://en.wikipedia.org/wiki/Order_of_operations """ - return {"+": 1, "-": 1, "*": 2, "/": 2, "^": 3}.get(char, -1) + return PRECEDENCES.get(char, -1) + + +def associativity(char: str) -> Literal["LR", "RL"]: + """ + Return the associativity of the operator `char`. + https://en.wikipedia.org/wiki/Operator_associativity + """ + return ASSOCIATIVITIES[char] def infix_to_postfix(expression_str: str) -> str: @@ -35,6 +60,8 @@ def infix_to_postfix(expression_str: str) -> str: 'a b c * + d e * f + g * +' >>> infix_to_postfix("x^y/(5*z)+2") 'x y ^ 5 z * / 2 +' + >>> infix_to_postfix("2^3^2") + '2 3 2 ^ ^' """ if not balanced_parentheses(expression_str): raise ValueError("Mismatched parentheses") @@ -50,9 +77,26 @@ def infix_to_postfix(expression_str: str) -> str: postfix.append(stack.pop()) stack.pop() else: - while not stack.is_empty() and precedence(char) <= precedence(stack.peek()): + while True: + if stack.is_empty(): + stack.push(char) + break + + char_precedence = precedence(char) + tos_precedence = precedence(stack.peek()) + + if char_precedence > tos_precedence: + stack.push(char) + break + if char_precedence < tos_precedence: + postfix.append(stack.pop()) + continue + # Precedences are equal + if associativity(char) == "RL": + stack.push(char) + break postfix.append(stack.pop()) - stack.push(char) + while not stack.is_empty(): postfix.append(stack.pop()) return " ".join(postfix) From db6bd4b17f471d4def7aa441f1da43bb6a0f18ae Mon Sep 17 00:00:00 2001 From: Dipankar Mitra <50228537+Mitra-babu@users.noreply.github.com> Date: Mon, 7 Aug 2023 17:17:42 +0530 Subject: [PATCH 142/757] IQR function is added (#8851) * tanh function been added * tanh function been added * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * tanh function is added * tanh function is added * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * tanh function added * tanh function added * tanh function is added * Apply suggestions from code review * ELU activation function is added * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * elu activation is added * ELU activation is added * Update maths/elu_activation.py Co-authored-by: Christian Clauss * Exponential_linear_unit activation is added * Exponential_linear_unit activation is added * SiLU activation is added * SiLU activation is added * mish added * mish activation is added * inter_quartile_range function is added * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Mish activation function is added * Mish action is added * mish activation added * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * mish activation added * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * inter quartile range (IQR) function is added * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * IQR function is added * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * code optimized in IQR function * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * interquartile_range function is added * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update maths/interquartile_range.py Co-authored-by: Christian Clauss * Changes on interquartile_range * numpy removed from interquartile_range * Fixes from code review * Update interquartile_range.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- maths/interquartile_range.py | 66 ++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 maths/interquartile_range.py diff --git a/maths/interquartile_range.py b/maths/interquartile_range.py new file mode 100644 index 000000000000..d4d72e73ef49 --- /dev/null +++ b/maths/interquartile_range.py @@ -0,0 +1,66 @@ +""" +An implementation of interquartile range (IQR) which is a measure of statistical +dispersion, which is the spread of the data. + +The function takes the list of numeric values as input and returns the IQR. + +Script inspired by this Wikipedia article: +https://en.wikipedia.org/wiki/Interquartile_range +""" +from __future__ import annotations + + +def find_median(nums: list[int | float]) -> float: + """ + This is the implementation of the median. + :param nums: The list of numeric nums + :return: Median of the list + >>> find_median(nums=([1, 2, 2, 3, 4])) + 2 + >>> find_median(nums=([1, 2, 2, 3, 4, 4])) + 2.5 + >>> find_median(nums=([-1, 2, 0, 3, 4, -4])) + 1.5 + >>> find_median(nums=([1.1, 2.2, 2, 3.3, 4.4, 4])) + 2.65 + """ + div, mod = divmod(len(nums), 2) + if mod: + return nums[div] + return (nums[div] + nums[(div) - 1]) / 2 + + +def interquartile_range(nums: list[int | float]) -> float: + """ + Return the interquartile range for a list of numeric values. + :param nums: The list of numeric values. + :return: interquartile range + + >>> interquartile_range(nums=[4, 1, 2, 3, 2]) + 2.0 + >>> interquartile_range(nums = [-2, -7, -10, 9, 8, 4, -67, 45]) + 17.0 + >>> interquartile_range(nums = [-2.1, -7.1, -10.1, 9.1, 8.1, 4.1, -67.1, 45.1]) + 17.2 + >>> interquartile_range(nums = [0, 0, 0, 0, 0]) + 0.0 + >>> interquartile_range(nums=[]) + Traceback (most recent call last): + ... + ValueError: The list is empty. Provide a non-empty list. + """ + if not nums: + raise ValueError("The list is empty. Provide a non-empty list.") + nums.sort() + length = len(nums) + div, mod = divmod(length, 2) + q1 = find_median(nums[:div]) + half_length = sum((div, mod)) + q3 = find_median(nums[half_length:length]) + return q3 - q1 + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From ac62cdb94fe2478fd809d9ec91e3b85304a5ac6d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 7 Aug 2023 19:52:39 -0400 Subject: [PATCH 143/757] [pre-commit.ci] pre-commit autoupdate (#8930) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/astral-sh/ruff-pre-commit: v0.0.281 → v0.0.282](https://github.com/astral-sh/ruff-pre-commit/compare/v0.0.281...v0.0.282) * updating DIRECTORY.md --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- DIRECTORY.md | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e158bd8d6879..da6762123b04 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.0.281 + rev: v0.0.282 hooks: - id: ruff diff --git a/DIRECTORY.md b/DIRECTORY.md index fdcf0ceedf1f..e6a1ff356143 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -585,6 +585,7 @@ * [Hardy Ramanujanalgo](maths/hardy_ramanujanalgo.py) * [Hexagonal Number](maths/hexagonal_number.py) * [Integration By Simpson Approx](maths/integration_by_simpson_approx.py) + * [Interquartile Range](maths/interquartile_range.py) * [Is Int Palindrome](maths/is_int_palindrome.py) * [Is Ip V4 Address Valid](maths/is_ip_v4_address_valid.py) * [Is Square Free](maths/is_square_free.py) From 842d03fb2ab7d83e4d4081c248d71e89bb520809 Mon Sep 17 00:00:00 2001 From: AmirSoroush Date: Wed, 9 Aug 2023 00:47:09 +0300 Subject: [PATCH 144/757] improvements to jump_search.py (#8932) * improvements to jump_search.py * add more tests to jump_search.py --- searches/jump_search.py | 45 +++++++++++++++++++++++++++++------------ 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/searches/jump_search.py b/searches/jump_search.py index 31a9656c55fe..3bc3c37809a1 100644 --- a/searches/jump_search.py +++ b/searches/jump_search.py @@ -4,14 +4,28 @@ until the element compared is bigger than the one searched. It will then perform a linear search until it matches the wanted number. If not found, it returns -1. + +https://en.wikipedia.org/wiki/Jump_search """ import math +from collections.abc import Sequence +from typing import Any, Protocol, TypeVar + + +class Comparable(Protocol): + def __lt__(self, other: Any, /) -> bool: + ... + +T = TypeVar("T", bound=Comparable) -def jump_search(arr: list, x: int) -> int: + +def jump_search(arr: Sequence[T], item: T) -> int: """ - Pure Python implementation of the jump search algorithm. + Python implementation of the jump search algorithm. + Return the index if the `item` is found, otherwise return -1. + Examples: >>> jump_search([0, 1, 2, 3, 4, 5], 3) 3 @@ -21,31 +35,36 @@ def jump_search(arr: list, x: int) -> int: -1 >>> jump_search([0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610], 55) 10 + >>> jump_search(["aa", "bb", "cc", "dd", "ee", "ff"], "ee") + 4 """ - n = len(arr) - step = int(math.floor(math.sqrt(n))) + arr_size = len(arr) + block_size = int(math.sqrt(arr_size)) + prev = 0 - while arr[min(step, n) - 1] < x: + step = block_size + while arr[min(step, arr_size) - 1] < item: prev = step - step += int(math.floor(math.sqrt(n))) - if prev >= n: + step += block_size + if prev >= arr_size: return -1 - while arr[prev] < x: - prev = prev + 1 - if prev == min(step, n): + while arr[prev] < item: + prev += 1 + if prev == min(step, arr_size): return -1 - if arr[prev] == x: + if arr[prev] == item: return prev return -1 if __name__ == "__main__": user_input = input("Enter numbers separated by a comma:\n").strip() - arr = [int(item) for item in user_input.split(",")] + array = [int(item) for item in user_input.split(",")] x = int(input("Enter the number to be searched:\n")) - res = jump_search(arr, x) + + res = jump_search(array, x) if res == -1: print("Number not found!") else: From ae0fc85401efd9816193a06e554a66600cc09a97 Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Wed, 9 Aug 2023 00:55:30 -0700 Subject: [PATCH 145/757] Fix ruff errors (#8936) * Fix ruff errors Renamed neural_network/input_data.py to neural_network/input_data.py_tf because it should be left out of the directory for the following reasons: 1. Its sole purpose is to be used by neural_network/gan.py_tf, which is itself left out of the directory because of issues with TensorFlow. 2. It was taken directly from TensorFlow's codebase and is actually already deprecated. If/when neural_network/gan.py_tf is eventually re-added back to the directory, its implementation should be changed to not use neural_network/input_data.py anyway. * updating DIRECTORY.md * Change input_data.py_tf file extension Change input_data.py_tf file extension because algorithms-keeper bot is being picky about it --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 1 - conversions/length_conversion.py | 30 +++++++++----- conversions/pressure_conversions.py | 28 ++++++++----- conversions/volume_conversions.py | 40 +++++++++++-------- .../binary_tree/distribute_coins.py | 10 +++-- electronics/electric_power.py | 22 +++++----- graphs/bi_directional_dijkstra.py | 4 +- maths/area_under_curve.py | 6 +-- maths/decimal_to_fraction.py | 2 +- maths/line_length.py | 6 +-- maths/numerical_integration.py | 6 +-- .../single_indeterminate_operations.py | 4 +- maths/series/geometric_series.py | 10 ++--- maths/series/p_series.py | 2 +- maths/volume.py | 2 +- matrix/matrix_class.py | 4 +- matrix/matrix_operation.py | 6 +-- matrix/searching_in_sorted_matrix.py | 4 +- matrix/sherman_morrison.py | 16 ++++---- ...t_data.py => input_data.py.DEPRECATED.txt} | 0 web_programming/covid_stats_via_xpath.py | 12 ++++-- 21 files changed, 121 insertions(+), 94 deletions(-) rename neural_network/{input_data.py => input_data.py.DEPRECATED.txt} (100%) diff --git a/DIRECTORY.md b/DIRECTORY.md index e6a1ff356143..5578c1c9a6dd 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -710,7 +710,6 @@ * [Exponential Linear Unit](neural_network/activation_functions/exponential_linear_unit.py) * [Back Propagation Neural Network](neural_network/back_propagation_neural_network.py) * [Convolution Neural Network](neural_network/convolution_neural_network.py) - * [Input Data](neural_network/input_data.py) * [Perceptron](neural_network/perceptron.py) * [Simple Neural Network](neural_network/simple_neural_network.py) diff --git a/conversions/length_conversion.py b/conversions/length_conversion.py index d8f39515255e..07fa93a198c7 100644 --- a/conversions/length_conversion.py +++ b/conversions/length_conversion.py @@ -22,9 +22,13 @@ -> Wikipedia reference: https://en.wikipedia.org/wiki/Millimeter """ -from collections import namedtuple +from typing import NamedTuple + + +class FromTo(NamedTuple): + from_factor: float + to_factor: float -from_to = namedtuple("from_to", "from_ to") TYPE_CONVERSION = { "millimeter": "mm", @@ -40,14 +44,14 @@ } METRIC_CONVERSION = { - "mm": from_to(0.001, 1000), - "cm": from_to(0.01, 100), - "m": from_to(1, 1), - "km": from_to(1000, 0.001), - "in": from_to(0.0254, 39.3701), - "ft": from_to(0.3048, 3.28084), - "yd": from_to(0.9144, 1.09361), - "mi": from_to(1609.34, 0.000621371), + "mm": FromTo(0.001, 1000), + "cm": FromTo(0.01, 100), + "m": FromTo(1, 1), + "km": FromTo(1000, 0.001), + "in": FromTo(0.0254, 39.3701), + "ft": FromTo(0.3048, 3.28084), + "yd": FromTo(0.9144, 1.09361), + "mi": FromTo(1609.34, 0.000621371), } @@ -115,7 +119,11 @@ def length_conversion(value: float, from_type: str, to_type: str) -> float: f"Conversion abbreviations are: {', '.join(METRIC_CONVERSION)}" ) raise ValueError(msg) - return value * METRIC_CONVERSION[new_from].from_ * METRIC_CONVERSION[new_to].to + return ( + value + * METRIC_CONVERSION[new_from].from_factor + * METRIC_CONVERSION[new_to].to_factor + ) if __name__ == "__main__": diff --git a/conversions/pressure_conversions.py b/conversions/pressure_conversions.py index e0cd18d234ba..fe78b1382677 100644 --- a/conversions/pressure_conversions.py +++ b/conversions/pressure_conversions.py @@ -19,19 +19,23 @@ -> https://www.unitconverters.net/pressure-converter.html """ -from collections import namedtuple +from typing import NamedTuple + + +class FromTo(NamedTuple): + from_factor: float + to_factor: float -from_to = namedtuple("from_to", "from_ to") PRESSURE_CONVERSION = { - "atm": from_to(1, 1), - "pascal": from_to(0.0000098, 101325), - "bar": from_to(0.986923, 1.01325), - "kilopascal": from_to(0.00986923, 101.325), - "megapascal": from_to(9.86923, 0.101325), - "psi": from_to(0.068046, 14.6959), - "inHg": from_to(0.0334211, 29.9213), - "torr": from_to(0.00131579, 760), + "atm": FromTo(1, 1), + "pascal": FromTo(0.0000098, 101325), + "bar": FromTo(0.986923, 1.01325), + "kilopascal": FromTo(0.00986923, 101.325), + "megapascal": FromTo(9.86923, 0.101325), + "psi": FromTo(0.068046, 14.6959), + "inHg": FromTo(0.0334211, 29.9213), + "torr": FromTo(0.00131579, 760), } @@ -71,7 +75,9 @@ def pressure_conversion(value: float, from_type: str, to_type: str) -> float: + ", ".join(PRESSURE_CONVERSION) ) return ( - value * PRESSURE_CONVERSION[from_type].from_ * PRESSURE_CONVERSION[to_type].to + value + * PRESSURE_CONVERSION[from_type].from_factor + * PRESSURE_CONVERSION[to_type].to_factor ) diff --git a/conversions/volume_conversions.py b/conversions/volume_conversions.py index 44d29009120c..cb240380534b 100644 --- a/conversions/volume_conversions.py +++ b/conversions/volume_conversions.py @@ -18,35 +18,39 @@ -> Wikipedia reference: https://en.wikipedia.org/wiki/Cup_(unit) """ -from collections import namedtuple +from typing import NamedTuple + + +class FromTo(NamedTuple): + from_factor: float + to_factor: float -from_to = namedtuple("from_to", "from_ to") METRIC_CONVERSION = { - "cubicmeter": from_to(1, 1), - "litre": from_to(0.001, 1000), - "kilolitre": from_to(1, 1), - "gallon": from_to(0.00454, 264.172), - "cubicyard": from_to(0.76455, 1.30795), - "cubicfoot": from_to(0.028, 35.3147), - "cup": from_to(0.000236588, 4226.75), + "cubic meter": FromTo(1, 1), + "litre": FromTo(0.001, 1000), + "kilolitre": FromTo(1, 1), + "gallon": FromTo(0.00454, 264.172), + "cubic yard": FromTo(0.76455, 1.30795), + "cubic foot": FromTo(0.028, 35.3147), + "cup": FromTo(0.000236588, 4226.75), } def volume_conversion(value: float, from_type: str, to_type: str) -> float: """ Conversion between volume units. - >>> volume_conversion(4, "cubicmeter", "litre") + >>> volume_conversion(4, "cubic meter", "litre") 4000 >>> volume_conversion(1, "litre", "gallon") 0.264172 - >>> volume_conversion(1, "kilolitre", "cubicmeter") + >>> volume_conversion(1, "kilolitre", "cubic meter") 1 - >>> volume_conversion(3, "gallon", "cubicyard") + >>> volume_conversion(3, "gallon", "cubic yard") 0.017814279 - >>> volume_conversion(2, "cubicyard", "litre") + >>> volume_conversion(2, "cubic yard", "litre") 1529.1 - >>> volume_conversion(4, "cubicfoot", "cup") + >>> volume_conversion(4, "cubic foot", "cup") 473.396 >>> volume_conversion(1, "cup", "kilolitre") 0.000236588 @@ -54,7 +58,7 @@ def volume_conversion(value: float, from_type: str, to_type: str) -> float: Traceback (most recent call last): ... ValueError: Invalid 'from_type' value: 'wrongUnit' Supported values are: - cubicmeter, litre, kilolitre, gallon, cubicyard, cubicfoot, cup + cubic meter, litre, kilolitre, gallon, cubic yard, cubic foot, cup """ if from_type not in METRIC_CONVERSION: raise ValueError( @@ -66,7 +70,11 @@ def volume_conversion(value: float, from_type: str, to_type: str) -> float: f"Invalid 'to_type' value: {to_type!r}. Supported values are:\n" + ", ".join(METRIC_CONVERSION) ) - return value * METRIC_CONVERSION[from_type].from_ * METRIC_CONVERSION[to_type].to + return ( + value + * METRIC_CONVERSION[from_type].from_factor + * METRIC_CONVERSION[to_type].to_factor + ) if __name__ == "__main__": diff --git a/data_structures/binary_tree/distribute_coins.py b/data_structures/binary_tree/distribute_coins.py index ea02afc2cea6..5712604cb87c 100644 --- a/data_structures/binary_tree/distribute_coins.py +++ b/data_structures/binary_tree/distribute_coins.py @@ -39,8 +39,8 @@ from __future__ import annotations -from collections import namedtuple from dataclasses import dataclass +from typing import NamedTuple @dataclass @@ -50,7 +50,9 @@ class TreeNode: right: TreeNode | None = None -CoinsDistribResult = namedtuple("CoinsDistribResult", "moves excess") +class CoinsDistribResult(NamedTuple): + moves: int + excess: int def distribute_coins(root: TreeNode | None) -> int: @@ -79,7 +81,7 @@ def distribute_coins(root: TreeNode | None) -> int: # Validation def count_nodes(node: TreeNode | None) -> int: """ - >>> count_nodes(None): + >>> count_nodes(None) 0 """ if node is None: @@ -89,7 +91,7 @@ def count_nodes(node: TreeNode | None) -> int: def count_coins(node: TreeNode | None) -> int: """ - >>> count_coins(None): + >>> count_coins(None) 0 """ if node is None: diff --git a/electronics/electric_power.py b/electronics/electric_power.py index e59795601791..8b92e320ace3 100644 --- a/electronics/electric_power.py +++ b/electronics/electric_power.py @@ -1,7 +1,12 @@ # https://en.m.wikipedia.org/wiki/Electric_power from __future__ import annotations -from collections import namedtuple +from typing import NamedTuple + + +class Result(NamedTuple): + name: str + value: float def electric_power(voltage: float, current: float, power: float) -> tuple: @@ -10,11 +15,11 @@ def electric_power(voltage: float, current: float, power: float) -> tuple: fundamental value of electrical system. examples are below: >>> electric_power(voltage=0, current=2, power=5) - result(name='voltage', value=2.5) + Result(name='voltage', value=2.5) >>> electric_power(voltage=2, current=2, power=0) - result(name='power', value=4.0) + Result(name='power', value=4.0) >>> electric_power(voltage=-2, current=3, power=0) - result(name='power', value=6.0) + Result(name='power', value=6.0) >>> electric_power(voltage=2, current=4, power=2) Traceback (most recent call last): ... @@ -28,9 +33,8 @@ def electric_power(voltage: float, current: float, power: float) -> tuple: ... ValueError: Power cannot be negative in any electrical/electronics system >>> electric_power(voltage=2.2, current=2.2, power=0) - result(name='power', value=4.84) + Result(name='power', value=4.84) """ - result = namedtuple("result", "name value") if (voltage, current, power).count(0) != 1: raise ValueError("Only one argument must be 0") elif power < 0: @@ -38,11 +42,11 @@ def electric_power(voltage: float, current: float, power: float) -> tuple: "Power cannot be negative in any electrical/electronics system" ) elif voltage == 0: - return result("voltage", power / current) + return Result("voltage", power / current) elif current == 0: - return result("current", power / voltage) + return Result("current", power / voltage) elif power == 0: - return result("power", float(round(abs(voltage * current), 2))) + return Result("power", float(round(abs(voltage * current), 2))) else: raise ValueError("Exactly one argument must be 0") diff --git a/graphs/bi_directional_dijkstra.py b/graphs/bi_directional_dijkstra.py index a4489026be80..529a235db625 100644 --- a/graphs/bi_directional_dijkstra.py +++ b/graphs/bi_directional_dijkstra.py @@ -26,8 +26,8 @@ def pass_and_relaxation( cst_bwd: dict, queue: PriorityQueue, parent: dict, - shortest_distance: float | int, -) -> float | int: + shortest_distance: float, +) -> float: for nxt, d in graph[v]: if nxt in visited_forward: continue diff --git a/maths/area_under_curve.py b/maths/area_under_curve.py index b557b2029657..0da6546b2e36 100644 --- a/maths/area_under_curve.py +++ b/maths/area_under_curve.py @@ -7,9 +7,9 @@ def trapezoidal_area( - fnc: Callable[[int | float], int | float], - x_start: int | float, - x_end: int | float, + fnc: Callable[[float], float], + x_start: float, + x_end: float, steps: int = 100, ) -> float: """ diff --git a/maths/decimal_to_fraction.py b/maths/decimal_to_fraction.py index 9462bafe0171..2aa8e3c3dfd6 100644 --- a/maths/decimal_to_fraction.py +++ b/maths/decimal_to_fraction.py @@ -1,4 +1,4 @@ -def decimal_to_fraction(decimal: int | float | str) -> tuple[int, int]: +def decimal_to_fraction(decimal: float | str) -> tuple[int, int]: """ Return a decimal number in its simplest fraction form >>> decimal_to_fraction(2) diff --git a/maths/line_length.py b/maths/line_length.py index b810f2d9ad1f..ed2efc31e96e 100644 --- a/maths/line_length.py +++ b/maths/line_length.py @@ -5,9 +5,9 @@ def line_length( - fnc: Callable[[int | float], int | float], - x_start: int | float, - x_end: int | float, + fnc: Callable[[float], float], + x_start: float, + x_end: float, steps: int = 100, ) -> float: """ diff --git a/maths/numerical_integration.py b/maths/numerical_integration.py index f2d65f89e390..4ac562644a07 100644 --- a/maths/numerical_integration.py +++ b/maths/numerical_integration.py @@ -7,9 +7,9 @@ def trapezoidal_area( - fnc: Callable[[int | float], int | float], - x_start: int | float, - x_end: int | float, + fnc: Callable[[float], float], + x_start: float, + x_end: float, steps: int = 100, ) -> float: """ diff --git a/maths/polynomials/single_indeterminate_operations.py b/maths/polynomials/single_indeterminate_operations.py index 8bafdb591793..e31e6caa3988 100644 --- a/maths/polynomials/single_indeterminate_operations.py +++ b/maths/polynomials/single_indeterminate_operations.py @@ -87,7 +87,7 @@ def __mul__(self, polynomial_2: Polynomial) -> Polynomial: return Polynomial(self.degree + polynomial_2.degree, coefficients) - def evaluate(self, substitution: int | float) -> int | float: + def evaluate(self, substitution: float) -> float: """ Evaluates the polynomial at x. >>> p = Polynomial(2, [1, 2, 3]) @@ -144,7 +144,7 @@ def derivative(self) -> Polynomial: coefficients[i] = self.coefficients[i + 1] * (i + 1) return Polynomial(self.degree - 1, coefficients) - def integral(self, constant: int | float = 0) -> Polynomial: + def integral(self, constant: float = 0) -> Polynomial: """ Returns the integral of the polynomial. >>> p = Polynomial(2, [1, 2, 3]) diff --git a/maths/series/geometric_series.py b/maths/series/geometric_series.py index 90c9fe77b733..b8d6a86206be 100644 --- a/maths/series/geometric_series.py +++ b/maths/series/geometric_series.py @@ -14,10 +14,10 @@ def geometric_series( - nth_term: float | int, - start_term_a: float | int, - common_ratio_r: float | int, -) -> list[float | int]: + nth_term: float, + start_term_a: float, + common_ratio_r: float, +) -> list[float]: """ Pure Python implementation of Geometric Series algorithm @@ -48,7 +48,7 @@ def geometric_series( """ if not all((nth_term, start_term_a, common_ratio_r)): return [] - series: list[float | int] = [] + series: list[float] = [] power = 1 multiple = common_ratio_r for _ in range(int(nth_term)): diff --git a/maths/series/p_series.py b/maths/series/p_series.py index 34fa3f2399af..a091a6f3fecf 100644 --- a/maths/series/p_series.py +++ b/maths/series/p_series.py @@ -13,7 +13,7 @@ from __future__ import annotations -def p_series(nth_term: int | float | str, power: int | float | str) -> list[str]: +def p_series(nth_term: float | str, power: float | str) -> list[str]: """ Pure Python implementation of P-Series algorithm :return: The P-Series starting from 1 to last (nth) term diff --git a/maths/volume.py b/maths/volume.py index 1da4584c893e..721974e68b66 100644 --- a/maths/volume.py +++ b/maths/volume.py @@ -8,7 +8,7 @@ from math import pi, pow -def vol_cube(side_length: int | float) -> float: +def vol_cube(side_length: float) -> float: """ Calculate the Volume of a Cube. >>> vol_cube(1) diff --git a/matrix/matrix_class.py b/matrix/matrix_class.py index a73e8b92a286..a5940a38e836 100644 --- a/matrix/matrix_class.py +++ b/matrix/matrix_class.py @@ -141,7 +141,7 @@ def num_columns(self) -> int: @property def order(self) -> tuple[int, int]: - return (self.num_rows, self.num_columns) + return self.num_rows, self.num_columns @property def is_square(self) -> bool: @@ -315,7 +315,7 @@ def __sub__(self, other: Matrix) -> Matrix: ] ) - def __mul__(self, other: Matrix | int | float) -> Matrix: + def __mul__(self, other: Matrix | float) -> Matrix: if isinstance(other, (int, float)): return Matrix( [[int(element * other) for element in row] for row in self.rows] diff --git a/matrix/matrix_operation.py b/matrix/matrix_operation.py index f189f1898d33..d63e758f1838 100644 --- a/matrix/matrix_operation.py +++ b/matrix/matrix_operation.py @@ -47,7 +47,7 @@ def subtract(matrix_a: list[list[int]], matrix_b: list[list[int]]) -> list[list[ raise TypeError("Expected a matrix, got int/list instead") -def scalar_multiply(matrix: list[list[int]], n: int | float) -> list[list[float]]: +def scalar_multiply(matrix: list[list[int]], n: float) -> list[list[float]]: """ >>> scalar_multiply([[1,2],[3,4]],5) [[5, 10], [15, 20]] @@ -189,9 +189,7 @@ def main() -> None: matrix_c = [[11, 12, 13, 14], [21, 22, 23, 24], [31, 32, 33, 34], [41, 42, 43, 44]] matrix_d = [[3, 0, 2], [2, 0, -2], [0, 1, 1]] print(f"Add Operation, {add(matrix_a, matrix_b) = } \n") - print( - f"Multiply Operation, {multiply(matrix_a, matrix_b) = } \n", - ) + print(f"Multiply Operation, {multiply(matrix_a, matrix_b) = } \n") print(f"Identity: {identity(5)}\n") print(f"Minor of {matrix_c} = {minor(matrix_c, 1, 2)} \n") print(f"Determinant of {matrix_b} = {determinant(matrix_b)} \n") diff --git a/matrix/searching_in_sorted_matrix.py b/matrix/searching_in_sorted_matrix.py index ddca3b1ce781..f55cc71d6f3a 100644 --- a/matrix/searching_in_sorted_matrix.py +++ b/matrix/searching_in_sorted_matrix.py @@ -1,9 +1,7 @@ from __future__ import annotations -def search_in_a_sorted_matrix( - mat: list[list[int]], m: int, n: int, key: int | float -) -> None: +def search_in_a_sorted_matrix(mat: list[list[int]], m: int, n: int, key: float) -> None: """ >>> search_in_a_sorted_matrix( ... [[2, 5, 7], [4, 8, 13], [9, 11, 15], [12, 17, 20]], 3, 3, 5) diff --git a/matrix/sherman_morrison.py b/matrix/sherman_morrison.py index 256271e8a87d..b6e50f70fdcf 100644 --- a/matrix/sherman_morrison.py +++ b/matrix/sherman_morrison.py @@ -22,7 +22,7 @@ def __init__(self, row: int, column: int, default_value: float = 0) -> None: """ self.row, self.column = row, column - self.array = [[default_value for c in range(column)] for r in range(row)] + self.array = [[default_value for _ in range(column)] for _ in range(row)] def __str__(self) -> str: """ @@ -54,15 +54,15 @@ def single_line(row_vector: list[float]) -> str: def __repr__(self) -> str: return str(self) - def validate_indicies(self, loc: tuple[int, int]) -> bool: + def validate_indices(self, loc: tuple[int, int]) -> bool: """ Check if given indices are valid to pick element from matrix. Example: >>> a = Matrix(2, 6, 0) - >>> a.validate_indicies((2, 7)) + >>> a.validate_indices((2, 7)) False - >>> a.validate_indicies((0, 0)) + >>> a.validate_indices((0, 0)) True """ if not (isinstance(loc, (list, tuple)) and len(loc) == 2): @@ -81,7 +81,7 @@ def __getitem__(self, loc: tuple[int, int]) -> Any: >>> a[1, 0] 7 """ - assert self.validate_indicies(loc) + assert self.validate_indices(loc) return self.array[loc[0]][loc[1]] def __setitem__(self, loc: tuple[int, int], value: float) -> None: @@ -96,7 +96,7 @@ def __setitem__(self, loc: tuple[int, int], value: float) -> None: [ 1, 1, 1] [ 1, 1, 51] """ - assert self.validate_indicies(loc) + assert self.validate_indices(loc) self.array[loc[0]][loc[1]] = value def __add__(self, another: Matrix) -> Matrix: @@ -145,7 +145,7 @@ def __neg__(self) -> Matrix: def __sub__(self, another: Matrix) -> Matrix: return self + (-another) - def __mul__(self, another: int | float | Matrix) -> Matrix: + def __mul__(self, another: float | Matrix) -> Matrix: """ Return self * another. @@ -233,7 +233,7 @@ def sherman_morrison(self, u: Matrix, v: Matrix) -> Any: v_t = v.transpose() numerator_factor = (v_t * self * u)[0, 0] + 1 if numerator_factor == 0: - return None # It's not invertable + return None # It's not invertible return self - ((self * u) * (v_t * self) * (1.0 / numerator_factor)) diff --git a/neural_network/input_data.py b/neural_network/input_data.py.DEPRECATED.txt similarity index 100% rename from neural_network/input_data.py rename to neural_network/input_data.py.DEPRECATED.txt diff --git a/web_programming/covid_stats_via_xpath.py b/web_programming/covid_stats_via_xpath.py index 85ea5d940d85..a95130badad9 100644 --- a/web_programming/covid_stats_via_xpath.py +++ b/web_programming/covid_stats_via_xpath.py @@ -4,17 +4,21 @@ more convenient to use in Python web projects (e.g. Django or Flask-based) """ -from collections import namedtuple +from typing import NamedTuple import requests from lxml import html # type: ignore -covid_data = namedtuple("covid_data", "cases deaths recovered") +class CovidData(NamedTuple): + cases: int + deaths: int + recovered: int -def covid_stats(url: str = "https://www.worldometers.info/coronavirus/") -> covid_data: + +def covid_stats(url: str = "https://www.worldometers.info/coronavirus/") -> CovidData: xpath_str = '//div[@class = "maincounter-number"]/span/text()' - return covid_data(*html.fromstring(requests.get(url).content).xpath(xpath_str)) + return CovidData(*html.fromstring(requests.get(url).content).xpath(xpath_str)) fmt = """Total COVID-19 cases in the world: {} From c39b7eadbd4d81dda5e7ffe4c169d670483f0113 Mon Sep 17 00:00:00 2001 From: Suman <66205793+Suman2023@users.noreply.github.com> Date: Sun, 13 Aug 2023 03:28:37 +0530 Subject: [PATCH 146/757] updated the URL and HTML tags for scrapping yahoo finance (#8942) * updated the url and tags for yahoo finance * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * updated to return the error text --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- web_programming/current_stock_price.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/web_programming/current_stock_price.py b/web_programming/current_stock_price.py index df44da4ef351..0c06354d8998 100644 --- a/web_programming/current_stock_price.py +++ b/web_programming/current_stock_price.py @@ -3,12 +3,18 @@ def stock_price(symbol: str = "AAPL") -> str: - url = f"https://in.finance.yahoo.com/quote/{symbol}?s={symbol}" - soup = BeautifulSoup(requests.get(url).text, "html.parser") - class_ = "My(6px) Pos(r) smartphone_Mt(6px)" - return soup.find("div", class_=class_).find("span").text + url = f"https://finance.yahoo.com/quote/{symbol}?p={symbol}" + yahoo_finance_source = requests.get(url, headers={"USER-AGENT": "Mozilla/5.0"}).text + soup = BeautifulSoup(yahoo_finance_source, "html.parser") + specific_fin_streamer_tag = soup.find("fin-streamer", {"data-test": "qsp-price"}) + if specific_fin_streamer_tag: + text = specific_fin_streamer_tag.get_text() + return text + return "No tag with the specified data-test attribute found." + +# Search for the symbol at https://finance.yahoo.com/lookup if __name__ == "__main__": for symbol in "AAPL AMZN IBM GOOG MSFT ORCL".split(): print(f"Current {symbol:<4} stock price is {stock_price(symbol):>8}") From 4f2a346c277076ce1d69578ef52a9766e5040176 Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Sun, 13 Aug 2023 13:05:42 +0300 Subject: [PATCH 147/757] Reduce the complexity of linear_algebra/src/polynom_for_points.py (#8605) * Reduce the complexity of linear_algebra/src/polynom_for_points.py * updating DIRECTORY.md * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix * Fix review issues --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- linear_algebra/src/polynom_for_points.py | 57 ++++++++---------------- 1 file changed, 19 insertions(+), 38 deletions(-) diff --git a/linear_algebra/src/polynom_for_points.py b/linear_algebra/src/polynom_for_points.py index f5e3db0cbb13..a9a9a8117c18 100644 --- a/linear_algebra/src/polynom_for_points.py +++ b/linear_algebra/src/polynom_for_points.py @@ -43,62 +43,43 @@ def points_to_polynomial(coordinates: list[list[int]]) -> str: x = len(coordinates) - count_of_line = 0 - matrix: list[list[float]] = [] # put the x and x to the power values in a matrix - while count_of_line < x: - count_in_line = 0 - a = coordinates[count_of_line][0] - count_line: list[float] = [] - while count_in_line < x: - count_line.append(a ** (x - (count_in_line + 1))) - count_in_line += 1 - matrix.append(count_line) - count_of_line += 1 + matrix: list[list[float]] = [ + [ + coordinates[count_of_line][0] ** (x - (count_in_line + 1)) + for count_in_line in range(x) + ] + for count_of_line in range(x) + ] - count_of_line = 0 # put the y values into a vector - vector: list[float] = [] - while count_of_line < x: - vector.append(coordinates[count_of_line][1]) - count_of_line += 1 + vector: list[float] = [coordinates[count_of_line][1] for count_of_line in range(x)] - count = 0 - - while count < x: - zahlen = 0 - while zahlen < x: - if count == zahlen: - zahlen += 1 - if zahlen == x: - break - bruch = matrix[zahlen][count] / matrix[count][count] + for count in range(x): + for number in range(x): + if count == number: + continue + fraction = matrix[number][count] / matrix[count][count] for counting_columns, item in enumerate(matrix[count]): # manipulating all the values in the matrix - matrix[zahlen][counting_columns] -= item * bruch + matrix[number][counting_columns] -= item * fraction # manipulating the values in the vector - vector[zahlen] -= vector[count] * bruch - zahlen += 1 - count += 1 + vector[number] -= vector[count] * fraction - count = 0 # make solutions - solution: list[str] = [] - while count < x: - solution.append(str(vector[count] / matrix[count][count])) - count += 1 + solution: list[str] = [ + str(vector[count] / matrix[count][count]) for count in range(x) + ] - count = 0 solved = "f(x)=" - while count < x: + for count in range(x): remove_e: list[str] = solution[count].split("E") if len(remove_e) > 1: solution[count] = f"{remove_e[0]}*10^{remove_e[1]}" solved += f"x^{x - (count + 1)}*{solution[count]}" if count + 1 != x: solved += "+" - count += 1 return solved From 9d86d4edaa754af06e0da9cac4a717f3765db7f4 Mon Sep 17 00:00:00 2001 From: Caeden Perelli-Harris Date: Mon, 14 Aug 2023 01:58:17 +0100 Subject: [PATCH 148/757] Create wa-tor algorithm (#8899) * feat(cellular_automata): Create wa-tor algorithm * updating DIRECTORY.md * chore(quality): Implement algo-keeper bot changes * Update cellular_automata/wa_tor.py Co-authored-by: Christian Clauss * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * refactor(repr): Return repr as python object * Update cellular_automata/wa_tor.py Co-authored-by: Tianyi Zheng * Update cellular_automata/wa_tor.py Co-authored-by: Tianyi Zheng * Update cellular_automata/wa_tor.py Co-authored-by: Tianyi Zheng * Update cellular_automata/wa_tor.py Co-authored-by: Tianyi Zheng * Update cellular_automata/wa_tor.py Co-authored-by: Tianyi Zheng * Update cellular_automata/wa_tor.py Co-authored-by: Tianyi Zheng * Update cellular_automata/wa_tor.py Co-authored-by: Tianyi Zheng * Update cellular_automata/wa_tor.py Co-authored-by: Tianyi Zheng * Update cellular_automata/wa_tor.py Co-authored-by: Tianyi Zheng * Update cellular_automata/wa_tor.py Co-authored-by: Tianyi Zheng * Update cellular_automata/wa_tor.py Co-authored-by: Tianyi Zheng * refactor(display): Rename to display_visually to visualise * refactor(wa-tor): Use double for loop * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * chore(wa-tor): Implement suggestions from code review --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: Christian Clauss Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Tianyi Zheng --- DIRECTORY.md | 1 + cellular_automata/wa_tor.py | 550 ++++++++++++++++++++++++++++++++++++ 2 files changed, 551 insertions(+) create mode 100644 cellular_automata/wa_tor.py diff --git a/DIRECTORY.md b/DIRECTORY.md index 5578c1c9a6dd..cdcd1a8ae8cc 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -74,6 +74,7 @@ * [Game Of Life](cellular_automata/game_of_life.py) * [Nagel Schrekenberg](cellular_automata/nagel_schrekenberg.py) * [One Dimensional](cellular_automata/one_dimensional.py) + * [Wa Tor](cellular_automata/wa_tor.py) ## Ciphers * [A1Z26](ciphers/a1z26.py) diff --git a/cellular_automata/wa_tor.py b/cellular_automata/wa_tor.py new file mode 100644 index 000000000000..e423d1595bdb --- /dev/null +++ b/cellular_automata/wa_tor.py @@ -0,0 +1,550 @@ +""" +Wa-Tor algorithm (1984) + +@ https://en.wikipedia.org/wiki/Wa-Tor +@ https://beltoforion.de/en/wator/ +@ https://beltoforion.de/en/wator/images/wator_medium.webm + +This solution aims to completely remove any systematic approach +to the Wa-Tor planet, and utilise fully random methods. + +The constants are a working set that allows the Wa-Tor planet +to result in one of the three possible results. +""" + +from collections.abc import Callable +from random import randint, shuffle +from time import sleep +from typing import Literal + +WIDTH = 50 # Width of the Wa-Tor planet +HEIGHT = 50 # Height of the Wa-Tor planet + +PREY_INITIAL_COUNT = 30 # The initial number of prey entities +PREY_REPRODUCTION_TIME = 5 # The chronons before reproducing + +PREDATOR_INITIAL_COUNT = 50 # The initial number of predator entities +# The initial energy value of predator entities +PREDATOR_INITIAL_ENERGY_VALUE = 15 +# The energy value provided when consuming prey +PREDATOR_FOOD_VALUE = 5 +PREDATOR_REPRODUCTION_TIME = 20 # The chronons before reproducing + +MAX_ENTITIES = 500 # The max number of organisms on the board +# The number of entities to delete from the unbalanced side +DELETE_UNBALANCED_ENTITIES = 50 + + +class Entity: + """ + Represents an entity (either prey or predator). + + >>> e = Entity(True, coords=(0, 0)) + >>> e.prey + True + >>> e.coords + (0, 0) + >>> e.alive + True + """ + + def __init__(self, prey: bool, coords: tuple[int, int]) -> None: + self.prey = prey + # The (row, col) pos of the entity + self.coords = coords + + self.remaining_reproduction_time = ( + PREY_REPRODUCTION_TIME if prey else PREDATOR_REPRODUCTION_TIME + ) + self.energy_value = None if prey is True else PREDATOR_INITIAL_ENERGY_VALUE + self.alive = True + + def reset_reproduction_time(self) -> None: + """ + >>> e = Entity(True, coords=(0, 0)) + >>> e.reset_reproduction_time() + >>> e.remaining_reproduction_time == PREY_REPRODUCTION_TIME + True + >>> e = Entity(False, coords=(0, 0)) + >>> e.reset_reproduction_time() + >>> e.remaining_reproduction_time == PREDATOR_REPRODUCTION_TIME + True + """ + self.remaining_reproduction_time = ( + PREY_REPRODUCTION_TIME if self.prey is True else PREDATOR_REPRODUCTION_TIME + ) + + def __repr__(self) -> str: + """ + >>> Entity(prey=True, coords=(1, 1)) + Entity(prey=True, coords=(1, 1), remaining_reproduction_time=5) + >>> Entity(prey=False, coords=(2, 1)) # doctest: +NORMALIZE_WHITESPACE + Entity(prey=False, coords=(2, 1), + remaining_reproduction_time=20, energy_value=15) + """ + repr_ = ( + f"Entity(prey={self.prey}, coords={self.coords}, " + f"remaining_reproduction_time={self.remaining_reproduction_time}" + ) + if self.energy_value is not None: + repr_ += f", energy_value={self.energy_value}" + return f"{repr_})" + + +class WaTor: + """ + Represents the main Wa-Tor algorithm. + + :attr time_passed: A function that is called every time + time passes (a chronon) in order to visually display + the new Wa-Tor planet. The time_passed function can block + using time.sleep to slow the algorithm progression. + + >>> wt = WaTor(10, 15) + >>> wt.width + 10 + >>> wt.height + 15 + >>> len(wt.planet) + 15 + >>> len(wt.planet[0]) + 10 + >>> len(wt.get_entities()) == PREDATOR_INITIAL_COUNT + PREY_INITIAL_COUNT + True + """ + + time_passed: Callable[["WaTor", int], None] | None + + def __init__(self, width: int, height: int) -> None: + self.width = width + self.height = height + self.time_passed = None + + self.planet: list[list[Entity | None]] = [[None] * width for _ in range(height)] + + # Populate planet with predators and prey randomly + for _ in range(PREY_INITIAL_COUNT): + self.add_entity(prey=True) + for _ in range(PREDATOR_INITIAL_COUNT): + self.add_entity(prey=False) + self.set_planet(self.planet) + + def set_planet(self, planet: list[list[Entity | None]]) -> None: + """ + Ease of access for testing + + >>> wt = WaTor(WIDTH, HEIGHT) + >>> planet = [ + ... [None, None, None], + ... [None, Entity(True, coords=(1, 1)), None] + ... ] + >>> wt.set_planet(planet) + >>> wt.planet == planet + True + >>> wt.width + 3 + >>> wt.height + 2 + """ + self.planet = planet + self.width = len(planet[0]) + self.height = len(planet) + + def add_entity(self, prey: bool) -> None: + """ + Adds an entity, making sure the entity does + not override another entity + + >>> wt = WaTor(WIDTH, HEIGHT) + >>> wt.set_planet([[None, None], [None, None]]) + >>> wt.add_entity(True) + >>> len(wt.get_entities()) + 1 + >>> wt.add_entity(False) + >>> len(wt.get_entities()) + 2 + """ + while True: + row, col = randint(0, self.height - 1), randint(0, self.width - 1) + if self.planet[row][col] is None: + self.planet[row][col] = Entity(prey=prey, coords=(row, col)) + return + + def get_entities(self) -> list[Entity]: + """ + Returns a list of all the entities within the planet. + + >>> wt = WaTor(WIDTH, HEIGHT) + >>> len(wt.get_entities()) == PREDATOR_INITIAL_COUNT + PREY_INITIAL_COUNT + True + """ + return [entity for column in self.planet for entity in column if entity] + + def balance_predators_and_prey(self) -> None: + """ + Balances predators and preys so that prey + can not dominate the predators, blocking up + space for them to reproduce. + + >>> wt = WaTor(WIDTH, HEIGHT) + >>> for i in range(2000): + ... row, col = i // HEIGHT, i % WIDTH + ... wt.planet[row][col] = Entity(True, coords=(row, col)) + >>> entities = len(wt.get_entities()) + >>> wt.balance_predators_and_prey() + >>> len(wt.get_entities()) == entities + False + """ + entities = self.get_entities() + shuffle(entities) + + if len(entities) >= MAX_ENTITIES - MAX_ENTITIES / 10: + prey = [entity for entity in entities if entity.prey] + predators = [entity for entity in entities if not entity.prey] + + prey_count, predator_count = len(prey), len(predators) + + entities_to_purge = ( + prey[:DELETE_UNBALANCED_ENTITIES] + if prey_count > predator_count + else predators[:DELETE_UNBALANCED_ENTITIES] + ) + for entity in entities_to_purge: + self.planet[entity.coords[0]][entity.coords[1]] = None + + def get_surrounding_prey(self, entity: Entity) -> list[Entity]: + """ + Returns all the prey entities around (N, S, E, W) a predator entity. + + Subtly different to the try_to_move_to_unoccupied square. + + >>> wt = WaTor(WIDTH, HEIGHT) + >>> wt.set_planet([ + ... [None, Entity(True, (0, 1)), None], + ... [None, Entity(False, (1, 1)), None], + ... [None, Entity(True, (2, 1)), None]]) + >>> wt.get_surrounding_prey( + ... Entity(False, (1, 1))) # doctest: +NORMALIZE_WHITESPACE + [Entity(prey=True, coords=(0, 1), remaining_reproduction_time=5), + Entity(prey=True, coords=(2, 1), remaining_reproduction_time=5)] + >>> wt.set_planet([[Entity(False, (0, 0))]]) + >>> wt.get_surrounding_prey(Entity(False, (0, 0))) + [] + >>> wt.set_planet([ + ... [Entity(True, (0, 0)), Entity(False, (1, 0)), Entity(False, (2, 0))], + ... [None, Entity(False, (1, 1)), Entity(True, (2, 1))], + ... [None, None, None]]) + >>> wt.get_surrounding_prey(Entity(False, (1, 0))) + [Entity(prey=True, coords=(0, 0), remaining_reproduction_time=5)] + """ + row, col = entity.coords + adjacent: list[tuple[int, int]] = [ + (row - 1, col), # North + (row + 1, col), # South + (row, col - 1), # West + (row, col + 1), # East + ] + + return [ + ent + for r, c in adjacent + if 0 <= r < self.height + and 0 <= c < self.width + and (ent := self.planet[r][c]) is not None + and ent.prey + ] + + def move_and_reproduce( + self, entity: Entity, direction_orders: list[Literal["N", "E", "S", "W"]] + ) -> None: + """ + Attempts to move to an unoccupied neighbouring square + in either of the four directions (North, South, East, West). + If the move was successful and the remaining_reproduction time is + equal to 0, then a new prey or predator can also be created + in the previous square. + + :param direction_orders: Ordered list (like priority queue) depicting + order to attempt to move. Removes any systematic + approach of checking neighbouring squares. + + >>> planet = [ + ... [None, None, None], + ... [None, Entity(True, coords=(1, 1)), None], + ... [None, None, None] + ... ] + >>> wt = WaTor(WIDTH, HEIGHT) + >>> wt.set_planet(planet) + >>> wt.move_and_reproduce(Entity(True, coords=(1, 1)), direction_orders=["N"]) + >>> wt.planet # doctest: +NORMALIZE_WHITESPACE + [[None, Entity(prey=True, coords=(0, 1), remaining_reproduction_time=4), None], + [None, None, None], + [None, None, None]] + >>> wt.planet[0][0] = Entity(True, coords=(0, 0)) + >>> wt.move_and_reproduce(Entity(True, coords=(0, 1)), + ... direction_orders=["N", "W", "E", "S"]) + >>> wt.planet # doctest: +NORMALIZE_WHITESPACE + [[Entity(prey=True, coords=(0, 0), remaining_reproduction_time=5), None, + Entity(prey=True, coords=(0, 2), remaining_reproduction_time=4)], + [None, None, None], + [None, None, None]] + >>> wt.planet[0][1] = wt.planet[0][2] + >>> wt.planet[0][2] = None + >>> wt.move_and_reproduce(Entity(True, coords=(0, 1)), + ... direction_orders=["N", "W", "S", "E"]) + >>> wt.planet # doctest: +NORMALIZE_WHITESPACE + [[Entity(prey=True, coords=(0, 0), remaining_reproduction_time=5), None, None], + [None, Entity(prey=True, coords=(1, 1), remaining_reproduction_time=4), None], + [None, None, None]] + + >>> wt = WaTor(WIDTH, HEIGHT) + >>> reproducable_entity = Entity(False, coords=(0, 1)) + >>> reproducable_entity.remaining_reproduction_time = 0 + >>> wt.planet = [[None, reproducable_entity]] + >>> wt.move_and_reproduce(reproducable_entity, + ... direction_orders=["N", "W", "S", "E"]) + >>> wt.planet # doctest: +NORMALIZE_WHITESPACE + [[Entity(prey=False, coords=(0, 0), + remaining_reproduction_time=20, energy_value=15), + Entity(prey=False, coords=(0, 1), remaining_reproduction_time=20, + energy_value=15)]] + """ + row, col = coords = entity.coords + + adjacent_squares: dict[Literal["N", "E", "S", "W"], tuple[int, int]] = { + "N": (row - 1, col), # North + "S": (row + 1, col), # South + "W": (row, col - 1), # West + "E": (row, col + 1), # East + } + # Weight adjacent locations + adjacent: list[tuple[int, int]] = [] + for order in direction_orders: + adjacent.append(adjacent_squares[order]) + + for r, c in adjacent: + if ( + 0 <= r < self.height + and 0 <= c < self.width + and self.planet[r][c] is None + ): + # Move entity to empty adjacent square + self.planet[r][c] = entity + self.planet[row][col] = None + entity.coords = (r, c) + break + + # (2.) See if it possible to reproduce in previous square + if coords != entity.coords and entity.remaining_reproduction_time <= 0: + # Check if the entities on the planet is less than the max limit + if len(self.get_entities()) < MAX_ENTITIES: + # Reproduce in previous square + self.planet[row][col] = Entity(prey=entity.prey, coords=coords) + entity.reset_reproduction_time() + else: + entity.remaining_reproduction_time -= 1 + + def perform_prey_actions( + self, entity: Entity, direction_orders: list[Literal["N", "E", "S", "W"]] + ) -> None: + """ + Performs the actions for a prey entity + + For prey the rules are: + 1. At each chronon, a prey moves randomly to one of the adjacent unoccupied + squares. If there are no free squares, no movement takes place. + 2. Once a prey has survived a certain number of chronons it may reproduce. + This is done as it moves to a neighbouring square, + leaving behind a new prey in its old position. + Its reproduction time is also reset to zero. + + >>> wt = WaTor(WIDTH, HEIGHT) + >>> reproducable_entity = Entity(True, coords=(0, 1)) + >>> reproducable_entity.remaining_reproduction_time = 0 + >>> wt.planet = [[None, reproducable_entity]] + >>> wt.perform_prey_actions(reproducable_entity, + ... direction_orders=["N", "W", "S", "E"]) + >>> wt.planet # doctest: +NORMALIZE_WHITESPACE + [[Entity(prey=True, coords=(0, 0), remaining_reproduction_time=5), + Entity(prey=True, coords=(0, 1), remaining_reproduction_time=5)]] + """ + self.move_and_reproduce(entity, direction_orders) + + def perform_predator_actions( + self, + entity: Entity, + occupied_by_prey_coords: tuple[int, int] | None, + direction_orders: list[Literal["N", "E", "S", "W"]], + ) -> None: + """ + Performs the actions for a predator entity + + :param occupied_by_prey_coords: Move to this location if there is prey there + + For predators the rules are: + 1. At each chronon, a predator moves randomly to an adjacent square occupied + by a prey. If there is none, the predator moves to a random adjacent + unoccupied square. If there are no free squares, no movement takes place. + 2. At each chronon, each predator is deprived of a unit of energy. + 3. Upon reaching zero energy, a predator dies. + 4. If a predator moves to a square occupied by a prey, + it eats the prey and earns a certain amount of energy. + 5. Once a predator has survived a certain number of chronons + it may reproduce in exactly the same way as the prey. + + >>> wt = WaTor(WIDTH, HEIGHT) + >>> wt.set_planet([[Entity(True, coords=(0, 0)), Entity(False, coords=(0, 1))]]) + >>> wt.perform_predator_actions(Entity(False, coords=(0, 1)), (0, 0), []) + >>> wt.planet # doctest: +NORMALIZE_WHITESPACE + [[Entity(prey=False, coords=(0, 0), + remaining_reproduction_time=20, energy_value=19), None]] + """ + assert entity.energy_value is not None # [type checking] + + # (3.) If the entity has 0 energy, it will die + if entity.energy_value == 0: + self.planet[entity.coords[0]][entity.coords[1]] = None + return + + # (1.) Move to entity if possible + if occupied_by_prey_coords is not None: + # Kill the prey + prey = self.planet[occupied_by_prey_coords[0]][occupied_by_prey_coords[1]] + assert prey is not None + prey.alive = False + + # Move onto prey + self.planet[occupied_by_prey_coords[0]][occupied_by_prey_coords[1]] = entity + self.planet[entity.coords[0]][entity.coords[1]] = None + + entity.coords = occupied_by_prey_coords + # (4.) Eats the prey and earns energy + entity.energy_value += PREDATOR_FOOD_VALUE + else: + # (5.) If it has survived the certain number of chronons it will also + # reproduce in this function + self.move_and_reproduce(entity, direction_orders) + + # (2.) Each chronon, the predator is deprived of a unit of energy + entity.energy_value -= 1 + + def run(self, *, iteration_count: int) -> None: + """ + Emulate time passing by looping iteration_count times + + >>> wt = WaTor(WIDTH, HEIGHT) + >>> wt.run(iteration_count=PREDATOR_INITIAL_ENERGY_VALUE - 1) + >>> len(list(filter(lambda entity: entity.prey is False, + ... wt.get_entities()))) >= PREDATOR_INITIAL_COUNT + True + """ + for iter_num in range(iteration_count): + # Generate list of all entities in order to randomly + # pop an entity at a time to simulate true randomness + # This removes the systematic approach of iterating + # through each entity width by height + all_entities = self.get_entities() + + for __ in range(len(all_entities)): + entity = all_entities.pop(randint(0, len(all_entities) - 1)) + if entity.alive is False: + continue + + directions: list[Literal["N", "E", "S", "W"]] = ["N", "E", "S", "W"] + shuffle(directions) # Randomly shuffle directions + + if entity.prey: + self.perform_prey_actions(entity, directions) + else: + # Create list of surrounding prey + surrounding_prey = self.get_surrounding_prey(entity) + surrounding_prey_coords = None + + if surrounding_prey: + # Again, randomly shuffle directions + shuffle(surrounding_prey) + surrounding_prey_coords = surrounding_prey[0].coords + + self.perform_predator_actions( + entity, surrounding_prey_coords, directions + ) + + # Balance out the predators and prey + self.balance_predators_and_prey() + + if self.time_passed is not None: + # Call time_passed function for Wa-Tor planet + # visualisation in a terminal or a graph. + self.time_passed(self, iter_num) + + +def visualise(wt: WaTor, iter_number: int, *, colour: bool = True) -> None: + """ + Visually displays the Wa-Tor planet using + an ascii code in terminal to clear and re-print + the Wa-Tor planet at intervals. + + Uses ascii colour codes to colourfully display + the predators and prey. + + (0x60f197) Prey = # + (0xfffff) Predator = x + + >>> wt = WaTor(30, 30) + >>> wt.set_planet([ + ... [Entity(True, coords=(0, 0)), Entity(False, coords=(0, 1)), None], + ... [Entity(False, coords=(1, 0)), None, Entity(False, coords=(1, 2))], + ... [None, Entity(True, coords=(2, 1)), None] + ... ]) + >>> visualise(wt, 0, colour=False) # doctest: +NORMALIZE_WHITESPACE + # x . + x . x + . # . + + Iteration: 0 | Prey count: 2 | Predator count: 3 | + """ + if colour: + __import__("os").system("") + print("\x1b[0;0H\x1b[2J\x1b[?25l") + + reprint = "\x1b[0;0H" if colour else "" + ansi_colour_end = "\x1b[0m " if colour else " " + + planet = wt.planet + output = "" + + # Iterate over every entity in the planet + for row in planet: + for entity in row: + if entity is None: + output += " . " + else: + if colour is True: + output += ( + "\x1b[38;2;96;241;151m" + if entity.prey + else "\x1b[38;2;255;255;15m" + ) + output += f" {'#' if entity.prey else 'x'}{ansi_colour_end}" + + output += "\n" + + entities = wt.get_entities() + prey_count = sum(entity.prey for entity in entities) + + print( + f"{output}\n Iteration: {iter_number} | Prey count: {prey_count} | " + f"Predator count: {len(entities) - prey_count} | {reprint}" + ) + # Block the thread to be able to visualise seeing the algorithm + sleep(0.05) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + + wt = WaTor(WIDTH, HEIGHT) + wt.time_passed = visualise + wt.run(iteration_count=100_000) From f24ab2c60dabb11c37667c5899c39713e84fc871 Mon Sep 17 00:00:00 2001 From: Amir Hosseini <19665344+itsamirhn@users.noreply.github.com> Date: Mon, 14 Aug 2023 09:07:41 +0330 Subject: [PATCH 149/757] Add: Two Regex match algorithm (Recursive & DP) (#6321) * Add recursive solution to regex_match.py * Add dp solution to regex_match.py * Add link to regex_match.py * Minor edit * Minor change * Minor change * Update dynamic_programming/regex_match.py Co-authored-by: Tianyi Zheng * Update dynamic_programming/regex_match.py Co-authored-by: Tianyi Zheng * Fix ruff formatting in if statements * Update dynamic_programming/regex_match.py Co-authored-by: Tianyi Zheng * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: Tianyi Zheng Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- dynamic_programming/regex_match.py | 97 ++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 dynamic_programming/regex_match.py diff --git a/dynamic_programming/regex_match.py b/dynamic_programming/regex_match.py new file mode 100644 index 000000000000..200a882831c0 --- /dev/null +++ b/dynamic_programming/regex_match.py @@ -0,0 +1,97 @@ +""" +Regex matching check if a text matches pattern or not. +Pattern: + '.' Matches any single character. + '*' Matches zero or more of the preceding element. +More info: + https://medium.com/trick-the-interviwer/regular-expression-matching-9972eb74c03 +""" + + +def recursive_match(text: str, pattern: str) -> bool: + """ + Recursive matching algorithm. + + Time complexity: O(2 ^ (|text| + |pattern|)) + Space complexity: Recursion depth is O(|text| + |pattern|). + + :param text: Text to match. + :param pattern: Pattern to match. + :return: True if text matches pattern, False otherwise. + + >>> recursive_match('abc', 'a.c') + True + >>> recursive_match('abc', 'af*.c') + True + >>> recursive_match('abc', 'a.c*') + True + >>> recursive_match('abc', 'a.c*d') + False + >>> recursive_match('aa', '.*') + True + """ + if not pattern: + return not text + + if not text: + return pattern[-1] == "*" and recursive_match(text, pattern[:-2]) + + if text[-1] == pattern[-1] or pattern[-1] == ".": + return recursive_match(text[:-1], pattern[:-1]) + + if pattern[-1] == "*": + return recursive_match(text[:-1], pattern) or recursive_match( + text, pattern[:-2] + ) + + return False + + +def dp_match(text: str, pattern: str) -> bool: + """ + Dynamic programming matching algorithm. + + Time complexity: O(|text| * |pattern|) + Space complexity: O(|text| * |pattern|) + + :param text: Text to match. + :param pattern: Pattern to match. + :return: True if text matches pattern, False otherwise. + + >>> dp_match('abc', 'a.c') + True + >>> dp_match('abc', 'af*.c') + True + >>> dp_match('abc', 'a.c*') + True + >>> dp_match('abc', 'a.c*d') + False + >>> dp_match('aa', '.*') + True + """ + m = len(text) + n = len(pattern) + dp = [[False for _ in range(n + 1)] for _ in range(m + 1)] + dp[0][0] = True + + for j in range(1, n + 1): + dp[0][j] = pattern[j - 1] == "*" and dp[0][j - 2] + + for i in range(1, m + 1): + for j in range(1, n + 1): + if pattern[j - 1] in {".", text[i - 1]}: + dp[i][j] = dp[i - 1][j - 1] + elif pattern[j - 1] == "*": + dp[i][j] = dp[i][j - 2] + if pattern[j - 2] in {".", text[i - 1]}: + dp[i][j] |= dp[i - 1][j] + else: + dp[i][j] = False + + return dp[m][n] + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 02d89bde679488e97cdb077c511b3dbfb660e2b8 Mon Sep 17 00:00:00 2001 From: Ajinkya Chikhale <86607732+ajinkyac03@users.noreply.github.com> Date: Mon, 14 Aug 2023 12:42:42 +0530 Subject: [PATCH 150/757] Added implementation for Tribonacci sequence using dp (#6356) * Added implementation for Tribonacci sequence using dp * Updated parameter name * Apply suggestions from code review --------- Co-authored-by: Tianyi Zheng --- dynamic_programming/tribonacci.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 dynamic_programming/tribonacci.py diff --git a/dynamic_programming/tribonacci.py b/dynamic_programming/tribonacci.py new file mode 100644 index 000000000000..58e15da918e2 --- /dev/null +++ b/dynamic_programming/tribonacci.py @@ -0,0 +1,24 @@ +# Tribonacci sequence using Dynamic Programming + + +def tribonacci(num: int) -> list[int]: + """ + Given a number, return first n Tribonacci Numbers. + >>> tribonacci(5) + [0, 0, 1, 1, 2] + >>> tribonacci(8) + [0, 0, 1, 1, 2, 4, 7, 13] + """ + dp = [0] * num + dp[2] = 1 + + for i in range(3, num): + dp[i] = dp[i - 1] + dp[i - 2] + dp[i - 3] + + return dp + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From c290dd6a433b43b242336d49d227f5e25bbb76de Mon Sep 17 00:00:00 2001 From: Adithya Awati Date: Mon, 14 Aug 2023 12:46:24 +0530 Subject: [PATCH 151/757] Update run.py in machine_learning/forecasting (#8957) * Fixed reading CSV file, added type check for data_safety_checker function * Formatted run.py * updating DIRECTORY.md --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 1 + machine_learning/forecasting/ex_data.csv | 2 +- machine_learning/forecasting/run.py | 35 ++++++++++++------------ 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index cdcd1a8ae8cc..3a244ca6caaf 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -336,6 +336,7 @@ * [Minimum Tickets Cost](dynamic_programming/minimum_tickets_cost.py) * [Optimal Binary Search Tree](dynamic_programming/optimal_binary_search_tree.py) * [Palindrome Partitioning](dynamic_programming/palindrome_partitioning.py) + * [Regex Match](dynamic_programming/regex_match.py) * [Rod Cutting](dynamic_programming/rod_cutting.py) * [Subset Generation](dynamic_programming/subset_generation.py) * [Sum Of Subset](dynamic_programming/sum_of_subset.py) diff --git a/machine_learning/forecasting/ex_data.csv b/machine_learning/forecasting/ex_data.csv index 1c429e649755..e6e73c4a1ca4 100644 --- a/machine_learning/forecasting/ex_data.csv +++ b/machine_learning/forecasting/ex_data.csv @@ -1,4 +1,4 @@ -total_user,total_events,days +total_users,total_events,days 18231,0.0,1 22621,1.0,2 15675,0.0,3 diff --git a/machine_learning/forecasting/run.py b/machine_learning/forecasting/run.py index 0909b76d8907..88c4a537b302 100644 --- a/machine_learning/forecasting/run.py +++ b/machine_learning/forecasting/run.py @@ -1,6 +1,6 @@ """ this is code for forecasting -but i modified it and used it for safety checker of data +but I modified it and used it for safety checker of data for ex: you have an online shop and for some reason some data are missing (the amount of data that u expected are not supposed to be) then we can use it @@ -102,6 +102,10 @@ def data_safety_checker(list_vote: list, actual_result: float) -> bool: """ safe = 0 not_safe = 0 + + if not isinstance(actual_result, float): + raise TypeError("Actual result should be float. Value passed is a list") + for i in list_vote: if i > actual_result: safe = not_safe + 1 @@ -114,16 +118,11 @@ def data_safety_checker(list_vote: list, actual_result: float) -> bool: if __name__ == "__main__": - # data_input_df = pd.read_csv("ex_data.csv", header=None) - data_input = [[18231, 0.0, 1], [22621, 1.0, 2], [15675, 0.0, 3], [23583, 1.0, 4]] - data_input_df = pd.DataFrame( - data_input, columns=["total_user", "total_even", "days"] - ) - """ data column = total user in a day, how much online event held in one day, what day is that(sunday-saturday) """ + data_input_df = pd.read_csv("ex_data.csv") # start normalization normalize_df = Normalizer().fit_transform(data_input_df.values) @@ -138,23 +137,23 @@ def data_safety_checker(list_vote: list, actual_result: float) -> bool: x_test = x[len(x) - 1 :] # for linear regression & sarimax - trn_date = total_date[: len(total_date) - 1] - trn_user = total_user[: len(total_user) - 1] - trn_match = total_match[: len(total_match) - 1] + train_date = total_date[: len(total_date) - 1] + train_user = total_user[: len(total_user) - 1] + train_match = total_match[: len(total_match) - 1] - tst_date = total_date[len(total_date) - 1 :] - tst_user = total_user[len(total_user) - 1 :] - tst_match = total_match[len(total_match) - 1 :] + test_date = total_date[len(total_date) - 1 :] + test_user = total_user[len(total_user) - 1 :] + test_match = total_match[len(total_match) - 1 :] # voting system with forecasting res_vote = [ linear_regression_prediction( - trn_date, trn_user, trn_match, tst_date, tst_match + train_date, train_user, train_match, test_date, test_match ), - sarimax_predictor(trn_user, trn_match, tst_match), - support_vector_regressor(x_train, x_test, trn_user), + sarimax_predictor(train_user, train_match, test_match), + support_vector_regressor(x_train, x_test, train_user), ] # check the safety of today's data - not_str = "" if data_safety_checker(res_vote, tst_user) else "not " - print("Today's data is {not_str}safe.") + not_str = "" if data_safety_checker(res_vote, test_user[0]) else "not " + print(f"Today's data is {not_str}safe.") From 4b7ecb6a8134379481dd3d5035cb99a627930462 Mon Sep 17 00:00:00 2001 From: Caeden Perelli-Harris Date: Mon, 14 Aug 2023 09:28:52 +0100 Subject: [PATCH 152/757] Create is valid email address algorithm (#8907) * feat(strings): Create is valid email address * updating DIRECTORY.md * feat(strings): Create is_valid_email_address algorithm * chore(is_valid_email_address): Implement changes from code review * Update strings/is_valid_email_address.py Co-authored-by: Tianyi Zheng * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * chore(is_valid_email_address): Fix ruff error * Update strings/is_valid_email_address.py Co-authored-by: Tianyi Zheng --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: Tianyi Zheng Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- DIRECTORY.md | 1 + strings/is_valid_email_address.py | 117 ++++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+) create mode 100644 strings/is_valid_email_address.py diff --git a/DIRECTORY.md b/DIRECTORY.md index 3a244ca6caaf..14152e4abd04 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -1171,6 +1171,7 @@ * [Is Pangram](strings/is_pangram.py) * [Is Spain National Id](strings/is_spain_national_id.py) * [Is Srilankan Phone Number](strings/is_srilankan_phone_number.py) + * [Is Valid Email Address](strings/is_valid_email_address.py) * [Jaro Winkler](strings/jaro_winkler.py) * [Join](strings/join.py) * [Knuth Morris Pratt](strings/knuth_morris_pratt.py) diff --git a/strings/is_valid_email_address.py b/strings/is_valid_email_address.py new file mode 100644 index 000000000000..205394f81297 --- /dev/null +++ b/strings/is_valid_email_address.py @@ -0,0 +1,117 @@ +""" +Implements an is valid email address algorithm + +@ https://en.wikipedia.org/wiki/Email_address +""" + +import string + +email_tests: tuple[tuple[str, bool], ...] = ( + ("simple@example.com", True), + ("very.common@example.com", True), + ("disposable.style.email.with+symbol@example.com", True), + ("other-email-with-hyphen@and.subdomains.example.com", True), + ("fully-qualified-domain@example.com", True), + ("user.name+tag+sorting@example.com", True), + ("x@example.com", True), + ("example-indeed@strange-example.com", True), + ("test/test@test.com", True), + ( + "123456789012345678901234567890123456789012345678901234567890123@example.com", + True, + ), + ("admin@mailserver1", True), + ("example@s.example", True), + ("Abc.example.com", False), + ("A@b@c@example.com", False), + ("abc@example..com", False), + ("a(c)d,e:f;gi[j\\k]l@example.com", False), + ( + "12345678901234567890123456789012345678901234567890123456789012345@example.com", + False, + ), + ("i.like.underscores@but_its_not_allowed_in_this_part", False), + ("", False), +) + +# The maximum octets (one character as a standard unicode character is one byte) +# that the local part and the domain part can have +MAX_LOCAL_PART_OCTETS = 64 +MAX_DOMAIN_OCTETS = 255 + + +def is_valid_email_address(email: str) -> bool: + """ + Returns True if the passed email address is valid. + + The local part of the email precedes the singular @ symbol and + is associated with a display-name. For example, "john.smith" + The domain is stricter than the local part and follows the @ symbol. + + Global email checks: + 1. There can only be one @ symbol in the email address. Technically if the + @ symbol is quoted in the local-part, then it is valid, however this + implementation ignores "" for now. + (See https://en.wikipedia.org/wiki/Email_address#:~:text=If%20quoted,) + 2. The local-part and the domain are limited to a certain number of octets. With + unicode storing a single character in one byte, each octet is equivalent to + a character. Hence, we can just check the length of the string. + Checks for the local-part: + 3. The local-part may contain: upper and lowercase latin letters, digits 0 to 9, + and printable characters (!#$%&'*+-/=?^_`{|}~) + 4. The local-part may also contain a "." in any place that is not the first or + last character, and may not have more than one "." consecutively. + + Checks for the domain: + 5. The domain may contain: upper and lowercase latin letters and digits 0 to 9 + 6. Hyphen "-", provided that it is not the first or last character + 7. The domain may also contain a "." in any place that is not the first or + last character, and may not have more than one "." consecutively. + + >>> for email, valid in email_tests: + ... assert is_valid_email_address(email) == valid + """ + + # (1.) Make sure that there is only one @ symbol in the email address + if email.count("@") != 1: + return False + + local_part, domain = email.split("@") + # (2.) Check octet length of the local part and domain + if len(local_part) > MAX_LOCAL_PART_OCTETS or len(domain) > MAX_DOMAIN_OCTETS: + return False + + # (3.) Validate the characters in the local-part + if any( + char not in string.ascii_letters + string.digits + ".(!#$%&'*+-/=?^_`{|}~)" + for char in local_part + ): + return False + + # (4.) Validate the placement of "." characters in the local-part + if local_part.startswith(".") or local_part.endswith(".") or ".." in local_part: + return False + + # (5.) Validate the characters in the domain + if any(char not in string.ascii_letters + string.digits + ".-" for char in domain): + return False + + # (6.) Validate the placement of "-" characters + if domain.startswith("-") or domain.endswith("."): + return False + + # (7.) Validate the placement of "." characters + if domain.startswith(".") or domain.endswith(".") or ".." in domain: + return False + return True + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + + for email, valid in email_tests: + is_valid = is_valid_email_address(email) + assert is_valid == valid, f"{email} is {is_valid}" + print(f"Email address {email} is {'not ' if not is_valid else ''}valid") From ac68dc1128535b6798af256fcdab67340f6c0fd9 Mon Sep 17 00:00:00 2001 From: Adithya Awati <1ds21ai001@dsce.edu.in> Date: Mon, 14 Aug 2023 14:04:16 +0530 Subject: [PATCH 153/757] Fixed Pytest warnings for machine_learning/forecasting (#8958) * updating DIRECTORY.md * Fixed pyTest Warnings --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 1 + machine_learning/forecasting/run.py | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index 14152e4abd04..384ce1b2209d 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -340,6 +340,7 @@ * [Rod Cutting](dynamic_programming/rod_cutting.py) * [Subset Generation](dynamic_programming/subset_generation.py) * [Sum Of Subset](dynamic_programming/sum_of_subset.py) + * [Tribonacci](dynamic_programming/tribonacci.py) * [Viterbi](dynamic_programming/viterbi.py) * [Word Break](dynamic_programming/word_break.py) diff --git a/machine_learning/forecasting/run.py b/machine_learning/forecasting/run.py index 88c4a537b302..64e719daacc2 100644 --- a/machine_learning/forecasting/run.py +++ b/machine_learning/forecasting/run.py @@ -11,6 +11,8 @@ u can just adjust it for ur own purpose """ +from warnings import simplefilter + import numpy as np import pandas as pd from sklearn.preprocessing import Normalizer @@ -45,8 +47,10 @@ def sarimax_predictor(train_user: list, train_match: list, test_match: list) -> >>> sarimax_predictor([4,2,6,8], [3,1,2,4], [2]) 6.6666671111109626 """ + # Suppress the User Warning raised by SARIMAX due to insufficient observations + simplefilter("ignore", UserWarning) order = (1, 2, 1) - seasonal_order = (1, 1, 0, 7) + seasonal_order = (1, 1, 1, 7) model = SARIMAX( train_user, exog=train_match, order=order, seasonal_order=seasonal_order ) From 2ab3bf2689d21e7375539c79ecee358e9d7c3359 Mon Sep 17 00:00:00 2001 From: robertjcalistri <85811008+robertjcalistri@users.noreply.github.com> Date: Mon, 14 Aug 2023 05:31:53 -0400 Subject: [PATCH 154/757] =?UTF-8?q?Added=20functions=20to=20calculate=20te?= =?UTF-8?q?mperature=20of=20an=20ideal=20gas=20and=20number=20o=E2=80=A6?= =?UTF-8?q?=20(#8919)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Added functions to calculate temperature of an ideal gas and number of moles of an ideal gas * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update physics/ideal_gas_law.py Renamed function name Co-authored-by: Tianyi Zheng * Update physics/ideal_gas_law.py Updated formatting Co-authored-by: Tianyi Zheng * Update physics/ideal_gas_law.py Removed unnecessary parentheses Co-authored-by: Tianyi Zheng * Update physics/ideal_gas_law.py Removed unnecessary parentheses Co-authored-by: Tianyi Zheng * Update ideal_gas_law.py Updated incorrect function calls moles of gas system doctests * Update physics/ideal_gas_law.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Tianyi Zheng --- physics/ideal_gas_law.py | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/physics/ideal_gas_law.py b/physics/ideal_gas_law.py index 805da47b0079..09b4fb3a9c14 100644 --- a/physics/ideal_gas_law.py +++ b/physics/ideal_gas_law.py @@ -53,6 +53,40 @@ def volume_of_gas_system(moles: float, kelvin: float, pressure: float) -> float: return moles * kelvin * UNIVERSAL_GAS_CONSTANT / pressure +def temperature_of_gas_system(moles: float, volume: float, pressure: float) -> float: + """ + >>> temperature_of_gas_system(2, 100, 5) + 30.068090996146232 + >>> temperature_of_gas_system(11, 5009, 1000) + 54767.66101807144 + >>> temperature_of_gas_system(3, -0.46, 23.5) + Traceback (most recent call last): + ... + ValueError: Invalid inputs. Enter positive value. + """ + if moles < 0 or volume < 0 or pressure < 0: + raise ValueError("Invalid inputs. Enter positive value.") + + return pressure * volume / (moles * UNIVERSAL_GAS_CONSTANT) + + +def moles_of_gas_system(kelvin: float, volume: float, pressure: float) -> float: + """ + >>> moles_of_gas_system(100, 5, 10) + 0.06013618199229246 + >>> moles_of_gas_system(110, 5009, 1000) + 5476.766101807144 + >>> moles_of_gas_system(3, -0.46, 23.5) + Traceback (most recent call last): + ... + ValueError: Invalid inputs. Enter positive value. + """ + if kelvin < 0 or volume < 0 or pressure < 0: + raise ValueError("Invalid inputs. Enter positive value.") + + return pressure * volume / (kelvin * UNIVERSAL_GAS_CONSTANT) + + if __name__ == "__main__": from doctest import testmod From fb1b939a89fb08370297cbb455846f61f66847bc Mon Sep 17 00:00:00 2001 From: Caeden Perelli-Harris Date: Mon, 14 Aug 2023 12:17:27 +0100 Subject: [PATCH 155/757] Consolidate find_min and find_min recursive and find_max and find_max_recursive (#8960) * updating DIRECTORY.md * refactor(min-max): Consolidate implementations * updating DIRECTORY.md * refactor(min-max): Append _iterative to func name --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 2 -- maths/find_max.py | 65 +++++++++++++++++++++++++++++++++---- maths/find_max_recursion.py | 58 --------------------------------- maths/find_min.py | 65 +++++++++++++++++++++++++++++++++---- maths/find_min_recursion.py | 58 --------------------------------- 5 files changed, 118 insertions(+), 130 deletions(-) delete mode 100644 maths/find_max_recursion.py delete mode 100644 maths/find_min_recursion.py diff --git a/DIRECTORY.md b/DIRECTORY.md index 384ce1b2209d..be5fa3584a58 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -573,9 +573,7 @@ * [Fermat Little Theorem](maths/fermat_little_theorem.py) * [Fibonacci](maths/fibonacci.py) * [Find Max](maths/find_max.py) - * [Find Max Recursion](maths/find_max_recursion.py) * [Find Min](maths/find_min.py) - * [Find Min Recursion](maths/find_min_recursion.py) * [Floor](maths/floor.py) * [Gamma](maths/gamma.py) * [Gamma Recursive](maths/gamma_recursive.py) diff --git a/maths/find_max.py b/maths/find_max.py index 684fbe8161e8..729a80ab421c 100644 --- a/maths/find_max.py +++ b/maths/find_max.py @@ -1,23 +1,23 @@ from __future__ import annotations -def find_max(nums: list[int | float]) -> int | float: +def find_max_iterative(nums: list[int | float]) -> int | float: """ >>> for nums in ([3, 2, 1], [-3, -2, -1], [3, -3, 0], [3.0, 3.1, 2.9]): - ... find_max(nums) == max(nums) + ... find_max_iterative(nums) == max(nums) True True True True - >>> find_max([2, 4, 9, 7, 19, 94, 5]) + >>> find_max_iterative([2, 4, 9, 7, 19, 94, 5]) 94 - >>> find_max([]) + >>> find_max_iterative([]) Traceback (most recent call last): ... - ValueError: find_max() arg is an empty sequence + ValueError: find_max_iterative() arg is an empty sequence """ if len(nums) == 0: - raise ValueError("find_max() arg is an empty sequence") + raise ValueError("find_max_iterative() arg is an empty sequence") max_num = nums[0] for x in nums: if x > max_num: @@ -25,6 +25,59 @@ def find_max(nums: list[int | float]) -> int | float: return max_num +# Divide and Conquer algorithm +def find_max_recursive(nums: list[int | float], left: int, right: int) -> int | float: + """ + find max value in list + :param nums: contains elements + :param left: index of first element + :param right: index of last element + :return: max in nums + + >>> for nums in ([3, 2, 1], [-3, -2, -1], [3, -3, 0], [3.0, 3.1, 2.9]): + ... find_max_recursive(nums, 0, len(nums) - 1) == max(nums) + True + True + True + True + >>> nums = [1, 3, 5, 7, 9, 2, 4, 6, 8, 10] + >>> find_max_recursive(nums, 0, len(nums) - 1) == max(nums) + True + >>> find_max_recursive([], 0, 0) + Traceback (most recent call last): + ... + ValueError: find_max_recursive() arg is an empty sequence + >>> find_max_recursive(nums, 0, len(nums)) == max(nums) + Traceback (most recent call last): + ... + IndexError: list index out of range + >>> find_max_recursive(nums, -len(nums), -1) == max(nums) + True + >>> find_max_recursive(nums, -len(nums) - 1, -1) == max(nums) + Traceback (most recent call last): + ... + IndexError: list index out of range + """ + if len(nums) == 0: + raise ValueError("find_max_recursive() arg is an empty sequence") + if ( + left >= len(nums) + or left < -len(nums) + or right >= len(nums) + or right < -len(nums) + ): + raise IndexError("list index out of range") + if left == right: + return nums[left] + mid = (left + right) >> 1 # the middle + left_max = find_max_recursive(nums, left, mid) # find max in range[left, mid] + right_max = find_max_recursive( + nums, mid + 1, right + ) # find max in range[mid + 1, right] + + return left_max if left_max >= right_max else right_max + + if __name__ == "__main__": import doctest diff --git a/maths/find_max_recursion.py b/maths/find_max_recursion.py deleted file mode 100644 index 629932e0818f..000000000000 --- a/maths/find_max_recursion.py +++ /dev/null @@ -1,58 +0,0 @@ -from __future__ import annotations - - -# Divide and Conquer algorithm -def find_max(nums: list[int | float], left: int, right: int) -> int | float: - """ - find max value in list - :param nums: contains elements - :param left: index of first element - :param right: index of last element - :return: max in nums - - >>> for nums in ([3, 2, 1], [-3, -2, -1], [3, -3, 0], [3.0, 3.1, 2.9]): - ... find_max(nums, 0, len(nums) - 1) == max(nums) - True - True - True - True - >>> nums = [1, 3, 5, 7, 9, 2, 4, 6, 8, 10] - >>> find_max(nums, 0, len(nums) - 1) == max(nums) - True - >>> find_max([], 0, 0) - Traceback (most recent call last): - ... - ValueError: find_max() arg is an empty sequence - >>> find_max(nums, 0, len(nums)) == max(nums) - Traceback (most recent call last): - ... - IndexError: list index out of range - >>> find_max(nums, -len(nums), -1) == max(nums) - True - >>> find_max(nums, -len(nums) - 1, -1) == max(nums) - Traceback (most recent call last): - ... - IndexError: list index out of range - """ - if len(nums) == 0: - raise ValueError("find_max() arg is an empty sequence") - if ( - left >= len(nums) - or left < -len(nums) - or right >= len(nums) - or right < -len(nums) - ): - raise IndexError("list index out of range") - if left == right: - return nums[left] - mid = (left + right) >> 1 # the middle - left_max = find_max(nums, left, mid) # find max in range[left, mid] - right_max = find_max(nums, mid + 1, right) # find max in range[mid + 1, right] - - return left_max if left_max >= right_max else right_max - - -if __name__ == "__main__": - import doctest - - doctest.testmod(verbose=True) diff --git a/maths/find_min.py b/maths/find_min.py index 2eac087c6388..762562e36ef9 100644 --- a/maths/find_min.py +++ b/maths/find_min.py @@ -1,33 +1,86 @@ from __future__ import annotations -def find_min(nums: list[int | float]) -> int | float: +def find_min_iterative(nums: list[int | float]) -> int | float: """ Find Minimum Number in a List :param nums: contains elements :return: min number in list >>> for nums in ([3, 2, 1], [-3, -2, -1], [3, -3, 0], [3.0, 3.1, 2.9]): - ... find_min(nums) == min(nums) + ... find_min_iterative(nums) == min(nums) True True True True - >>> find_min([0, 1, 2, 3, 4, 5, -3, 24, -56]) + >>> find_min_iterative([0, 1, 2, 3, 4, 5, -3, 24, -56]) -56 - >>> find_min([]) + >>> find_min_iterative([]) Traceback (most recent call last): ... - ValueError: find_min() arg is an empty sequence + ValueError: find_min_iterative() arg is an empty sequence """ if len(nums) == 0: - raise ValueError("find_min() arg is an empty sequence") + raise ValueError("find_min_iterative() arg is an empty sequence") min_num = nums[0] for num in nums: min_num = min(min_num, num) return min_num +# Divide and Conquer algorithm +def find_min_recursive(nums: list[int | float], left: int, right: int) -> int | float: + """ + find min value in list + :param nums: contains elements + :param left: index of first element + :param right: index of last element + :return: min in nums + + >>> for nums in ([3, 2, 1], [-3, -2, -1], [3, -3, 0], [3.0, 3.1, 2.9]): + ... find_min_recursive(nums, 0, len(nums) - 1) == min(nums) + True + True + True + True + >>> nums = [1, 3, 5, 7, 9, 2, 4, 6, 8, 10] + >>> find_min_recursive(nums, 0, len(nums) - 1) == min(nums) + True + >>> find_min_recursive([], 0, 0) + Traceback (most recent call last): + ... + ValueError: find_min_recursive() arg is an empty sequence + >>> find_min_recursive(nums, 0, len(nums)) == min(nums) + Traceback (most recent call last): + ... + IndexError: list index out of range + >>> find_min_recursive(nums, -len(nums), -1) == min(nums) + True + >>> find_min_recursive(nums, -len(nums) - 1, -1) == min(nums) + Traceback (most recent call last): + ... + IndexError: list index out of range + """ + if len(nums) == 0: + raise ValueError("find_min_recursive() arg is an empty sequence") + if ( + left >= len(nums) + or left < -len(nums) + or right >= len(nums) + or right < -len(nums) + ): + raise IndexError("list index out of range") + if left == right: + return nums[left] + mid = (left + right) >> 1 # the middle + left_min = find_min_recursive(nums, left, mid) # find min in range[left, mid] + right_min = find_min_recursive( + nums, mid + 1, right + ) # find min in range[mid + 1, right] + + return left_min if left_min <= right_min else right_min + + if __name__ == "__main__": import doctest diff --git a/maths/find_min_recursion.py b/maths/find_min_recursion.py deleted file mode 100644 index 4d11015efcd5..000000000000 --- a/maths/find_min_recursion.py +++ /dev/null @@ -1,58 +0,0 @@ -from __future__ import annotations - - -# Divide and Conquer algorithm -def find_min(nums: list[int | float], left: int, right: int) -> int | float: - """ - find min value in list - :param nums: contains elements - :param left: index of first element - :param right: index of last element - :return: min in nums - - >>> for nums in ([3, 2, 1], [-3, -2, -1], [3, -3, 0], [3.0, 3.1, 2.9]): - ... find_min(nums, 0, len(nums) - 1) == min(nums) - True - True - True - True - >>> nums = [1, 3, 5, 7, 9, 2, 4, 6, 8, 10] - >>> find_min(nums, 0, len(nums) - 1) == min(nums) - True - >>> find_min([], 0, 0) - Traceback (most recent call last): - ... - ValueError: find_min() arg is an empty sequence - >>> find_min(nums, 0, len(nums)) == min(nums) - Traceback (most recent call last): - ... - IndexError: list index out of range - >>> find_min(nums, -len(nums), -1) == min(nums) - True - >>> find_min(nums, -len(nums) - 1, -1) == min(nums) - Traceback (most recent call last): - ... - IndexError: list index out of range - """ - if len(nums) == 0: - raise ValueError("find_min() arg is an empty sequence") - if ( - left >= len(nums) - or left < -len(nums) - or right >= len(nums) - or right < -len(nums) - ): - raise IndexError("list index out of range") - if left == right: - return nums[left] - mid = (left + right) >> 1 # the middle - left_min = find_min(nums, left, mid) # find min in range[left, mid] - right_min = find_min(nums, mid + 1, right) # find min in range[mid + 1, right] - - return left_min if left_min <= right_min else right_min - - -if __name__ == "__main__": - import doctest - - doctest.testmod(verbose=True) From 7021afda047b034958bfdb67e8479af2e8c7aeb9 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 14 Aug 2023 23:12:11 -0400 Subject: [PATCH 156/757] [pre-commit.ci] pre-commit autoupdate (#8963) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.0.282 → v0.0.284](https://github.com/astral-sh/ruff-pre-commit/compare/v0.0.282...v0.0.284) - [github.com/tox-dev/pyproject-fmt: 0.13.0 → 0.13.1](https://github.com/tox-dev/pyproject-fmt/compare/0.13.0...0.13.1) - [github.com/pre-commit/mirrors-mypy: v1.4.1 → v1.5.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.4.1...v1.5.0) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index da6762123b04..b08139561639 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.0.282 + rev: v0.0.284 hooks: - id: ruff @@ -33,7 +33,7 @@ repos: - tomli - repo: https://github.com/tox-dev/pyproject-fmt - rev: "0.13.0" + rev: "0.13.1" hooks: - id: pyproject-fmt @@ -51,7 +51,7 @@ repos: - id: validate-pyproject - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.4.1 + rev: v1.5.0 hooks: - id: mypy args: From 7618a92fee002475b3bed9227944972d346db440 Mon Sep 17 00:00:00 2001 From: Erfan Alimohammadi Date: Wed, 16 Aug 2023 00:07:49 +0330 Subject: [PATCH 157/757] Remove a slash in path to save the file correctly on Linux (#8053) --- computer_vision/flip_augmentation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/computer_vision/flip_augmentation.py b/computer_vision/flip_augmentation.py index 93b4e3f6da79..77a8cbd7b14f 100644 --- a/computer_vision/flip_augmentation.py +++ b/computer_vision/flip_augmentation.py @@ -32,13 +32,13 @@ def main() -> None: letter_code = random_chars(32) file_name = paths[index].split(os.sep)[-1].rsplit(".", 1)[0] file_root = f"{OUTPUT_DIR}/{file_name}_FLIP_{letter_code}" - cv2.imwrite(f"/{file_root}.jpg", image, [cv2.IMWRITE_JPEG_QUALITY, 85]) + cv2.imwrite(f"{file_root}.jpg", image, [cv2.IMWRITE_JPEG_QUALITY, 85]) print(f"Success {index+1}/{len(new_images)} with {file_name}") annos_list = [] for anno in new_annos[index]: obj = f"{anno[0]} {anno[1]} {anno[2]} {anno[3]} {anno[4]}" annos_list.append(obj) - with open(f"/{file_root}.txt", "w") as outfile: + with open(f"{file_root}.txt", "w") as outfile: outfile.write("\n".join(line for line in annos_list)) From 490e645ed3b7ae50f0d7e23e047d088ba069ed56 Mon Sep 17 00:00:00 2001 From: Caeden Perelli-Harris Date: Tue, 15 Aug 2023 22:27:41 +0100 Subject: [PATCH 158/757] Fix minor typing errors in maths/ (#8959) * updating DIRECTORY.md * types(maths): Fix pylance issues in maths * reset(vsc): Reset settings changes * Update maths/jaccard_similarity.py Co-authored-by: Tianyi Zheng * revert(erosion_operation): Revert erosion_operation * test(jaccard_similarity): Add doctest to test alternative_union * types(newton_raphson): Add typehints to func bodies --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: Tianyi Zheng --- .../erosion_operation.py | 1 + digital_image_processing/rotation/rotation.py | 4 +- maths/average_median.py | 4 +- maths/euler_modified.py | 2 +- maths/gaussian_error_linear_unit.py | 4 +- maths/jaccard_similarity.py | 45 ++++++++++++------- maths/newton_raphson.py | 33 +++++++++----- maths/qr_decomposition.py | 2 +- maths/sigmoid.py | 2 +- maths/tanh.py | 4 +- 10 files changed, 65 insertions(+), 36 deletions(-) diff --git a/digital_image_processing/morphological_operations/erosion_operation.py b/digital_image_processing/morphological_operations/erosion_operation.py index c2cde2ea6990..c0e1ef847237 100644 --- a/digital_image_processing/morphological_operations/erosion_operation.py +++ b/digital_image_processing/morphological_operations/erosion_operation.py @@ -21,6 +21,7 @@ def rgb2gray(rgb: np.array) -> np.array: def gray2binary(gray: np.array) -> np.array: """ Return binary image from gray image + >>> gray2binary(np.array([[127, 255, 0]])) array([[False, True, False]]) >>> gray2binary(np.array([[0]])) diff --git a/digital_image_processing/rotation/rotation.py b/digital_image_processing/rotation/rotation.py index 958d16fafb91..0f5e36ddd5be 100644 --- a/digital_image_processing/rotation/rotation.py +++ b/digital_image_processing/rotation/rotation.py @@ -10,12 +10,12 @@ def get_rotation( ) -> np.ndarray: """ Get image rotation - :param img: np.array + :param img: np.ndarray :param pt1: 3x2 list :param pt2: 3x2 list :param rows: columns image shape :param cols: rows image shape - :return: np.array + :return: np.ndarray """ matrix = cv2.getAffineTransform(pt1, pt2) return cv2.warpAffine(img, matrix, (rows, cols)) diff --git a/maths/average_median.py b/maths/average_median.py index cd1ec1574893..f24e525736b3 100644 --- a/maths/average_median.py +++ b/maths/average_median.py @@ -19,7 +19,9 @@ def median(nums: list) -> int | float: Returns: Median. """ - sorted_list = sorted(nums) + # The sorted function returns list[SupportsRichComparisonT@sorted] + # which does not support `+` + sorted_list: list[int] = sorted(nums) length = len(sorted_list) mid_index = length >> 1 return ( diff --git a/maths/euler_modified.py b/maths/euler_modified.py index 14bddadf4c53..d02123e1e2fb 100644 --- a/maths/euler_modified.py +++ b/maths/euler_modified.py @@ -5,7 +5,7 @@ def euler_modified( ode_func: Callable, y0: float, x0: float, step_size: float, x_end: float -) -> np.array: +) -> np.ndarray: """ Calculate solution at each step to an ODE using Euler's Modified Method The Euler Method is straightforward to implement, but can't give accurate solutions. diff --git a/maths/gaussian_error_linear_unit.py b/maths/gaussian_error_linear_unit.py index 7b5f875143b9..18384bb6c864 100644 --- a/maths/gaussian_error_linear_unit.py +++ b/maths/gaussian_error_linear_unit.py @@ -13,7 +13,7 @@ import numpy as np -def sigmoid(vector: np.array) -> np.array: +def sigmoid(vector: np.ndarray) -> np.ndarray: """ Mathematical function sigmoid takes a vector x of K real numbers as input and returns 1/ (1 + e^-x). @@ -25,7 +25,7 @@ def sigmoid(vector: np.array) -> np.array: return 1 / (1 + np.exp(-vector)) -def gaussian_error_linear_unit(vector: np.array) -> np.array: +def gaussian_error_linear_unit(vector: np.ndarray) -> np.ndarray: """ Implements the Gaussian Error Linear Unit (GELU) function diff --git a/maths/jaccard_similarity.py b/maths/jaccard_similarity.py index 32054414c0c2..6b6243458fa8 100644 --- a/maths/jaccard_similarity.py +++ b/maths/jaccard_similarity.py @@ -14,7 +14,11 @@ """ -def jaccard_similarity(set_a, set_b, alternative_union=False): +def jaccard_similarity( + set_a: set[str] | list[str] | tuple[str], + set_b: set[str] | list[str] | tuple[str], + alternative_union=False, +): """ Finds the jaccard similarity between two sets. Essentially, its intersection over union. @@ -37,41 +41,52 @@ def jaccard_similarity(set_a, set_b, alternative_union=False): >>> set_b = {'c', 'd', 'e', 'f', 'h', 'i'} >>> jaccard_similarity(set_a, set_b) 0.375 - >>> jaccard_similarity(set_a, set_a) 1.0 - >>> jaccard_similarity(set_a, set_a, True) 0.5 - >>> set_a = ['a', 'b', 'c', 'd', 'e'] >>> set_b = ('c', 'd', 'e', 'f', 'h', 'i') >>> jaccard_similarity(set_a, set_b) 0.375 + >>> set_a = ('c', 'd', 'e', 'f', 'h', 'i') + >>> set_b = ['a', 'b', 'c', 'd', 'e'] + >>> jaccard_similarity(set_a, set_b) + 0.375 + >>> set_a = ('c', 'd', 'e', 'f', 'h', 'i') + >>> set_b = ['a', 'b', 'c', 'd'] + >>> jaccard_similarity(set_a, set_b, True) + 0.2 + >>> set_a = {'a', 'b'} + >>> set_b = ['c', 'd'] + >>> jaccard_similarity(set_a, set_b) + Traceback (most recent call last): + ... + ValueError: Set a and b must either both be sets or be either a list or a tuple. """ if isinstance(set_a, set) and isinstance(set_b, set): - intersection = len(set_a.intersection(set_b)) + intersection_length = len(set_a.intersection(set_b)) if alternative_union: - union = len(set_a) + len(set_b) + union_length = len(set_a) + len(set_b) else: - union = len(set_a.union(set_b)) + union_length = len(set_a.union(set_b)) - return intersection / union + return intersection_length / union_length - if isinstance(set_a, (list, tuple)) and isinstance(set_b, (list, tuple)): + elif isinstance(set_a, (list, tuple)) and isinstance(set_b, (list, tuple)): intersection = [element for element in set_a if element in set_b] if alternative_union: - union = len(set_a) + len(set_b) - return len(intersection) / union + return len(intersection) / (len(set_a) + len(set_b)) else: - union = set_a + [element for element in set_b if element not in set_a] + # Cast set_a to list because tuples cannot be mutated + union = list(set_a) + [element for element in set_b if element not in set_a] return len(intersection) / len(union) - - return len(intersection) / len(union) - return None + raise ValueError( + "Set a and b must either both be sets or be either a list or a tuple." + ) if __name__ == "__main__": diff --git a/maths/newton_raphson.py b/maths/newton_raphson.py index 2c9cd1de95b0..f6b227b5c9c1 100644 --- a/maths/newton_raphson.py +++ b/maths/newton_raphson.py @@ -1,16 +1,20 @@ """ - Author: P Shreyas Shetty - Implementation of Newton-Raphson method for solving equations of kind - f(x) = 0. It is an iterative method where solution is found by the expression - x[n+1] = x[n] + f(x[n])/f'(x[n]) - If no solution exists, then either the solution will not be found when iteration - limit is reached or the gradient f'(x[n]) approaches zero. In both cases, exception - is raised. If iteration limit is reached, try increasing maxiter. - """ +Author: P Shreyas Shetty +Implementation of Newton-Raphson method for solving equations of kind +f(x) = 0. It is an iterative method where solution is found by the expression + x[n+1] = x[n] + f(x[n])/f'(x[n]) +If no solution exists, then either the solution will not be found when iteration +limit is reached or the gradient f'(x[n]) approaches zero. In both cases, exception +is raised. If iteration limit is reached, try increasing maxiter. +""" + import math as m +from collections.abc import Callable + +DerivativeFunc = Callable[[float], float] -def calc_derivative(f, a, h=0.001): +def calc_derivative(f: DerivativeFunc, a: float, h: float = 0.001) -> float: """ Calculates derivative at point a for function f using finite difference method @@ -18,7 +22,14 @@ def calc_derivative(f, a, h=0.001): return (f(a + h) - f(a - h)) / (2 * h) -def newton_raphson(f, x0=0, maxiter=100, step=0.0001, maxerror=1e-6, logsteps=False): +def newton_raphson( + f: DerivativeFunc, + x0: float = 0, + maxiter: int = 100, + step: float = 0.0001, + maxerror: float = 1e-6, + logsteps: bool = False, +) -> tuple[float, float, list[float]]: a = x0 # set the initial guess steps = [a] error = abs(f(a)) @@ -36,7 +47,7 @@ def newton_raphson(f, x0=0, maxiter=100, step=0.0001, maxerror=1e-6, logsteps=Fa if logsteps: # If logstep is true, then log intermediate steps return a, error, steps - return a, error + return a, error, [] if __name__ == "__main__": diff --git a/maths/qr_decomposition.py b/maths/qr_decomposition.py index a8414fbece87..670b49206aa7 100644 --- a/maths/qr_decomposition.py +++ b/maths/qr_decomposition.py @@ -1,7 +1,7 @@ import numpy as np -def qr_householder(a): +def qr_householder(a: np.ndarray): """Return a QR-decomposition of the matrix A using Householder reflection. The QR-decomposition decomposes the matrix A of shape (m, n) into an diff --git a/maths/sigmoid.py b/maths/sigmoid.py index 147588e8871f..cb45bde2702c 100644 --- a/maths/sigmoid.py +++ b/maths/sigmoid.py @@ -11,7 +11,7 @@ import numpy as np -def sigmoid(vector: np.array) -> np.array: +def sigmoid(vector: np.ndarray) -> np.ndarray: """ Implements the sigmoid function diff --git a/maths/tanh.py b/maths/tanh.py index ddab3e1ab717..38a369d9118d 100644 --- a/maths/tanh.py +++ b/maths/tanh.py @@ -12,12 +12,12 @@ import numpy as np -def tangent_hyperbolic(vector: np.array) -> np.array: +def tangent_hyperbolic(vector: np.ndarray) -> np.ndarray: """ Implements the tanh function Parameters: - vector: np.array + vector: np.ndarray Returns: tanh (np.array): The input numpy array after applying tanh. From cecf1fdd529782d754e1aa4d6df099e391003c76 Mon Sep 17 00:00:00 2001 From: Juyoung Kim <61103343+JadeKim042386@users.noreply.github.com> Date: Wed, 16 Aug 2023 07:52:51 +0900 Subject: [PATCH 159/757] Fix greedy_best_first (#8775) * fix: typo #8770 * refactor: delete unnecessary continue * add test grids * fix: add \_\_eq\_\_ in Node class #8770 * fix: delete unnecessary code - node in self.open_nodes is always better node #8770 * fix: docstring * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix: docstring max length * refactor: get the successors using a list comprehension * Apply suggestions from code review --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Tianyi Zheng --- graphs/greedy_best_first.py | 120 ++++++++++++++++++++---------------- 1 file changed, 67 insertions(+), 53 deletions(-) diff --git a/graphs/greedy_best_first.py b/graphs/greedy_best_first.py index 35f7ca9feeef..bb3160047e34 100644 --- a/graphs/greedy_best_first.py +++ b/graphs/greedy_best_first.py @@ -6,14 +6,32 @@ Path = list[tuple[int, int]] -grid = [ - [0, 0, 0, 0, 0, 0, 0], - [0, 1, 0, 0, 0, 0, 0], # 0 are free path whereas 1's are obstacles - [0, 0, 0, 0, 0, 0, 0], - [0, 0, 1, 0, 0, 0, 0], - [1, 0, 1, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 1, 0, 0], +# 0's are free path whereas 1's are obstacles +TEST_GRIDS = [ + [ + [0, 0, 0, 0, 0, 0, 0], + [0, 1, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0], + [0, 0, 1, 0, 0, 0, 0], + [1, 0, 1, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 1, 0, 0], + ], + [ + [0, 0, 0, 1, 1, 0, 0], + [0, 0, 0, 0, 1, 0, 1], + [0, 0, 0, 1, 1, 0, 0], + [0, 1, 0, 0, 1, 0, 0], + [1, 0, 0, 1, 1, 0, 1], + [0, 0, 0, 0, 0, 0, 0], + ], + [ + [0, 0, 1, 0, 0], + [0, 1, 0, 0, 0], + [0, 0, 1, 0, 1], + [1, 0, 0, 1, 1], + [0, 0, 0, 0, 0], + ], ] delta = ([-1, 0], [0, -1], [1, 0], [0, 1]) # up, left, down, right @@ -65,10 +83,14 @@ def calculate_heuristic(self) -> float: def __lt__(self, other) -> bool: return self.f_cost < other.f_cost + def __eq__(self, other) -> bool: + return self.pos == other.pos + class GreedyBestFirst: """ - >>> gbf = GreedyBestFirst((0, 0), (len(grid) - 1, len(grid[0]) - 1)) + >>> grid = TEST_GRIDS[2] + >>> gbf = GreedyBestFirst(grid, (0, 0), (len(grid) - 1, len(grid[0]) - 1)) >>> [x.pos for x in gbf.get_successors(gbf.start)] [(1, 0), (0, 1)] >>> (gbf.start.pos_y + delta[3][0], gbf.start.pos_x + delta[3][1]) @@ -78,11 +100,14 @@ class GreedyBestFirst: >>> gbf.retrace_path(gbf.start) [(0, 0)] >>> gbf.search() # doctest: +NORMALIZE_WHITESPACE - [(0, 0), (1, 0), (2, 0), (3, 0), (3, 1), (4, 1), (5, 1), (6, 1), - (6, 2), (6, 3), (5, 3), (5, 4), (5, 5), (6, 5), (6, 6)] + [(0, 0), (1, 0), (2, 0), (2, 1), (3, 1), (4, 1), (4, 2), (4, 3), + (4, 4)] """ - def __init__(self, start: tuple[int, int], goal: tuple[int, int]): + def __init__( + self, grid: list[list[int]], start: tuple[int, int], goal: tuple[int, int] + ): + self.grid = grid self.start = Node(start[1], start[0], goal[1], goal[0], 0, None) self.target = Node(goal[1], goal[0], goal[1], goal[0], 99999, None) @@ -114,14 +139,6 @@ def search(self) -> Path | None: if child_node not in self.open_nodes: self.open_nodes.append(child_node) - else: - # retrieve the best current path - better_node = self.open_nodes.pop(self.open_nodes.index(child_node)) - - if child_node.g_cost < better_node.g_cost: - self.open_nodes.append(child_node) - else: - self.open_nodes.append(better_node) if not self.reached: return [self.start.pos] @@ -131,28 +148,22 @@ def get_successors(self, parent: Node) -> list[Node]: """ Returns a list of successors (both in the grid and free spaces) """ - successors = [] - for action in delta: - pos_x = parent.pos_x + action[1] - pos_y = parent.pos_y + action[0] - - if not (0 <= pos_x <= len(grid[0]) - 1 and 0 <= pos_y <= len(grid) - 1): - continue - - if grid[pos_y][pos_x] != 0: - continue - - successors.append( - Node( - pos_x, - pos_y, - self.target.pos_y, - self.target.pos_x, - parent.g_cost + 1, - parent, - ) + return [ + Node( + pos_x, + pos_y, + self.target.pos_x, + self.target.pos_y, + parent.g_cost + 1, + parent, + ) + for action in delta + if ( + 0 <= (pos_x := parent.pos_x + action[1]) < len(self.grid[0]) + and 0 <= (pos_y := parent.pos_y + action[0]) < len(self.grid) + and self.grid[pos_y][pos_x] == 0 ) - return successors + ] def retrace_path(self, node: Node | None) -> Path: """ @@ -168,18 +179,21 @@ def retrace_path(self, node: Node | None) -> Path: if __name__ == "__main__": - init = (0, 0) - goal = (len(grid) - 1, len(grid[0]) - 1) - for elem in grid: - print(elem) - - print("------") - - greedy_bf = GreedyBestFirst(init, goal) - path = greedy_bf.search() - if path: - for pos_x, pos_y in path: - grid[pos_x][pos_y] = 2 + for idx, grid in enumerate(TEST_GRIDS): + print(f"==grid-{idx + 1}==") + init = (0, 0) + goal = (len(grid) - 1, len(grid[0]) - 1) for elem in grid: print(elem) + + print("------") + + greedy_bf = GreedyBestFirst(grid, init, goal) + path = greedy_bf.search() + if path: + for pos_x, pos_y in path: + grid[pos_x][pos_y] = 2 + + for elem in grid: + print(elem) From efaf526737a83815a609a00fd59370f25f6d2e09 Mon Sep 17 00:00:00 2001 From: isidroas Date: Wed, 16 Aug 2023 01:04:53 +0200 Subject: [PATCH 160/757] BST and RSA doctest (#8693) * rsa key doctest * move doctest to module docstring * all tests to doctest * moved is_right to property * is right test * fixed rsa doctest import * Test error when deleting non-existing element * fixing ruff EM102 * convert property 'is_right' to one-liner Also use 'is' instead of '==' Co-authored-by: Tianyi Zheng * child instead of children Co-authored-by: Tianyi Zheng * remove type hint * Update data_structures/binary_tree/binary_search_tree.py --------- Co-authored-by: Tianyi Zheng --- ciphers/rsa_key_generator.py | 25 +-- .../binary_tree/binary_search_tree.py | 155 ++++++++++-------- 2 files changed, 98 insertions(+), 82 deletions(-) diff --git a/ciphers/rsa_key_generator.py b/ciphers/rsa_key_generator.py index 2573ed01387b..eedc7336804a 100644 --- a/ciphers/rsa_key_generator.py +++ b/ciphers/rsa_key_generator.py @@ -2,8 +2,7 @@ import random import sys -from . import cryptomath_module as cryptoMath # noqa: N812 -from . import rabin_miller as rabinMiller # noqa: N812 +from . import cryptomath_module, rabin_miller def main() -> None: @@ -13,20 +12,26 @@ def main() -> None: def generate_key(key_size: int) -> tuple[tuple[int, int], tuple[int, int]]: - print("Generating prime p...") - p = rabinMiller.generate_large_prime(key_size) - print("Generating prime q...") - q = rabinMiller.generate_large_prime(key_size) + """ + >>> random.seed(0) # for repeatability + >>> public_key, private_key = generate_key(8) + >>> public_key + (26569, 239) + >>> private_key + (26569, 2855) + """ + p = rabin_miller.generate_large_prime(key_size) + q = rabin_miller.generate_large_prime(key_size) n = p * q - print("Generating e that is relatively prime to (p - 1) * (q - 1)...") + # Generate e that is relatively prime to (p - 1) * (q - 1) while True: e = random.randrange(2 ** (key_size - 1), 2 ** (key_size)) - if cryptoMath.gcd(e, (p - 1) * (q - 1)) == 1: + if cryptomath_module.gcd(e, (p - 1) * (q - 1)) == 1: break - print("Calculating d that is mod inverse of e...") - d = cryptoMath.find_mod_inverse(e, (p - 1) * (q - 1)) + # Calculate d that is mod inverse of e + d = cryptomath_module.find_mod_inverse(e, (p - 1) * (q - 1)) public_key = (n, e) private_key = (n, d) diff --git a/data_structures/binary_tree/binary_search_tree.py b/data_structures/binary_tree/binary_search_tree.py index c72195424c7c..a706d21e3bb2 100644 --- a/data_structures/binary_tree/binary_search_tree.py +++ b/data_structures/binary_tree/binary_search_tree.py @@ -1,5 +1,62 @@ -""" +r""" A binary search Tree + +Example + 8 + / \ + 3 10 + / \ \ + 1 6 14 + / \ / + 4 7 13 + +>>> t = BinarySearchTree() +>>> t.insert(8, 3, 6, 1, 10, 14, 13, 4, 7) +>>> print(" ".join(repr(i.value) for i in t.traversal_tree())) +8 3 1 6 4 7 10 14 13 +>>> print(" ".join(repr(i.value) for i in t.traversal_tree(postorder))) +1 4 7 6 3 13 14 10 8 +>>> t.remove(20) +Traceback (most recent call last): + ... +ValueError: Value 20 not found +>>> BinarySearchTree().search(6) +Traceback (most recent call last): + ... +IndexError: Warning: Tree is empty! please use another. + +Other example: + +>>> testlist = (8, 3, 6, 1, 10, 14, 13, 4, 7) +>>> t = BinarySearchTree() +>>> for i in testlist: +... t.insert(i) + +Prints all the elements of the list in order traversal +>>> print(t) +{'8': ({'3': (1, {'6': (4, 7)})}, {'10': (None, {'14': (13, None)})})} + +Test existence +>>> t.search(6) is not None +True +>>> t.search(-1) is not None +False + +>>> t.search(6).is_right +True +>>> t.search(1).is_right +False + +>>> t.get_max().value +14 +>>> t.get_min().value +1 +>>> t.empty() +False +>>> for i in testlist: +... t.remove(i) +>>> t.empty() +True """ from collections.abc import Iterable @@ -20,6 +77,10 @@ def __repr__(self) -> str: return str(self.value) return pformat({f"{self.value}": (self.left, self.right)}, indent=1) + @property + def is_right(self) -> bool: + return self.parent is not None and self is self.parent.right + class BinarySearchTree: def __init__(self, root: Node | None = None): @@ -35,18 +96,13 @@ def __reassign_nodes(self, node: Node, new_children: Node | None) -> None: if new_children is not None: # reset its kids new_children.parent = node.parent if node.parent is not None: # reset its parent - if self.is_right(node): # If it is the right children + if node.is_right: # If it is the right child node.parent.right = new_children else: node.parent.left = new_children else: self.root = new_children - def is_right(self, node: Node) -> bool: - if node.parent and node.parent.right: - return node == node.parent.right - return False - def empty(self) -> bool: return self.root is None @@ -119,22 +175,26 @@ def get_min(self, node: Node | None = None) -> Node | None: return node def remove(self, value: int) -> None: - node = self.search(value) # Look for the node with that label - if node is not None: - if node.left is None and node.right is None: # If it has no children - self.__reassign_nodes(node, None) - elif node.left is None: # Has only right children - self.__reassign_nodes(node, node.right) - elif node.right is None: # Has only left children - self.__reassign_nodes(node, node.left) - else: - tmp_node = self.get_max( - node.left - ) # Gets the max value of the left branch - self.remove(tmp_node.value) # type: ignore - node.value = ( - tmp_node.value # type: ignore - ) # Assigns the value to the node to delete and keep tree structure + # Look for the node with that label + node = self.search(value) + if node is None: + msg = f"Value {value} not found" + raise ValueError(msg) + + if node.left is None and node.right is None: # If it has no children + self.__reassign_nodes(node, None) + elif node.left is None: # Has only right children + self.__reassign_nodes(node, node.right) + elif node.right is None: # Has only left children + self.__reassign_nodes(node, node.left) + else: + predecessor = self.get_max( + node.left + ) # Gets the max value of the left branch + self.remove(predecessor.value) # type: ignore + node.value = ( + predecessor.value # type: ignore + ) # Assigns the value to the node to delete and keep tree structure def preorder_traverse(self, node: Node | None) -> Iterable: if node is not None: @@ -177,55 +237,6 @@ def postorder(curr_node: Node | None) -> list[Node]: return node_list -def binary_search_tree() -> None: - r""" - Example - 8 - / \ - 3 10 - / \ \ - 1 6 14 - / \ / - 4 7 13 - - >>> t = BinarySearchTree() - >>> t.insert(8, 3, 6, 1, 10, 14, 13, 4, 7) - >>> print(" ".join(repr(i.value) for i in t.traversal_tree())) - 8 3 1 6 4 7 10 14 13 - >>> print(" ".join(repr(i.value) for i in t.traversal_tree(postorder))) - 1 4 7 6 3 13 14 10 8 - >>> BinarySearchTree().search(6) - Traceback (most recent call last): - ... - IndexError: Warning: Tree is empty! please use another. - """ - testlist = (8, 3, 6, 1, 10, 14, 13, 4, 7) - t = BinarySearchTree() - for i in testlist: - t.insert(i) - - # Prints all the elements of the list in order traversal - print(t) - - if t.search(6) is not None: - print("The value 6 exists") - else: - print("The value 6 doesn't exist") - - if t.search(-1) is not None: - print("The value -1 exists") - else: - print("The value -1 doesn't exist") - - if not t.empty(): - print("Max Value: ", t.get_max().value) # type: ignore - print("Min Value: ", t.get_min().value) # type: ignore - - for i in testlist: - t.remove(i) - print(t) - - if __name__ == "__main__": import doctest From f66568e981edf5e384fe28a357daee3e13f16de9 Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Wed, 16 Aug 2023 02:10:22 +0300 Subject: [PATCH 161/757] Reduce the complexity of boolean_algebra/quine_mc_cluskey.py (#8604) * Reduce the complexity of boolean_algebra/quine_mc_cluskey.py * updating DIRECTORY.md * Fix * Fix review issues * Fix * Fix review issues --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- boolean_algebra/quine_mc_cluskey.py | 49 ++++++++++++----------------- 1 file changed, 20 insertions(+), 29 deletions(-) diff --git a/boolean_algebra/quine_mc_cluskey.py b/boolean_algebra/quine_mc_cluskey.py index 6788dfb28ba1..8e22e66726d4 100644 --- a/boolean_algebra/quine_mc_cluskey.py +++ b/boolean_algebra/quine_mc_cluskey.py @@ -74,10 +74,7 @@ def is_for_table(string1: str, string2: str, count: int) -> bool: """ list1 = list(string1) list2 = list(string2) - count_n = 0 - for i in range(len(list1)): - if list1[i] != list2[i]: - count_n += 1 + count_n = sum(item1 != item2 for item1, item2 in zip(list1, list2)) return count_n == count @@ -92,40 +89,34 @@ def selection(chart: list[list[int]], prime_implicants: list[str]) -> list[str]: 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 + count = sum(row[i] == 1 for row in chart) if count == 1: + rem = max(j for j, row in enumerate(chart) if row[i] == 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]) + for i, item in enumerate(select): + if item != 1: + continue + for j in range(len(chart[0])): + if chart[i][j] != 1: + continue + for row in chart: + row[j] = 0 + temp.append(prime_implicants[i]) while True: - 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 + counts = [chart[i].count(1) for i in range(len(chart))] + max_n = max(counts) + rem = counts.index(max_n) 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 + for j in range(len(chart[0])): + if chart[rem][j] != 1: + continue + for i in range(len(chart)): + chart[i][j] = 0 def prime_implicant_chart( From bfed2fb7883fb7c472cd09afea1aad4e3f87d71b Mon Sep 17 00:00:00 2001 From: Saksham1970 <45041294+Saksham1970@users.noreply.github.com> Date: Wed, 16 Aug 2023 12:54:12 +0530 Subject: [PATCH 162/757] Added Continued fractions (#6846) * updating DIRECTORY.md * added continued fractions * updating DIRECTORY.md * Update maths/continued_fraction.py Co-authored-by: Caeden Perelli-Harris * Update maths/continued_fraction.py Co-authored-by: Caeden Perelli-Harris --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: Caeden Perelli-Harris Co-authored-by: Tianyi Zheng --- DIRECTORY.md | 1 + maths/continued_fraction.py | 51 +++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 maths/continued_fraction.py diff --git a/DIRECTORY.md b/DIRECTORY.md index be5fa3584a58..8d1567465fbc 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -555,6 +555,7 @@ * [Chudnovsky Algorithm](maths/chudnovsky_algorithm.py) * [Collatz Sequence](maths/collatz_sequence.py) * [Combinations](maths/combinations.py) + * [Continued Fraction](maths/continued_fraction.py) * [Decimal Isolate](maths/decimal_isolate.py) * [Decimal To Fraction](maths/decimal_to_fraction.py) * [Dodecahedron](maths/dodecahedron.py) diff --git a/maths/continued_fraction.py b/maths/continued_fraction.py new file mode 100644 index 000000000000..25ff649db77a --- /dev/null +++ b/maths/continued_fraction.py @@ -0,0 +1,51 @@ +""" +Finding the continuous fraction for a rational number using python + +https://en.wikipedia.org/wiki/Continued_fraction +""" + + +from fractions import Fraction + + +def continued_fraction(num: Fraction) -> list[int]: + """ + :param num: + Fraction of the number whose continued fractions to be found. + Use Fraction(str(number)) for more accurate results due to + float inaccuracies. + + :return: + The continued fraction of rational number. + It is the all commas in the (n + 1)-tuple notation. + + >>> continued_fraction(Fraction(2)) + [2] + >>> continued_fraction(Fraction("3.245")) + [3, 4, 12, 4] + >>> continued_fraction(Fraction("2.25")) + [2, 4] + >>> continued_fraction(1/Fraction("2.25")) + [0, 2, 4] + >>> continued_fraction(Fraction("415/93")) + [4, 2, 6, 7] + """ + numerator, denominator = num.as_integer_ratio() + continued_fraction_list: list[int] = [] + while True: + integer_part = int(numerator / denominator) + continued_fraction_list.append(integer_part) + numerator -= integer_part * denominator + if numerator == 0: + break + numerator, denominator = denominator, numerator + + return continued_fraction_list + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + + print("Continued Fraction of 0.84375 is: ", continued_fraction(Fraction("0.84375"))) From 5c276a8377b9f4139dac9cfff83fd47b88511a40 Mon Sep 17 00:00:00 2001 From: homsim <103424895+homsim@users.noreply.github.com> Date: Wed, 16 Aug 2023 10:07:50 +0200 Subject: [PATCH 163/757] Quick fix: fig.canvas.set_window_title deprecated (#8961) Co-authored-by: homsim --- physics/n_body_simulation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/physics/n_body_simulation.py b/physics/n_body_simulation.py index 2b701283f166..46330844df61 100644 --- a/physics/n_body_simulation.py +++ b/physics/n_body_simulation.py @@ -226,7 +226,7 @@ def plot( No doctest provided since this function does not have a return value. """ fig = plt.figure() - fig.canvas.set_window_title(title) + fig.canvas.manager.set_window_title(title) ax = plt.axes( xlim=(x_start, x_end), ylim=(y_start, y_end) ) # Set section to be plotted From beb43517c3552b72b9c8fc1710f681b0180418ec Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Wed, 16 Aug 2023 04:36:10 -0700 Subject: [PATCH 164/757] Fix `mypy` errors in `maths/gaussian_error_linear_unit.py` (#8610) * updating DIRECTORY.md * Fix mypy errors in gaussian_error_linear_unit.py * updating DIRECTORY.md * updating DIRECTORY.md --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- maths/gaussian_error_linear_unit.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/maths/gaussian_error_linear_unit.py b/maths/gaussian_error_linear_unit.py index 18384bb6c864..b3cbd7810716 100644 --- a/maths/gaussian_error_linear_unit.py +++ b/maths/gaussian_error_linear_unit.py @@ -30,12 +30,10 @@ def gaussian_error_linear_unit(vector: np.ndarray) -> np.ndarray: Implements the Gaussian Error Linear Unit (GELU) function Parameters: - vector (np.array): A numpy array of shape (1,n) - consisting of real values + vector (np.ndarray): A numpy array of shape (1, n) consisting of real values Returns: - gelu_vec (np.array): The input numpy array, after applying - gelu. + gelu_vec (np.ndarray): The input numpy array, after applying gelu Examples: >>> gaussian_error_linear_unit(np.array([-1.0, 1.0, 2.0])) From fd7cc4cf8e731c16a5dd2cf30c4ddb0dd017d59e Mon Sep 17 00:00:00 2001 From: Caeden Perelli-Harris Date: Thu, 17 Aug 2023 02:21:00 +0100 Subject: [PATCH 165/757] Rename norgate to nor_gate to keep consistency (#8968) * refactor(boolean-algebra): Rename norgate to nor_gate * updating DIRECTORY.md --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 2 +- boolean_algebra/{norgate.py => nor_gate.py} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename boolean_algebra/{norgate.py => nor_gate.py} (100%) diff --git a/DIRECTORY.md b/DIRECTORY.md index 8d1567465fbc..d4a2bb48511a 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -62,7 +62,7 @@ ## Boolean Algebra * [And Gate](boolean_algebra/and_gate.py) * [Nand Gate](boolean_algebra/nand_gate.py) - * [Norgate](boolean_algebra/norgate.py) + * [Nor Gate](boolean_algebra/nor_gate.py) * [Not Gate](boolean_algebra/not_gate.py) * [Or Gate](boolean_algebra/or_gate.py) * [Quine Mc Cluskey](boolean_algebra/quine_mc_cluskey.py) diff --git a/boolean_algebra/norgate.py b/boolean_algebra/nor_gate.py similarity index 100% rename from boolean_algebra/norgate.py rename to boolean_algebra/nor_gate.py From f6b12420ce2a16ddf55c5226ea6f188936af33ad Mon Sep 17 00:00:00 2001 From: Kausthub Kannan <99611070+kausthub-kannan@users.noreply.github.com> Date: Thu, 17 Aug 2023 06:52:15 +0530 Subject: [PATCH 166/757] Added Leaky ReLU Activation Function (#8962) * Added Leaky ReLU activation function * Added Leaky ReLU activation function * Added Leaky ReLU activation function * Formatting and spelling fixes done --- .../leaky_rectified_linear_unit.py | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 neural_network/activation_functions/leaky_rectified_linear_unit.py diff --git a/neural_network/activation_functions/leaky_rectified_linear_unit.py b/neural_network/activation_functions/leaky_rectified_linear_unit.py new file mode 100644 index 000000000000..019086fd9821 --- /dev/null +++ b/neural_network/activation_functions/leaky_rectified_linear_unit.py @@ -0,0 +1,39 @@ +""" +Leaky Rectified Linear Unit (Leaky ReLU) + +Use Case: Leaky ReLU addresses the problem of the vanishing gradient. +For more detailed information, you can refer to the following link: +https://en.wikipedia.org/wiki/Rectifier_(neural_networks)#Leaky_ReLU +""" + +import numpy as np + + +def leaky_rectified_linear_unit(vector: np.ndarray, alpha: float) -> np.ndarray: + """ + Implements the LeakyReLU activation function. + + Parameters: + vector (np.ndarray): The input array for LeakyReLU activation. + alpha (float): The slope for negative values. + + Returns: + np.ndarray: The input array after applying the LeakyReLU activation. + + Formula: f(x) = x if x > 0 else f(x) = alpha * x + + Examples: + >>> leaky_rectified_linear_unit(vector=np.array([2.3,0.6,-2,-3.8]), alpha=0.3) + array([ 2.3 , 0.6 , -0.6 , -1.14]) + + >>> leaky_rectified_linear_unit(np.array([-9.2, -0.3, 0.45, -4.56]), alpha=0.067) + array([-0.6164 , -0.0201 , 0.45 , -0.30552]) + + """ + return np.where(vector > 0, vector, alpha * vector) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From a207187ddb368edb121153d4f6e190fcfb857427 Mon Sep 17 00:00:00 2001 From: Ilkin Mengusoglu <113149540+imengus@users.noreply.github.com> Date: Thu, 17 Aug 2023 22:34:53 +0100 Subject: [PATCH 167/757] Fix simplex.py (#8843) * changes to accommodate special case * changed n_slack calculation method * fix precommit typehints * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * n_art_vars inputs * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix: docstrings and typehints * fix: doctest issues when running code * additional check and doctests * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix ruff * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix whitespace --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- linear_programming/simplex.py | 229 +++++++++++++++++++--------------- 1 file changed, 128 insertions(+), 101 deletions(-) diff --git a/linear_programming/simplex.py b/linear_programming/simplex.py index ba64add40b5f..bbc97d8e22bf 100644 --- a/linear_programming/simplex.py +++ b/linear_programming/simplex.py @@ -20,40 +20,60 @@ class Tableau: """Operate on simplex tableaus - >>> t = Tableau(np.array([[-1,-1,0,0,-1],[1,3,1,0,4],[3,1,0,1,4.]]), 2) + >>> Tableau(np.array([[-1,-1,0,0,1],[1,3,1,0,4],[3,1,0,1,4]]), 2, 2) + Traceback (most recent call last): + ... + TypeError: Tableau must have type float64 + + >>> Tableau(np.array([[-1,-1,0,0,-1],[1,3,1,0,4],[3,1,0,1,4.]]), 2, 2) Traceback (most recent call last): ... ValueError: RHS must be > 0 + + >>> Tableau(np.array([[-1,-1,0,0,1],[1,3,1,0,4],[3,1,0,1,4.]]), -2, 2) + Traceback (most recent call last): + ... + ValueError: number of (artificial) variables must be a natural number """ - def __init__(self, tableau: np.ndarray, n_vars: int) -> None: + # Max iteration number to prevent cycling + maxiter = 100 + + def __init__( + self, tableau: np.ndarray, n_vars: int, n_artificial_vars: int + ) -> None: + if tableau.dtype != "float64": + raise TypeError("Tableau must have type float64") + # Check if RHS is negative - if np.any(tableau[:, -1], where=tableau[:, -1] < 0): + if not (tableau[:, -1] >= 0).all(): raise ValueError("RHS must be > 0") + if n_vars < 2 or n_artificial_vars < 0: + raise ValueError( + "number of (artificial) variables must be a natural number" + ) + self.tableau = tableau - self.n_rows, _ = tableau.shape + self.n_rows, n_cols = tableau.shape # Number of decision variables x1, x2, x3... - self.n_vars = n_vars - - # Number of artificial variables to be minimised - self.n_art_vars = len(np.where(tableau[self.n_vars : -1] == -1)[0]) + self.n_vars, self.n_artificial_vars = n_vars, n_artificial_vars # 2 if there are >= or == constraints (nonstandard), 1 otherwise (std) - self.n_stages = (self.n_art_vars > 0) + 1 + self.n_stages = (self.n_artificial_vars > 0) + 1 # Number of slack variables added to make inequalities into equalities - self.n_slack = self.n_rows - self.n_stages + self.n_slack = n_cols - self.n_vars - self.n_artificial_vars - 1 # Objectives for each stage self.objectives = ["max"] # In two stage simplex, first minimise then maximise - if self.n_art_vars: + if self.n_artificial_vars: self.objectives.append("min") - self.col_titles = [""] + self.col_titles = self.generate_col_titles() # Index of current pivot row and column self.row_idx = None @@ -62,48 +82,39 @@ def __init__(self, tableau: np.ndarray, n_vars: int) -> None: # Does objective row only contain (non)-negative values? self.stop_iter = False - @staticmethod - def generate_col_titles(*args: int) -> list[str]: + def generate_col_titles(self) -> list[str]: """Generate column titles for tableau of specific dimensions - >>> Tableau.generate_col_titles(2, 3, 1) - ['x1', 'x2', 's1', 's2', 's3', 'a1', 'RHS'] - - >>> Tableau.generate_col_titles() - Traceback (most recent call last): - ... - ValueError: Must provide n_vars, n_slack, and n_art_vars - >>> Tableau.generate_col_titles(-2, 3, 1) - Traceback (most recent call last): - ... - ValueError: All arguments must be non-negative integers - """ - if len(args) != 3: - raise ValueError("Must provide n_vars, n_slack, and n_art_vars") + >>> Tableau(np.array([[-1,-1,0,0,1],[1,3,1,0,4],[3,1,0,1,4.]]), + ... 2, 0).generate_col_titles() + ['x1', 'x2', 's1', 's2', 'RHS'] - if not all(x >= 0 and isinstance(x, int) for x in args): - raise ValueError("All arguments must be non-negative integers") + >>> Tableau(np.array([[-1,-1,0,0,1],[1,3,1,0,4],[3,1,0,1,4.]]), + ... 2, 2).generate_col_titles() + ['x1', 'x2', 'RHS'] + """ + args = (self.n_vars, self.n_slack) - # decision | slack | artificial - string_starts = ["x", "s", "a"] + # decision | slack + string_starts = ["x", "s"] titles = [] - for i in range(3): + for i in range(2): for j in range(args[i]): titles.append(string_starts[i] + str(j + 1)) titles.append("RHS") return titles - def find_pivot(self, tableau: np.ndarray) -> tuple[Any, Any]: + def find_pivot(self) -> tuple[Any, Any]: """Finds the pivot row and column. - >>> t = Tableau(np.array([[-2,1,0,0,0], [3,1,1,0,6], [1,2,0,1,7.]]), 2) - >>> t.find_pivot(t.tableau) + >>> Tableau(np.array([[-2,1,0,0,0], [3,1,1,0,6], [1,2,0,1,7.]]), + ... 2, 0).find_pivot() (1, 0) """ objective = self.objectives[-1] # Find entries of highest magnitude in objective rows sign = (objective == "min") - (objective == "max") - col_idx = np.argmax(sign * tableau[0, : self.n_vars]) + col_idx = np.argmax(sign * self.tableau[0, :-1]) # Choice is only valid if below 0 for maximise, and above for minimise if sign * self.tableau[0, col_idx] <= 0: @@ -117,15 +128,15 @@ def find_pivot(self, tableau: np.ndarray) -> tuple[Any, Any]: s = slice(self.n_stages, self.n_rows) # RHS - dividend = tableau[s, -1] + dividend = self.tableau[s, -1] # Elements of pivot column within slice - divisor = tableau[s, col_idx] + divisor = self.tableau[s, col_idx] # Array filled with nans nans = np.full(self.n_rows - self.n_stages, np.nan) - # If element in pivot column is greater than zeron_stages, return + # If element in pivot column is greater than zero, return # quotient or nan otherwise quotients = np.divide(dividend, divisor, out=nans, where=divisor > 0) @@ -134,18 +145,18 @@ def find_pivot(self, tableau: np.ndarray) -> tuple[Any, Any]: row_idx = np.nanargmin(quotients) + self.n_stages return row_idx, col_idx - def pivot(self, tableau: np.ndarray, row_idx: int, col_idx: int) -> np.ndarray: + def pivot(self, row_idx: int, col_idx: int) -> np.ndarray: """Pivots on value on the intersection of pivot row and column. - >>> t = Tableau(np.array([[-2,-3,0,0,0],[1,3,1,0,4],[3,1,0,1,4.]]), 2) - >>> t.pivot(t.tableau, 1, 0).tolist() + >>> Tableau(np.array([[-2,-3,0,0,0],[1,3,1,0,4],[3,1,0,1,4.]]), + ... 2, 2).pivot(1, 0).tolist() ... # doctest: +NORMALIZE_WHITESPACE [[0.0, 3.0, 2.0, 0.0, 8.0], [1.0, 3.0, 1.0, 0.0, 4.0], [0.0, -8.0, -3.0, 1.0, -8.0]] """ # Avoid changes to original tableau - piv_row = tableau[row_idx].copy() + piv_row = self.tableau[row_idx].copy() piv_val = piv_row[col_idx] @@ -153,48 +164,47 @@ def pivot(self, tableau: np.ndarray, row_idx: int, col_idx: int) -> np.ndarray: piv_row *= 1 / piv_val # Variable in pivot column becomes basic, ie the only non-zero entry - for idx, coeff in enumerate(tableau[:, col_idx]): - tableau[idx] += -coeff * piv_row - tableau[row_idx] = piv_row - return tableau + for idx, coeff in enumerate(self.tableau[:, col_idx]): + self.tableau[idx] += -coeff * piv_row + self.tableau[row_idx] = piv_row + return self.tableau - def change_stage(self, tableau: np.ndarray) -> np.ndarray: + def change_stage(self) -> np.ndarray: """Exits first phase of the two-stage method by deleting artificial rows and columns, or completes the algorithm if exiting the standard case. - >>> t = Tableau(np.array([ + >>> Tableau(np.array([ ... [3, 3, -1, -1, 0, 0, 4], ... [2, 1, 0, 0, 0, 0, 0.], ... [1, 2, -1, 0, 1, 0, 2], ... [2, 1, 0, -1, 0, 1, 2] - ... ]), 2) - >>> t.change_stage(t.tableau).tolist() + ... ]), 2, 2).change_stage().tolist() ... # doctest: +NORMALIZE_WHITESPACE - [[2.0, 1.0, 0.0, 0.0, 0.0, 0.0], - [1.0, 2.0, -1.0, 0.0, 1.0, 2.0], - [2.0, 1.0, 0.0, -1.0, 0.0, 2.0]] + [[2.0, 1.0, 0.0, 0.0, 0.0], + [1.0, 2.0, -1.0, 0.0, 2.0], + [2.0, 1.0, 0.0, -1.0, 2.0]] """ # Objective of original objective row remains self.objectives.pop() if not self.objectives: - return tableau + return self.tableau # Slice containing ids for artificial columns - s = slice(-self.n_art_vars - 1, -1) + s = slice(-self.n_artificial_vars - 1, -1) # Delete the artificial variable columns - tableau = np.delete(tableau, s, axis=1) + self.tableau = np.delete(self.tableau, s, axis=1) # Delete the objective row of the first stage - tableau = np.delete(tableau, 0, axis=0) + self.tableau = np.delete(self.tableau, 0, axis=0) self.n_stages = 1 self.n_rows -= 1 - self.n_art_vars = 0 + self.n_artificial_vars = 0 self.stop_iter = False - return tableau + return self.tableau def run_simplex(self) -> dict[Any, Any]: """Operate on tableau until objective function cannot be @@ -205,15 +215,29 @@ def run_simplex(self) -> dict[Any, Any]: ST: x1 + 3x2 <= 4 3x1 + x2 <= 4 >>> Tableau(np.array([[-1,-1,0,0,0],[1,3,1,0,4],[3,1,0,1,4.]]), - ... 2).run_simplex() + ... 2, 0).run_simplex() {'P': 2.0, 'x1': 1.0, 'x2': 1.0} + # Standard linear program with 3 variables: + Max: 3x1 + x2 + 3x3 + ST: 2x1 + x2 + x3 ≤ 2 + x1 + 2x2 + 3x3 ≤ 5 + 2x1 + 2x2 + x3 ≤ 6 + >>> Tableau(np.array([ + ... [-3,-1,-3,0,0,0,0], + ... [2,1,1,1,0,0,2], + ... [1,2,3,0,1,0,5], + ... [2,2,1,0,0,1,6.] + ... ]),3,0).run_simplex() # doctest: +ELLIPSIS + {'P': 5.4, 'x1': 0.199..., 'x3': 1.6} + + # Optimal tableau input: >>> Tableau(np.array([ ... [0, 0, 0.25, 0.25, 2], ... [0, 1, 0.375, -0.125, 1], ... [1, 0, -0.125, 0.375, 1] - ... ]), 2).run_simplex() + ... ]), 2, 0).run_simplex() {'P': 2.0, 'x1': 1.0, 'x2': 1.0} # Non-standard: >= constraints @@ -227,7 +251,7 @@ def run_simplex(self) -> dict[Any, Any]: ... [1, 1, 1, 1, 0, 0, 0, 0, 40], ... [2, 1, -1, 0, -1, 0, 1, 0, 10], ... [0, -1, 1, 0, 0, -1, 0, 1, 10.] - ... ]), 3).run_simplex() + ... ]), 3, 2).run_simplex() {'P': 70.0, 'x1': 10.0, 'x2': 10.0, 'x3': 20.0} # Non standard: minimisation and equalities @@ -235,73 +259,76 @@ def run_simplex(self) -> dict[Any, Any]: ST: 2x1 + x2 = 12 6x1 + 5x2 = 40 >>> Tableau(np.array([ - ... [8, 6, 0, -1, 0, -1, 0, 0, 52], - ... [1, 1, 0, 0, 0, 0, 0, 0, 0], - ... [2, 1, 1, 0, 0, 0, 0, 0, 12], - ... [2, 1, 0, -1, 0, 0, 1, 0, 12], - ... [6, 5, 0, 0, 1, 0, 0, 0, 40], - ... [6, 5, 0, 0, 0, -1, 0, 1, 40.] - ... ]), 2).run_simplex() + ... [8, 6, 0, 0, 52], + ... [1, 1, 0, 0, 0], + ... [2, 1, 1, 0, 12], + ... [6, 5, 0, 1, 40.], + ... ]), 2, 2).run_simplex() {'P': 7.0, 'x1': 5.0, 'x2': 2.0} + + + # Pivot on slack variables + Max: 8x1 + 6x2 + ST: x1 + 3x2 <= 33 + 4x1 + 2x2 <= 48 + 2x1 + 4x2 <= 48 + x1 + x2 >= 10 + x1 >= 2 + >>> Tableau(np.array([ + ... [2, 1, 0, 0, 0, -1, -1, 0, 0, 12.0], + ... [-8, -6, 0, 0, 0, 0, 0, 0, 0, 0.0], + ... [1, 3, 1, 0, 0, 0, 0, 0, 0, 33.0], + ... [4, 2, 0, 1, 0, 0, 0, 0, 0, 60.0], + ... [2, 4, 0, 0, 1, 0, 0, 0, 0, 48.0], + ... [1, 1, 0, 0, 0, -1, 0, 1, 0, 10.0], + ... [1, 0, 0, 0, 0, 0, -1, 0, 1, 2.0] + ... ]), 2, 2).run_simplex() # doctest: +ELLIPSIS + {'P': 132.0, 'x1': 12.000... 'x2': 5.999...} """ # Stop simplex algorithm from cycling. - for _ in range(100): + for _ in range(Tableau.maxiter): # Completion of each stage removes an objective. If both stages # are complete, then no objectives are left if not self.objectives: - self.col_titles = self.generate_col_titles( - self.n_vars, self.n_slack, self.n_art_vars - ) - # Find the values of each variable at optimal solution - return self.interpret_tableau(self.tableau, self.col_titles) + return self.interpret_tableau() - row_idx, col_idx = self.find_pivot(self.tableau) + row_idx, col_idx = self.find_pivot() # If there are no more negative values in objective row if self.stop_iter: # Delete artificial variable columns and rows. Update attributes - self.tableau = self.change_stage(self.tableau) + self.tableau = self.change_stage() else: - self.tableau = self.pivot(self.tableau, row_idx, col_idx) + self.tableau = self.pivot(row_idx, col_idx) return {} - def interpret_tableau( - self, tableau: np.ndarray, col_titles: list[str] - ) -> dict[str, float]: + def interpret_tableau(self) -> dict[str, float]: """Given the final tableau, add the corresponding values of the basic decision variables to the `output_dict` - >>> tableau = np.array([ + >>> Tableau(np.array([ ... [0,0,0.875,0.375,5], ... [0,1,0.375,-0.125,1], ... [1,0,-0.125,0.375,1] - ... ]) - >>> t = Tableau(tableau, 2) - >>> t.interpret_tableau(tableau, ["x1", "x2", "s1", "s2", "RHS"]) + ... ]),2, 0).interpret_tableau() {'P': 5.0, 'x1': 1.0, 'x2': 1.0} """ # P = RHS of final tableau - output_dict = {"P": abs(tableau[0, -1])} + output_dict = {"P": abs(self.tableau[0, -1])} for i in range(self.n_vars): - # Gives ids of nonzero entries in the ith column - nonzero = np.nonzero(tableau[:, i]) + # Gives indices of nonzero entries in the ith column + nonzero = np.nonzero(self.tableau[:, i]) n_nonzero = len(nonzero[0]) - # First entry in the nonzero ids + # First entry in the nonzero indices nonzero_rowidx = nonzero[0][0] - nonzero_val = tableau[nonzero_rowidx, i] + nonzero_val = self.tableau[nonzero_rowidx, i] # If there is only one nonzero value in column, which is one - if n_nonzero == nonzero_val == 1: - rhs_val = tableau[nonzero_rowidx, -1] - output_dict[col_titles[i]] = rhs_val - - # Check for basic variables - for title in col_titles: - # Don't add RHS or slack variables to output dict - if title[0] not in "R-s-a": - output_dict.setdefault(title, 0) + if n_nonzero == 1 and nonzero_val == 1: + rhs_val = self.tableau[nonzero_rowidx, -1] + output_dict[self.col_titles[i]] = rhs_val return output_dict From 72c7b05caa7e5b109b7b42c796a8af39f99a5100 Mon Sep 17 00:00:00 2001 From: Boris Galochkin Date: Fri, 18 Aug 2023 04:38:19 +0300 Subject: [PATCH 168/757] Fix `sorts/bucket_sort.py` implementation (#5786) * Fix sorts/bucket_sort.py * updating DIRECTORY.md * Remove unused var in bucket_sort.py * Fix list index in bucket_sort.py --------- Co-authored-by: Tianyi Zheng Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 1 + sorts/bucket_sort.py | 18 ++++++++++++------ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index d4a2bb48511a..e39a0674743a 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -710,6 +710,7 @@ * [2 Hidden Layers Neural Network](neural_network/2_hidden_layers_neural_network.py) * Activation Functions * [Exponential Linear Unit](neural_network/activation_functions/exponential_linear_unit.py) + * [Leaky Rectified Linear Unit](neural_network/activation_functions/leaky_rectified_linear_unit.py) * [Back Propagation Neural Network](neural_network/back_propagation_neural_network.py) * [Convolution Neural Network](neural_network/convolution_neural_network.py) * [Perceptron](neural_network/perceptron.py) diff --git a/sorts/bucket_sort.py b/sorts/bucket_sort.py index 7bcbe61a4526..c016e9e26e73 100644 --- a/sorts/bucket_sort.py +++ b/sorts/bucket_sort.py @@ -30,7 +30,7 @@ from __future__ import annotations -def bucket_sort(my_list: list) -> list: +def bucket_sort(my_list: list, bucket_count: int = 10) -> list: """ >>> data = [-1, 2, -5, 0] >>> bucket_sort(data) == sorted(data) @@ -43,21 +43,27 @@ def bucket_sort(my_list: list) -> list: True >>> bucket_sort([]) == sorted([]) True + >>> data = [-1e10, 1e10] + >>> bucket_sort(data) == sorted(data) + True >>> import random >>> collection = random.sample(range(-50, 50), 50) >>> bucket_sort(collection) == sorted(collection) True """ - if len(my_list) == 0: + + if len(my_list) == 0 or bucket_count <= 0: return [] + min_value, max_value = min(my_list), max(my_list) - bucket_count = int(max_value - min_value) + 1 + bucket_size = (max_value - min_value) / bucket_count buckets: list[list] = [[] for _ in range(bucket_count)] - for i in my_list: - buckets[int(i - min_value)].append(i) + for val in my_list: + index = min(int((val - min_value) / bucket_size), bucket_count - 1) + buckets[index].append(val) - return [v for bucket in buckets for v in sorted(bucket)] + return [val for bucket in buckets for val in sorted(bucket)] if __name__ == "__main__": From 5f7819e1cd192ecc89a7b7b929db63e045a47b45 Mon Sep 17 00:00:00 2001 From: Caeden Perelli-Harris Date: Fri, 18 Aug 2023 13:13:38 +0100 Subject: [PATCH 169/757] Fix get top billionaires BROKEN file (#8970) * updating DIRECTORY.md * fix(get-top-billionaires): Handle timestamp before epoch * updating DIRECTORY.md * revert(pyproject): Re-implement ignore lru_cache * fix(age): Update age to current year * fix(doctest): Make years since dynamic --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 1 + ...es.py.disabled => get_top_billionaires.py} | 27 ++++++++++++++----- 2 files changed, 21 insertions(+), 7 deletions(-) rename web_programming/{get_top_billionaires.py.disabled => get_top_billionaires.py} (72%) diff --git a/DIRECTORY.md b/DIRECTORY.md index e39a0674743a..1ff093d88766 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -1221,6 +1221,7 @@ * [Get Amazon Product Data](web_programming/get_amazon_product_data.py) * [Get Imdb Top 250 Movies Csv](web_programming/get_imdb_top_250_movies_csv.py) * [Get Imdbtop](web_programming/get_imdbtop.py) + * [Get Top Billionaires](web_programming/get_top_billionaires.py) * [Get Top Hn Posts](web_programming/get_top_hn_posts.py) * [Get User Tweets](web_programming/get_user_tweets.py) * [Giphy](web_programming/giphy.py) diff --git a/web_programming/get_top_billionaires.py.disabled b/web_programming/get_top_billionaires.py similarity index 72% rename from web_programming/get_top_billionaires.py.disabled rename to web_programming/get_top_billionaires.py index 6a8054e26270..6f986acb9181 100644 --- a/web_programming/get_top_billionaires.py.disabled +++ b/web_programming/get_top_billionaires.py @@ -3,7 +3,7 @@ This works for some of us but fails for others. """ -from datetime import datetime +from datetime import UTC, datetime, timedelta import requests from rich import box @@ -20,18 +20,31 @@ ) -def calculate_age(unix_date: int) -> str: +def calculate_age(unix_date: float) -> str: """Calculates age from given unix time format. Returns: Age as string - >>> calculate_age(-657244800000) - '73' - >>> calculate_age(46915200000) - '51' + >>> from datetime import datetime, UTC + >>> years_since_create = datetime.now(tz=UTC).year - 2022 + >>> int(calculate_age(-657244800000)) - years_since_create + 73 + >>> int(calculate_age(46915200000)) - years_since_create + 51 """ - birthdate = datetime.fromtimestamp(unix_date / 1000).date() + # Convert date from milliseconds to seconds + unix_date /= 1000 + + if unix_date < 0: + # Handle timestamp before epoch + epoch = datetime.fromtimestamp(0, tz=UTC) + seconds_since_epoch = (datetime.now(tz=UTC) - epoch).seconds + birthdate = ( + epoch - timedelta(seconds=abs(unix_date) - seconds_since_epoch) + ).date() + else: + birthdate = datetime.fromtimestamp(unix_date, tz=UTC).date() return str( TODAY.year - birthdate.year From 945803f65d79d0277c663a0e043228ed10996a92 Mon Sep 17 00:00:00 2001 From: Caeden Perelli-Harris Date: Fri, 18 Aug 2023 13:19:25 +0100 Subject: [PATCH 170/757] Unmark fetch anime and play as BROKEN and fix type errors (#8988) * updating DIRECTORY.md * type(fetch-anime-and-play): Fix type errors and re-enable * updating DIRECTORY.md --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 1 + ...play.py.BROKEN => fetch_anime_and_play.py} | 71 ++++++++++--------- 2 files changed, 38 insertions(+), 34 deletions(-) rename web_programming/{fetch_anime_and_play.py.BROKEN => fetch_anime_and_play.py} (70%) diff --git a/DIRECTORY.md b/DIRECTORY.md index 1ff093d88766..6af4ead56ebd 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -1213,6 +1213,7 @@ * [Daily Horoscope](web_programming/daily_horoscope.py) * [Download Images From Google Query](web_programming/download_images_from_google_query.py) * [Emails From Url](web_programming/emails_from_url.py) + * [Fetch Anime And Play](web_programming/fetch_anime_and_play.py) * [Fetch Bbc News](web_programming/fetch_bbc_news.py) * [Fetch Github Info](web_programming/fetch_github_info.py) * [Fetch Jobs](web_programming/fetch_jobs.py) diff --git a/web_programming/fetch_anime_and_play.py.BROKEN b/web_programming/fetch_anime_and_play.py similarity index 70% rename from web_programming/fetch_anime_and_play.py.BROKEN rename to web_programming/fetch_anime_and_play.py index 3bd4f704dd8d..366807785e85 100644 --- a/web_programming/fetch_anime_and_play.py.BROKEN +++ b/web_programming/fetch_anime_and_play.py @@ -1,7 +1,5 @@ -from xml.dom import NotFoundErr - import requests -from bs4 import BeautifulSoup, NavigableString +from bs4 import BeautifulSoup, NavigableString, Tag from fake_useragent import UserAgent BASE_URL = "https://ww1.gogoanime2.org" @@ -41,25 +39,23 @@ def search_scraper(anime_name: str) -> list: # get list of anime anime_ul = soup.find("ul", {"class": "items"}) + if anime_ul is None or isinstance(anime_ul, NavigableString): + msg = f"Could not find and anime with name {anime_name}" + raise ValueError(msg) anime_li = anime_ul.children # for each anime, insert to list. the name and url. anime_list = [] for anime in anime_li: - if not isinstance(anime, NavigableString): - try: - anime_url, anime_title = ( - anime.find("a")["href"], - anime.find("a")["title"], - ) - anime_list.append( - { - "title": anime_title, - "url": anime_url, - } - ) - except (NotFoundErr, KeyError): - pass + if isinstance(anime, Tag): + anime_url = anime.find("a") + if anime_url is None or isinstance(anime_url, NavigableString): + continue + anime_title = anime.find("a") + if anime_title is None or isinstance(anime_title, NavigableString): + continue + + anime_list.append({"title": anime_title["title"], "url": anime_url["href"]}) return anime_list @@ -93,22 +89,24 @@ def search_anime_episode_list(episode_endpoint: str) -> list: # With this id. get the episode list. episode_page_ul = soup.find("ul", {"id": "episode_related"}) + if episode_page_ul is None or isinstance(episode_page_ul, NavigableString): + msg = f"Could not find any anime eposiodes with name {anime_name}" + raise ValueError(msg) episode_page_li = episode_page_ul.children episode_list = [] for episode in episode_page_li: - try: - if not isinstance(episode, NavigableString): - episode_list.append( - { - "title": episode.find("div", {"class": "name"}).text.replace( - " ", "" - ), - "url": episode.find("a")["href"], - } - ) - except (KeyError, NotFoundErr): - pass + if isinstance(episode, Tag): + url = episode.find("a") + if url is None or isinstance(url, NavigableString): + continue + title = episode.find("div", {"class": "name"}) + if title is None or isinstance(title, NavigableString): + continue + + episode_list.append( + {"title": title.text.replace(" ", ""), "url": url["href"]} + ) return episode_list @@ -140,11 +138,16 @@ def get_anime_episode(episode_endpoint: str) -> list: soup = BeautifulSoup(response.text, "html.parser") - try: - episode_url = soup.find("iframe", {"id": "playerframe"})["src"] - download_url = episode_url.replace("/embed/", "/playlist/") + ".m3u8" - except (KeyError, NotFoundErr) as e: - raise e + url = soup.find("iframe", {"id": "playerframe"}) + if url is None or isinstance(url, NavigableString): + msg = f"Could not find url and download url from {episode_endpoint}" + raise RuntimeError(msg) + + episode_url = url["src"] + if not isinstance(episode_url, str): + msg = f"Could not find url and download url from {episode_endpoint}" + raise RuntimeError(msg) + download_url = episode_url.replace("/embed/", "/playlist/") + ".m3u8" return [f"{BASE_URL}{episode_url}", f"{BASE_URL}{download_url}"] From e887c14f1252cd7de3d99ef0553c448c8c9711df Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Fri, 18 Aug 2023 13:53:17 -0700 Subject: [PATCH 171/757] Fix continued_fraction.py to work for negative numbers (#8985) * Add doctests to continued_fraction.py for 0 and neg nums * Fix continued_fraction.py to work for negative nums Fix continued_fraction.py to work for negative nums by replacing int() call with floor() * Move comment in doctest --- maths/continued_fraction.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/maths/continued_fraction.py b/maths/continued_fraction.py index 25ff649db77a..04ff0b6ff0d2 100644 --- a/maths/continued_fraction.py +++ b/maths/continued_fraction.py @@ -6,6 +6,7 @@ from fractions import Fraction +from math import floor def continued_fraction(num: Fraction) -> list[int]: @@ -29,11 +30,17 @@ def continued_fraction(num: Fraction) -> list[int]: [0, 2, 4] >>> continued_fraction(Fraction("415/93")) [4, 2, 6, 7] + >>> continued_fraction(Fraction(0)) + [0] + >>> continued_fraction(Fraction(0.75)) + [0, 1, 3] + >>> continued_fraction(Fraction("-2.25")) # -2.25 = -3 + 0.75 + [-3, 1, 3] """ numerator, denominator = num.as_integer_ratio() continued_fraction_list: list[int] = [] while True: - integer_part = int(numerator / denominator) + integer_part = floor(numerator / denominator) continued_fraction_list.append(integer_part) numerator -= integer_part * denominator if numerator == 0: From 5ecb6baef8bf52f9bb99a1bb7cec4899b6df7ab4 Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Sun, 20 Aug 2023 05:36:00 -0700 Subject: [PATCH 172/757] Move and reimplement `convert_number_to_words.py` (#8998) * Move and reimplement convert_number_to_words.py - Move convert_number_to_words.py from web_programming/ to conversions/ - Reimplement the algorithm from scratch because the logic was very opaque and too heavily nested - Add support for the Western numbering system (both short and long) because the original implementation only supported the Indian numbering system - Add extensive doctests and error handling * updating DIRECTORY.md --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 2 +- conversions/convert_number_to_words.py | 205 +++++++++++++++++++++ web_programming/convert_number_to_words.py | 109 ----------- 3 files changed, 206 insertions(+), 110 deletions(-) create mode 100644 conversions/convert_number_to_words.py delete mode 100644 web_programming/convert_number_to_words.py diff --git a/DIRECTORY.md b/DIRECTORY.md index 6af4ead56ebd..653c1831d820 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -143,6 +143,7 @@ * [Binary To Decimal](conversions/binary_to_decimal.py) * [Binary To Hexadecimal](conversions/binary_to_hexadecimal.py) * [Binary To Octal](conversions/binary_to_octal.py) + * [Convert Number To Words](conversions/convert_number_to_words.py) * [Decimal To Any](conversions/decimal_to_any.py) * [Decimal To Binary](conversions/decimal_to_binary.py) * [Decimal To Binary Recursion](conversions/decimal_to_binary_recursion.py) @@ -1203,7 +1204,6 @@ ## Web Programming * [Co2 Emission](web_programming/co2_emission.py) - * [Convert Number To Words](web_programming/convert_number_to_words.py) * [Covid Stats Via Xpath](web_programming/covid_stats_via_xpath.py) * [Crawl Google Results](web_programming/crawl_google_results.py) * [Crawl Google Scholar Citation](web_programming/crawl_google_scholar_citation.py) diff --git a/conversions/convert_number_to_words.py b/conversions/convert_number_to_words.py new file mode 100644 index 000000000000..0e4405319f1f --- /dev/null +++ b/conversions/convert_number_to_words.py @@ -0,0 +1,205 @@ +from enum import Enum +from typing import ClassVar, Literal + + +class NumberingSystem(Enum): + SHORT = ( + (15, "quadrillion"), + (12, "trillion"), + (9, "billion"), + (6, "million"), + (3, "thousand"), + (2, "hundred"), + ) + + LONG = ( + (15, "billiard"), + (9, "milliard"), + (6, "million"), + (3, "thousand"), + (2, "hundred"), + ) + + INDIAN = ( + (14, "crore crore"), + (12, "lakh crore"), + (7, "crore"), + (5, "lakh"), + (3, "thousand"), + (2, "hundred"), + ) + + @classmethod + def max_value(cls, system: str) -> int: + """ + Gets the max value supported by the given number system. + + >>> NumberingSystem.max_value("short") == 10**18 - 1 + True + >>> NumberingSystem.max_value("long") == 10**21 - 1 + True + >>> NumberingSystem.max_value("indian") == 10**19 - 1 + True + """ + match (system_enum := cls[system.upper()]): + case cls.SHORT: + max_exp = system_enum.value[0][0] + 3 + case cls.LONG: + max_exp = system_enum.value[0][0] + 6 + case cls.INDIAN: + max_exp = 19 + case _: + raise ValueError("Invalid numbering system") + return 10**max_exp - 1 + + +class NumberWords(Enum): + ONES: ClassVar = { + 0: "", + 1: "one", + 2: "two", + 3: "three", + 4: "four", + 5: "five", + 6: "six", + 7: "seven", + 8: "eight", + 9: "nine", + } + + TEENS: ClassVar = { + 0: "ten", + 1: "eleven", + 2: "twelve", + 3: "thirteen", + 4: "fourteen", + 5: "fifteen", + 6: "sixteen", + 7: "seventeen", + 8: "eighteen", + 9: "nineteen", + } + + TENS: ClassVar = { + 2: "twenty", + 3: "thirty", + 4: "forty", + 5: "fifty", + 6: "sixty", + 7: "seventy", + 8: "eighty", + 9: "ninety", + } + + +def convert_small_number(num: int) -> str: + """ + Converts small, non-negative integers with irregular constructions in English (i.e., + numbers under 100) into words. + + >>> convert_small_number(0) + 'zero' + >>> convert_small_number(5) + 'five' + >>> convert_small_number(10) + 'ten' + >>> convert_small_number(15) + 'fifteen' + >>> convert_small_number(20) + 'twenty' + >>> convert_small_number(25) + 'twenty-five' + >>> convert_small_number(-1) + Traceback (most recent call last): + ... + ValueError: This function only accepts non-negative integers + >>> convert_small_number(123) + Traceback (most recent call last): + ... + ValueError: This function only converts numbers less than 100 + """ + if num < 0: + raise ValueError("This function only accepts non-negative integers") + if num >= 100: + raise ValueError("This function only converts numbers less than 100") + tens, ones = divmod(num, 10) + if tens == 0: + return NumberWords.ONES.value[ones] or "zero" + if tens == 1: + return NumberWords.TEENS.value[ones] + return ( + NumberWords.TENS.value[tens] + + ("-" if NumberWords.ONES.value[ones] else "") + + NumberWords.ONES.value[ones] + ) + + +def convert_number( + num: int, system: Literal["short", "long", "indian"] = "short" +) -> str: + """ + Converts an integer to English words. + + :param num: The integer to be converted + :param system: The numbering system (short, long, or Indian) + + >>> convert_number(0) + 'zero' + >>> convert_number(1) + 'one' + >>> convert_number(100) + 'one hundred' + >>> convert_number(-100) + 'negative one hundred' + >>> convert_number(123_456_789_012_345) # doctest: +NORMALIZE_WHITESPACE + 'one hundred twenty-three trillion four hundred fifty-six billion + seven hundred eighty-nine million twelve thousand three hundred forty-five' + >>> convert_number(123_456_789_012_345, "long") # doctest: +NORMALIZE_WHITESPACE + 'one hundred twenty-three thousand four hundred fifty-six milliard + seven hundred eighty-nine million twelve thousand three hundred forty-five' + >>> convert_number(12_34_56_78_90_12_345, "indian") # doctest: +NORMALIZE_WHITESPACE + 'one crore crore twenty-three lakh crore + forty-five thousand six hundred seventy-eight crore + ninety lakh twelve thousand three hundred forty-five' + >>> convert_number(10**18) + Traceback (most recent call last): + ... + ValueError: Input number is too large + >>> convert_number(10**21, "long") + Traceback (most recent call last): + ... + ValueError: Input number is too large + >>> convert_number(10**19, "indian") + Traceback (most recent call last): + ... + ValueError: Input number is too large + """ + word_groups = [] + + if num < 0: + word_groups.append("negative") + num *= -1 + + if num > NumberingSystem.max_value(system): + raise ValueError("Input number is too large") + + for power, unit in NumberingSystem[system.upper()].value: + digit_group, num = divmod(num, 10**power) + if digit_group > 0: + word_group = ( + convert_number(digit_group, system) + if digit_group >= 100 + else convert_small_number(digit_group) + ) + word_groups.append(f"{word_group} {unit}") + if num > 0 or not word_groups: # word_groups is only empty if input num was 0 + word_groups.append(convert_small_number(num)) + return " ".join(word_groups) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + + print(f"{convert_number(123456789) = }") diff --git a/web_programming/convert_number_to_words.py b/web_programming/convert_number_to_words.py deleted file mode 100644 index dac9e3e38e7c..000000000000 --- a/web_programming/convert_number_to_words.py +++ /dev/null @@ -1,109 +0,0 @@ -import math - - -def convert(number: int) -> str: - """ - Given a number return the number in words. - - >>> convert(123) - 'OneHundred,TwentyThree' - """ - if number == 0: - words = "Zero" - return words - else: - digits = math.log10(number) - digits = digits + 1 - singles = {} - singles[0] = "" - singles[1] = "One" - singles[2] = "Two" - singles[3] = "Three" - singles[4] = "Four" - singles[5] = "Five" - singles[6] = "Six" - singles[7] = "Seven" - singles[8] = "Eight" - singles[9] = "Nine" - - doubles = {} - doubles[0] = "" - doubles[2] = "Twenty" - doubles[3] = "Thirty" - doubles[4] = "Forty" - doubles[5] = "Fifty" - doubles[6] = "Sixty" - doubles[7] = "Seventy" - doubles[8] = "Eighty" - doubles[9] = "Ninety" - - teens = {} - teens[0] = "Ten" - teens[1] = "Eleven" - teens[2] = "Twelve" - teens[3] = "Thirteen" - teens[4] = "Fourteen" - teens[5] = "Fifteen" - teens[6] = "Sixteen" - teens[7] = "Seventeen" - teens[8] = "Eighteen" - teens[9] = "Nineteen" - - placevalue = {} - placevalue[2] = "Hundred," - placevalue[3] = "Thousand," - placevalue[5] = "Lakh," - placevalue[7] = "Crore," - - temp_num = number - words = "" - counter = 0 - digits = int(digits) - while counter < digits: - current = temp_num % 10 - if counter % 2 == 0: - addition = "" - if counter in placevalue and current != 0: - addition = placevalue[counter] - if counter == 2: - words = singles[current] + addition + words - elif counter == 0: - if ((temp_num % 100) // 10) == 1: - words = teens[current] + addition + words - temp_num = temp_num // 10 - counter += 1 - else: - words = singles[current] + addition + words - - else: - words = doubles[current] + addition + words - - else: - if counter == 1: - if current == 1: - words = teens[number % 10] + words - else: - addition = "" - if counter in placevalue: - addition = placevalue[counter] - words = doubles[current] + addition + words - else: - addition = "" - if counter in placevalue: - if current != 0 and ((temp_num % 100) // 10) != 0: - addition = placevalue[counter] - if ((temp_num % 100) // 10) == 1: - words = teens[current] + addition + words - temp_num = temp_num // 10 - counter += 1 - else: - words = singles[current] + addition + words - counter += 1 - temp_num = temp_num // 10 - return words - - -if __name__ == "__main__": - import doctest - - doctest.testmod() From 062957ef27fcaaf59753e3739052928ec37f220e Mon Sep 17 00:00:00 2001 From: Bama Charan Chhandogi Date: Sun, 20 Aug 2023 18:10:23 +0530 Subject: [PATCH 173/757] Octal to Binary Convert (#8949) * Octal to Binary Convert * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * mention return type * code scratch * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * mentioned return type * remove comment * added documention and some test cases * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * add another test case * fixes documention * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Documention and test cases added * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * documention problem solved * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * error in exit 1 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Apply suggestions from code review --------- Co-authored-by: BamaCharanChhandogi Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Tianyi Zheng --- conversions/octal_to_binary.py | 54 ++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 conversions/octal_to_binary.py diff --git a/conversions/octal_to_binary.py b/conversions/octal_to_binary.py new file mode 100644 index 000000000000..84e1e85f33ca --- /dev/null +++ b/conversions/octal_to_binary.py @@ -0,0 +1,54 @@ +""" +* Author: Bama Charan Chhandogi (https://github.com/BamaCharanChhandogi) +* Description: Convert a Octal number to Binary. + +References for better understanding: +https://en.wikipedia.org/wiki/Binary_number +https://en.wikipedia.org/wiki/Octal +""" + + +def octal_to_binary(octal_number: str) -> str: + """ + Convert an Octal number to Binary. + + >>> octal_to_binary("17") + '001111' + >>> octal_to_binary("7") + '111' + >>> octal_to_binary("Av") + Traceback (most recent call last): + ... + ValueError: Non-octal value was passed to the function + >>> octal_to_binary("@#") + Traceback (most recent call last): + ... + ValueError: Non-octal value was passed to the function + >>> octal_to_binary("") + Traceback (most recent call last): + ... + ValueError: Empty string was passed to the function + """ + if not octal_number: + raise ValueError("Empty string was passed to the function") + + binary_number = "" + octal_digits = "01234567" + for digit in octal_number: + if digit not in octal_digits: + raise ValueError("Non-octal value was passed to the function") + + binary_digit = "" + value = int(digit) + for _ in range(3): + binary_digit = str(value % 2) + binary_digit + value //= 2 + binary_number += binary_digit + + return binary_number + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 672e7bde2e5fad38a3bc4038d11a9c343e3667f7 Mon Sep 17 00:00:00 2001 From: Guduly <133545858+Guduly@users.noreply.github.com> Date: Sun, 20 Aug 2023 18:39:29 -0500 Subject: [PATCH 174/757] Update arc_length.py (#8964) * Update arc_length.py Wrote the output of testcase * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update arc_length.py Added the requested changes * Update arc_length.py followed the change request * Update arc_length.py followed suggestions --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- maths/arc_length.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/maths/arc_length.py b/maths/arc_length.py index 9e87ca38cc7d..4c518f321dc7 100644 --- a/maths/arc_length.py +++ b/maths/arc_length.py @@ -7,6 +7,8 @@ def arc_length(angle: int, radius: int) -> float: 3.9269908169872414 >>> arc_length(120, 15) 31.415926535897928 + >>> arc_length(90, 10) + 15.707963267948966 """ return 2 * pi * radius * (angle / 360) From 1984d9717158c89f9acca2b635a373bad7048633 Mon Sep 17 00:00:00 2001 From: Dom <97384583+tosemml@users.noreply.github.com> Date: Sun, 20 Aug 2023 16:43:09 -0700 Subject: [PATCH 175/757] Refactorings (#8987) * use np.dot * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * further improvements using array slicing Co-authored-by: Tianyi Zheng --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Tianyi Zheng --- arithmetic_analysis/gaussian_elimination.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/arithmetic_analysis/gaussian_elimination.py b/arithmetic_analysis/gaussian_elimination.py index f0f20af8e417..13f509a4f117 100644 --- a/arithmetic_analysis/gaussian_elimination.py +++ b/arithmetic_analysis/gaussian_elimination.py @@ -33,10 +33,7 @@ def retroactive_resolution( x: NDArray[float64] = np.zeros((rows, 1), dtype=float) for row in reversed(range(rows)): - total = 0 - for col in range(row + 1, columns): - total += coefficients[row, col] * x[col] - + total = np.dot(coefficients[row, row + 1 :], x[row + 1 :]) x[row, 0] = (vector[row] - total) / coefficients[row, row] return x From 1210559deb60b44cb9f57ce16c9bf6d79c0f443c Mon Sep 17 00:00:00 2001 From: Caeden Perelli-Harris Date: Mon, 21 Aug 2023 14:25:20 +0100 Subject: [PATCH 176/757] Consolidate decimal to binary iterative and recursive (#8999) * updating DIRECTORY.md * refactor(decimal-to-binary): Consolidate implementations * updating DIRECTORY.md * refactor(decimal-to-binary): Rename main and helper recursive --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 1 - conversions/decimal_to_binary.py | 67 +++++++++++++++++++--- conversions/decimal_to_binary_recursion.py | 53 ----------------- 3 files changed, 59 insertions(+), 62 deletions(-) delete mode 100644 conversions/decimal_to_binary_recursion.py diff --git a/DIRECTORY.md b/DIRECTORY.md index 653c1831d820..dd4404edd364 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -146,7 +146,6 @@ * [Convert Number To Words](conversions/convert_number_to_words.py) * [Decimal To Any](conversions/decimal_to_any.py) * [Decimal To Binary](conversions/decimal_to_binary.py) - * [Decimal To Binary Recursion](conversions/decimal_to_binary_recursion.py) * [Decimal To Hexadecimal](conversions/decimal_to_hexadecimal.py) * [Decimal To Octal](conversions/decimal_to_octal.py) * [Energy Conversions](conversions/energy_conversions.py) diff --git a/conversions/decimal_to_binary.py b/conversions/decimal_to_binary.py index 973c47c8af67..cf2b6040ec2a 100644 --- a/conversions/decimal_to_binary.py +++ b/conversions/decimal_to_binary.py @@ -1,27 +1,27 @@ """Convert a Decimal Number to a Binary Number.""" -def decimal_to_binary(num: int) -> str: +def decimal_to_binary_iterative(num: int) -> str: """ Convert an Integer Decimal Number to a Binary Number as str. - >>> decimal_to_binary(0) + >>> decimal_to_binary_iterative(0) '0b0' - >>> decimal_to_binary(2) + >>> decimal_to_binary_iterative(2) '0b10' - >>> decimal_to_binary(7) + >>> decimal_to_binary_iterative(7) '0b111' - >>> decimal_to_binary(35) + >>> decimal_to_binary_iterative(35) '0b100011' >>> # negatives work too - >>> decimal_to_binary(-2) + >>> decimal_to_binary_iterative(-2) '-0b10' >>> # other floats will error - >>> decimal_to_binary(16.16) # doctest: +ELLIPSIS + >>> decimal_to_binary_iterative(16.16) # doctest: +ELLIPSIS Traceback (most recent call last): ... TypeError: 'float' object cannot be interpreted as an integer >>> # strings will error as well - >>> decimal_to_binary('0xfffff') # doctest: +ELLIPSIS + >>> decimal_to_binary_iterative('0xfffff') # doctest: +ELLIPSIS Traceback (most recent call last): ... TypeError: 'str' object cannot be interpreted as an integer @@ -52,7 +52,58 @@ def decimal_to_binary(num: int) -> str: return "0b" + "".join(str(e) for e in binary) +def decimal_to_binary_recursive_helper(decimal: int) -> str: + """ + Take a positive integer value and return its binary equivalent. + >>> decimal_to_binary_recursive_helper(1000) + '1111101000' + >>> decimal_to_binary_recursive_helper("72") + '1001000' + >>> decimal_to_binary_recursive_helper("number") + Traceback (most recent call last): + ... + ValueError: invalid literal for int() with base 10: 'number' + """ + decimal = int(decimal) + if decimal in (0, 1): # Exit cases for the recursion + return str(decimal) + div, mod = divmod(decimal, 2) + return decimal_to_binary_recursive_helper(div) + str(mod) + + +def decimal_to_binary_recursive(number: str) -> str: + """ + Take an integer value and raise ValueError for wrong inputs, + call the function above and return the output with prefix "0b" & "-0b" + for positive and negative integers respectively. + >>> decimal_to_binary_recursive(0) + '0b0' + >>> decimal_to_binary_recursive(40) + '0b101000' + >>> decimal_to_binary_recursive(-40) + '-0b101000' + >>> decimal_to_binary_recursive(40.8) + Traceback (most recent call last): + ... + ValueError: Input value is not an integer + >>> decimal_to_binary_recursive("forty") + Traceback (most recent call last): + ... + ValueError: Input value is not an integer + """ + number = str(number).strip() + if not number: + raise ValueError("No input value was provided") + negative = "-" if number.startswith("-") else "" + number = number.lstrip("-") + if not number.isnumeric(): + raise ValueError("Input value is not an integer") + return f"{negative}0b{decimal_to_binary_recursive_helper(int(number))}" + + if __name__ == "__main__": import doctest doctest.testmod() + + print(decimal_to_binary_recursive(input("Input a decimal number: "))) diff --git a/conversions/decimal_to_binary_recursion.py b/conversions/decimal_to_binary_recursion.py deleted file mode 100644 index 05833ca670c3..000000000000 --- a/conversions/decimal_to_binary_recursion.py +++ /dev/null @@ -1,53 +0,0 @@ -def binary_recursive(decimal: int) -> str: - """ - Take a positive integer value and return its binary equivalent. - >>> binary_recursive(1000) - '1111101000' - >>> binary_recursive("72") - '1001000' - >>> binary_recursive("number") - Traceback (most recent call last): - ... - ValueError: invalid literal for int() with base 10: 'number' - """ - decimal = int(decimal) - if decimal in (0, 1): # Exit cases for the recursion - return str(decimal) - div, mod = divmod(decimal, 2) - return binary_recursive(div) + str(mod) - - -def main(number: str) -> str: - """ - Take an integer value and raise ValueError for wrong inputs, - call the function above and return the output with prefix "0b" & "-0b" - for positive and negative integers respectively. - >>> main(0) - '0b0' - >>> main(40) - '0b101000' - >>> main(-40) - '-0b101000' - >>> main(40.8) - Traceback (most recent call last): - ... - ValueError: Input value is not an integer - >>> main("forty") - Traceback (most recent call last): - ... - ValueError: Input value is not an integer - """ - number = str(number).strip() - if not number: - raise ValueError("No input value was provided") - negative = "-" if number.startswith("-") else "" - number = number.lstrip("-") - if not number.isnumeric(): - raise ValueError("Input value is not an integer") - return f"{negative}0b{binary_recursive(int(number))}" - - -if __name__ == "__main__": - from doctest import testmod - - testmod() From b3dc6ef035f097c9eb91911d8970668049e47d62 Mon Sep 17 00:00:00 2001 From: AmirSoroush Date: Tue, 22 Aug 2023 02:17:02 +0300 Subject: [PATCH 177/757] fixes #9002; improve insertion_sort algorithm (#9005) * fixes #9002; improve insertion_sort algorithm * add type hints to sorts/insertion_sort.py --- sorts/insertion_sort.py | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/sorts/insertion_sort.py b/sorts/insertion_sort.py index 6d5bb2b46013..f11ddac349a0 100644 --- a/sorts/insertion_sort.py +++ b/sorts/insertion_sort.py @@ -13,8 +13,19 @@ python3 insertion_sort.py """ +from collections.abc import MutableSequence +from typing import Any, Protocol, TypeVar -def insertion_sort(collection: list) -> list: + +class Comparable(Protocol): + def __lt__(self, other: Any, /) -> bool: + ... + + +T = TypeVar("T", bound=Comparable) + + +def insertion_sort(collection: MutableSequence[T]) -> MutableSequence[T]: """A pure Python implementation of the insertion sort algorithm :param collection: some mutable ordered collection with heterogeneous @@ -40,13 +51,12 @@ def insertion_sort(collection: list) -> list: True """ - for insert_index, insert_value in enumerate(collection[1:]): - temp_index = insert_index - while insert_index >= 0 and insert_value < collection[insert_index]: - collection[insert_index + 1] = collection[insert_index] + for insert_index in range(1, len(collection)): + insert_value = collection[insert_index] + while insert_index > 0 and insert_value < collection[insert_index - 1]: + collection[insert_index] = collection[insert_index - 1] insert_index -= 1 - if insert_index != temp_index: - collection[insert_index + 1] = insert_value + collection[insert_index] = insert_value return collection From 04fd5c1b5e7880017d874f4305ca3396f868ee37 Mon Sep 17 00:00:00 2001 From: Caeden Perelli-Harris Date: Tue, 22 Aug 2023 00:20:51 +0100 Subject: [PATCH 178/757] Create langtons ant algorithm (#8967) * updating DIRECTORY.md * feat(cellular_automata): Langonts ant algorithm * updating DIRECTORY.md * Update cellular_automata/langtons_ant.py Co-authored-by: Tianyi Zheng * Apply suggestions from code review Co-authored-by: Tianyi Zheng * fix(langtons-ant): Set funcanimation interval to 1 --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: Tianyi Zheng --- DIRECTORY.md | 1 + cellular_automata/langtons_ant.py | 106 ++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+) create mode 100644 cellular_automata/langtons_ant.py diff --git a/DIRECTORY.md b/DIRECTORY.md index dd4404edd364..866a3084f67b 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -72,6 +72,7 @@ ## Cellular Automata * [Conways Game Of Life](cellular_automata/conways_game_of_life.py) * [Game Of Life](cellular_automata/game_of_life.py) + * [Langtons Ant](cellular_automata/langtons_ant.py) * [Nagel Schrekenberg](cellular_automata/nagel_schrekenberg.py) * [One Dimensional](cellular_automata/one_dimensional.py) * [Wa Tor](cellular_automata/wa_tor.py) diff --git a/cellular_automata/langtons_ant.py b/cellular_automata/langtons_ant.py new file mode 100644 index 000000000000..983c626546ad --- /dev/null +++ b/cellular_automata/langtons_ant.py @@ -0,0 +1,106 @@ +""" +Langton's ant + +@ https://en.wikipedia.org/wiki/Langton%27s_ant +@ https://upload.wikimedia.org/wikipedia/commons/0/09/LangtonsAntAnimated.gif +""" + +from functools import partial + +from matplotlib import pyplot as plt +from matplotlib.animation import FuncAnimation + +WIDTH = 80 +HEIGHT = 80 + + +class LangtonsAnt: + """ + Represents the main LangonsAnt algorithm. + + >>> la = LangtonsAnt(2, 2) + >>> la.board + [[True, True], [True, True]] + >>> la.ant_position + (1, 1) + """ + + def __init__(self, width: int, height: int) -> None: + # Each square is either True or False where True is white and False is black + self.board = [[True] * width for _ in range(height)] + self.ant_position: tuple[int, int] = (width // 2, height // 2) + + # Initially pointing left (similar to the the wikipedia image) + # (0 = 0° | 1 = 90° | 2 = 180 ° | 3 = 270°) + self.ant_direction: int = 3 + + def move_ant(self, axes: plt.Axes | None, display: bool, _frame: int) -> None: + """ + Performs three tasks: + 1. The ant turns either clockwise or anti-clockwise according to the colour + of the square that it is currently on. If the square is white, the ant + turns clockwise, and if the square is black the ant turns anti-clockwise + 2. The ant moves one square in the direction that it is currently facing + 3. The square the ant was previously on is inverted (White -> Black and + Black -> White) + + If display is True, the board will also be displayed on the axes + + >>> la = LangtonsAnt(2, 2) + >>> la.move_ant(None, True, 0) + >>> la.board + [[True, True], [True, False]] + >>> la.move_ant(None, True, 0) + >>> la.board + [[True, False], [True, False]] + """ + directions = { + 0: (-1, 0), # 0° + 1: (0, 1), # 90° + 2: (1, 0), # 180° + 3: (0, -1), # 270° + } + x, y = self.ant_position + + # Turn clockwise or anti-clockwise according to colour of square + if self.board[x][y] is True: + # The square is white so turn 90° clockwise + self.ant_direction = (self.ant_direction + 1) % 4 + else: + # The square is black so turn 90° anti-clockwise + self.ant_direction = (self.ant_direction - 1) % 4 + + # Move ant + move_x, move_y = directions[self.ant_direction] + self.ant_position = (x + move_x, y + move_y) + + # Flip colour of square + self.board[x][y] = not self.board[x][y] + + if display and axes: + # Display the board on the axes + axes.get_xaxis().set_ticks([]) + axes.get_yaxis().set_ticks([]) + axes.imshow(self.board, cmap="gray", interpolation="nearest") + + def display(self, frames: int = 100_000) -> None: + """ + Displays the board without delay in a matplotlib plot + to visually understand and track the ant. + + >>> _ = LangtonsAnt(WIDTH, HEIGHT) + """ + fig, ax = plt.subplots() + # Assign animation to a variable to prevent it from getting garbage collected + self.animation = FuncAnimation( + fig, partial(self.move_ant, ax, True), frames=frames, interval=1 + ) + plt.show() + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + + LangtonsAnt(WIDTH, HEIGHT).display() From c7aeaa3fd8a114ecf9b1e800dfb8cc3cc7a3cbaa Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 22 Aug 2023 07:42:14 +0200 Subject: [PATCH 179/757] [pre-commit.ci] pre-commit autoupdate (#9006) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/astral-sh/ruff-pre-commit: v0.0.284 → v0.0.285](https://github.com/astral-sh/ruff-pre-commit/compare/v0.0.284...v0.0.285) - [github.com/abravalheri/validate-pyproject: v0.13 → v0.14](https://github.com/abravalheri/validate-pyproject/compare/v0.13...v0.14) - [github.com/pre-commit/mirrors-mypy: v1.5.0 → v1.5.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.5.0...v1.5.1) * updating DIRECTORY.md --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- .pre-commit-config.yaml | 6 +++--- DIRECTORY.md | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b08139561639..ad3e0cd87f2e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.0.284 + rev: v0.0.285 hooks: - id: ruff @@ -46,12 +46,12 @@ repos: pass_filenames: false - repo: https://github.com/abravalheri/validate-pyproject - rev: v0.13 + rev: v0.14 hooks: - id: validate-pyproject - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.5.0 + rev: v1.5.1 hooks: - id: mypy args: diff --git a/DIRECTORY.md b/DIRECTORY.md index 866a3084f67b..ebb164d0496c 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -155,6 +155,7 @@ * [Hexadecimal To Decimal](conversions/hexadecimal_to_decimal.py) * [Length Conversion](conversions/length_conversion.py) * [Molecular Chemistry](conversions/molecular_chemistry.py) + * [Octal To Binary](conversions/octal_to_binary.py) * [Octal To Decimal](conversions/octal_to_decimal.py) * [Prefix Conversions](conversions/prefix_conversions.py) * [Prefix Conversions String](conversions/prefix_conversions_string.py) From fceacf977f0e4567d00f297686527ac9b4e5561f Mon Sep 17 00:00:00 2001 From: Caeden Perelli-Harris Date: Tue, 22 Aug 2023 10:33:47 +0100 Subject: [PATCH 180/757] Fix type errors in permutations (#9007) * updating DIRECTORY.md * types(permuations): Rename permute2 * Apply suggestions from code review Co-authored-by: Tianyi Zheng * fix(permutations): Call permute_recursive * fix(permutations): Correct permutations order --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: Tianyi Zheng --- data_structures/arrays/permutations.py | 28 ++++++++++++-------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/data_structures/arrays/permutations.py b/data_structures/arrays/permutations.py index 4558bd8d468a..0f029187b92b 100644 --- a/data_structures/arrays/permutations.py +++ b/data_structures/arrays/permutations.py @@ -1,17 +1,16 @@ -def permute(nums: list[int]) -> list[list[int]]: +def permute_recursive(nums: list[int]) -> list[list[int]]: """ Return all permutations. - >>> from itertools import permutations - >>> numbers= [1,2,3] - >>> all(list(nums) in permute(numbers) for nums in permutations(numbers)) - True + + >>> permute_recursive([1, 2, 3]) + [[3, 2, 1], [2, 3, 1], [1, 3, 2], [3, 1, 2], [2, 1, 3], [1, 2, 3]] """ - result = [] - if len(nums) == 1: - return [nums.copy()] + result: list[list[int]] = [] + if len(nums) == 0: + return [[]] for _ in range(len(nums)): n = nums.pop(0) - permutations = permute(nums) + permutations = permute_recursive(nums) for perm in permutations: perm.append(n) result.extend(permutations) @@ -19,15 +18,15 @@ def permute(nums: list[int]) -> list[list[int]]: return result -def permute2(nums): +def permute_backtrack(nums: list[int]) -> list[list[int]]: """ Return all permutations of the given list. - >>> permute2([1, 2, 3]) + >>> permute_backtrack([1, 2, 3]) [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 2, 1], [3, 1, 2]] """ - def backtrack(start): + def backtrack(start: int) -> None: if start == len(nums) - 1: output.append(nums[:]) else: @@ -36,7 +35,7 @@ def backtrack(start): backtrack(start + 1) nums[start], nums[i] = nums[i], nums[start] # backtrack - output = [] + output: list[list[int]] = [] backtrack(0) return output @@ -44,7 +43,6 @@ def backtrack(start): if __name__ == "__main__": import doctest - # use res to print the data in permute2 function - res = permute2([1, 2, 3]) + res = permute_backtrack([1, 2, 3]) print(res) doctest.testmod() From 0a9438071ee08121f069c77a5cb662206a4d348f Mon Sep 17 00:00:00 2001 From: Arijit De Date: Wed, 23 Aug 2023 18:06:59 +0530 Subject: [PATCH 181/757] Updated postfix_evaluation.py to support Unary operators (#8787) * Updated postfix_evaluation.py to support Unary operators and floating point numbers Fixes #8754 and #8724 Also merged evaluate_postfix_notations.py and postfix_evaluation.py into postfix_evaluation.py Signed-off-by: Arijit De * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Updated postfix_evaluation.py to support Unary operators and floating point numbers. Fixes #8754 and formatted code to pass ruff and black test. Also merged evaluate_postfix_notations.py and postfix_evaluation.py into postfix_evaluation.py which fixes #8724 and made sure it passes doctest Signed-off-by: Arijit De * Fixed return type hinting required by pre commit for evaluate function Signed-off-by: Arijit De * Changed line 186 to return only top of stack instead of calling the get_number function as it was converting float values to int, resulting in data loss. Fixes #8754 and #8724 Signed-off-by: Arijit De * Made the requested changes Also changed the code to make the evaluate function first convert all the numbers and then process the valid expression. * Fixes #8754, #8724 Updated postfix_evaluation.py postfix_evaluation.py now supports Unary operators and floating point numbers. Also merged evaluate_postfix_notations.py and postfix_evaluation.py into postfix_evaluation.py which fixes #8724. Added a doctest example with unary operator. * Fixes #8754, #8724 Updated postfix_evaluation.py postfix_evaluation.py now supports Unary operators and floating point numbers. Also merged evaluate_postfix_notations.py and postfix_evaluation.py into postfix_evaluation.py which fixes #8724. Added a doctest example with unary operator. * Fixes #8754, #8724 Updated the parse_token function of postfix_evaluation.py ostfix_evaluation.py now supports Unary operators and floating point numbers. Also merged evaluate_postfix_notations.py and postfix_evaluation.py into postfix_evaluation.py which fixes #8724. Added a doctest example with unary operator and invalid expression. * Fixes #8754, #8724 Updated postfix_evaluation.py postfix_evaluation.py now supports Unary operators and floating point numbers. Also merged evaluate_postfix_notations.py and postfix_evaluation.py into postfix_evaluation.py which fixes #8724. Added a doctest example with unary operator and invalid expression. * Update postfix_evaluation.py * Update postfix_evaluation.py * Update postfix_evaluation.py * Update postfix_evaluation.py * Update postfix_evaluation.py --------- Signed-off-by: Arijit De Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- .../stacks/evaluate_postfix_notations.py | 52 ----- data_structures/stacks/postfix_evaluation.py | 200 +++++++++++++++--- 2 files changed, 166 insertions(+), 86 deletions(-) delete mode 100644 data_structures/stacks/evaluate_postfix_notations.py diff --git a/data_structures/stacks/evaluate_postfix_notations.py b/data_structures/stacks/evaluate_postfix_notations.py deleted file mode 100644 index 51ea353b17de..000000000000 --- a/data_structures/stacks/evaluate_postfix_notations.py +++ /dev/null @@ -1,52 +0,0 @@ -""" -The Reverse Polish Nation also known as Polish postfix notation -or simply postfix notation. -https://en.wikipedia.org/wiki/Reverse_Polish_notation -Classic examples of simple stack implementations -Valid operators are +, -, *, /. -Each operand may be an integer or another expression. -""" -from __future__ import annotations - -from typing import Any - - -def evaluate_postfix(postfix_notation: list) -> int: - """ - >>> evaluate_postfix(["2", "1", "+", "3", "*"]) - 9 - >>> evaluate_postfix(["4", "13", "5", "/", "+"]) - 6 - >>> evaluate_postfix([]) - 0 - """ - if not postfix_notation: - return 0 - - operations = {"+", "-", "*", "/"} - stack: list[Any] = [] - - for token in postfix_notation: - if token in operations: - b, a = stack.pop(), stack.pop() - if token == "+": - stack.append(a + b) - elif token == "-": - stack.append(a - b) - elif token == "*": - stack.append(a * b) - else: - if a * b < 0 and a % b != 0: - stack.append(a // b + 1) - else: - stack.append(a // b) - else: - stack.append(int(token)) - - return stack.pop() - - -if __name__ == "__main__": - import doctest - - doctest.testmod() diff --git a/data_structures/stacks/postfix_evaluation.py b/data_structures/stacks/postfix_evaluation.py index 28128f82ec19..03a87b9e0fa3 100644 --- a/data_structures/stacks/postfix_evaluation.py +++ b/data_structures/stacks/postfix_evaluation.py @@ -1,4 +1,11 @@ """ +Reverse Polish Nation is also known as Polish postfix notation or simply postfix +notation. +https://en.wikipedia.org/wiki/Reverse_Polish_notation +Classic examples of simple stack implementations. +Valid operators are +, -, *, /. +Each operand may be an integer or another expression. + Output: Enter a Postfix Equation (space separated) = 5 6 9 * + @@ -17,52 +24,177 @@ Result = 59 """ -import operator as op +# Defining valid unary operator symbols +UNARY_OP_SYMBOLS = ("-", "+") + +# operators & their respective operation +OPERATORS = { + "^": lambda p, q: p**q, + "*": lambda p, q: p * q, + "/": lambda p, q: p / q, + "+": lambda p, q: p + q, + "-": lambda p, q: p - q, +} + + +def parse_token(token: str | float) -> float | str: + """ + Converts the given data to the appropriate number if it is indeed a number, else + returns the data as it is with a False flag. This function also serves as a check + of whether the input is a number or not. + + Parameters + ---------- + token: The data that needs to be converted to the appropriate operator or number. + + Returns + ------- + float or str + Returns a float if `token` is a number or a str if `token` is an operator + """ + if token in OPERATORS: + return token + try: + return float(token) + except ValueError: + msg = f"{token} is neither a number nor a valid operator" + raise ValueError(msg) + + +def evaluate(post_fix: list[str], verbose: bool = False) -> float: + """ + Evaluate postfix expression using a stack. + >>> evaluate(["0"]) + 0.0 + >>> evaluate(["-0"]) + -0.0 + >>> evaluate(["1"]) + 1.0 + >>> evaluate(["-1"]) + -1.0 + >>> evaluate(["-1.1"]) + -1.1 + >>> evaluate(["2", "1", "+", "3", "*"]) + 9.0 + >>> evaluate(["2", "1.9", "+", "3", "*"]) + 11.7 + >>> evaluate(["2", "-1.9", "+", "3", "*"]) + 0.30000000000000027 + >>> evaluate(["4", "13", "5", "/", "+"]) + 6.6 + >>> evaluate(["2", "-", "3", "+"]) + 1.0 + >>> evaluate(["-4", "5", "*", "6", "-"]) + -26.0 + >>> evaluate([]) + 0 + >>> evaluate(["4", "-", "6", "7", "/", "9", "8"]) + Traceback (most recent call last): + ... + ArithmeticError: Input is not a valid postfix expression + + Parameters + ---------- + post_fix: + The postfix expression is tokenized into operators and operands and stored + as a Python list + verbose: + Display stack contents while evaluating the expression if verbose is True -def solve(post_fix): + Returns + ------- + float + The evaluated value + """ + if not post_fix: + return 0 + # Checking the list to find out whether the postfix expression is valid + valid_expression = [parse_token(token) for token in post_fix] + if verbose: + # print table header + print("Symbol".center(8), "Action".center(12), "Stack", sep=" | ") + print("-" * (30 + len(post_fix))) stack = [] - div = lambda x, y: int(x / y) # noqa: E731 integer division operation - opr = { - "^": op.pow, - "*": op.mul, - "/": div, - "+": op.add, - "-": op.sub, - } # operators & their respective operation - - # print table header - print("Symbol".center(8), "Action".center(12), "Stack", sep=" | ") - print("-" * (30 + len(post_fix))) - - for x in post_fix: - if x.isdigit(): # if x in digit + for x in valid_expression: + if x not in OPERATORS: stack.append(x) # append x to stack - # output in tabular format - print(x.rjust(8), ("push(" + x + ")").ljust(12), ",".join(stack), sep=" | ") - else: + if verbose: + # output in tabular format + print( + f"{x}".rjust(8), + f"push({x})".ljust(12), + stack, + sep=" | ", + ) + continue + # If x is operator + # If only 1 value is inside the stack and + or - is encountered + # then this is unary + or - case + if x in UNARY_OP_SYMBOLS and len(stack) < 2: b = stack.pop() # pop stack + if x == "-": + b *= -1 # negate b + stack.append(b) + if verbose: + # output in tabular format + print( + "".rjust(8), + f"pop({b})".ljust(12), + stack, + sep=" | ", + ) + print( + str(x).rjust(8), + f"push({x}{b})".ljust(12), + stack, + sep=" | ", + ) + continue + b = stack.pop() # pop stack + if verbose: # output in tabular format - print("".rjust(8), ("pop(" + b + ")").ljust(12), ",".join(stack), sep=" | ") + print( + "".rjust(8), + f"pop({b})".ljust(12), + stack, + sep=" | ", + ) - a = stack.pop() # pop stack + a = stack.pop() # pop stack + if verbose: # output in tabular format - print("".rjust(8), ("pop(" + a + ")").ljust(12), ",".join(stack), sep=" | ") - - stack.append( - str(opr[x](int(a), int(b))) - ) # evaluate the 2 values popped from stack & push result to stack + print( + "".rjust(8), + f"pop({a})".ljust(12), + stack, + sep=" | ", + ) + # evaluate the 2 values popped from stack & push result to stack + stack.append(OPERATORS[x](a, b)) # type: ignore[index] + if verbose: # output in tabular format print( - x.rjust(8), - ("push(" + a + x + b + ")").ljust(12), - ",".join(stack), + f"{x}".rjust(8), + f"push({a}{x}{b})".ljust(12), + stack, sep=" | ", ) - - return int(stack[0]) + # If everything is executed correctly, the stack will contain + # only one element which is the result + if len(stack) != 1: + raise ArithmeticError("Input is not a valid postfix expression") + return float(stack[0]) if __name__ == "__main__": - Postfix = input("\n\nEnter a Postfix Equation (space separated) = ").split(" ") - print("\n\tResult = ", solve(Postfix)) + # Create a loop so that the user can evaluate postfix expressions multiple times + while True: + expression = input("Enter a Postfix Expression (space separated): ").split(" ") + prompt = "Do you want to see stack contents while evaluating? [y/N]: " + verbose = input(prompt).strip().lower() == "y" + output = evaluate(expression, verbose) + print("Result = ", output) + prompt = "Do you want to enter another expression? [y/N]: " + if input(prompt).strip().lower() != "y": + break From 421ace81edb0d9af3a173f4ca7e66cc900078c1d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 29 Aug 2023 15:18:10 +0200 Subject: [PATCH 182/757] [pre-commit.ci] pre-commit autoupdate (#9013) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/astral-sh/ruff-pre-commit: v0.0.285 → v0.0.286](https://github.com/astral-sh/ruff-pre-commit/compare/v0.0.285...v0.0.286) - [github.com/tox-dev/pyproject-fmt: 0.13.1 → 1.1.0](https://github.com/tox-dev/pyproject-fmt/compare/0.13.1...1.1.0) * updating DIRECTORY.md * Fis ruff rules PIE808,PLR1714 --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: Christian Clauss --- .pre-commit-config.yaml | 4 ++-- DIRECTORY.md | 1 - arithmetic_analysis/jacobi_iteration_method.py | 4 ++-- arithmetic_analysis/secant_method.py | 2 +- backtracking/hamiltonian_cycle.py | 2 +- backtracking/sudoku.py | 2 +- bit_manipulation/reverse_bits.py | 2 +- ciphers/trafid_cipher.py | 2 +- data_structures/binary_tree/lazy_segment_tree.py | 6 +++--- data_structures/linked_list/circular_linked_list.py | 2 +- data_structures/linked_list/doubly_linked_list.py | 6 +++--- data_structures/linked_list/is_palindrome.py | 2 +- data_structures/linked_list/singly_linked_list.py | 8 ++++---- data_structures/stacks/stock_span_problem.py | 2 +- digital_image_processing/filters/bilateral_filter.py | 4 ++-- digital_image_processing/filters/convolve.py | 4 ++-- .../filters/local_binary_pattern.py | 4 ++-- .../test_digital_image_processing.py | 4 ++-- divide_and_conquer/strassen_matrix_multiplication.py | 4 ++-- dynamic_programming/floyd_warshall.py | 10 +++++----- hashes/chaos_machine.py | 2 +- hashes/hamming_code.py | 4 ++-- hashes/sha1.py | 2 +- hashes/sha256.py | 2 +- machine_learning/gradient_descent.py | 2 +- machine_learning/linear_regression.py | 4 ++-- machine_learning/lstm/lstm_prediction.py | 4 ++-- maths/entropy.py | 2 +- maths/eulers_totient.py | 2 +- maths/greedy_coin_change.py | 2 +- maths/persistence.py | 4 ++-- maths/series/harmonic.py | 2 +- matrix/spiral_print.py | 2 +- other/magicdiamondpattern.py | 6 +++--- project_euler/problem_070/sol1.py | 2 +- project_euler/problem_112/sol1.py | 2 +- quantum/q_full_adder.py | 2 +- scheduling/highest_response_ratio_next.py | 6 +++--- sorts/counting_sort.py | 2 +- sorts/cycle_sort.py | 2 +- sorts/double_sort.py | 4 ++-- sorts/odd_even_transposition_parallel.py | 4 ++-- strings/rabin_karp.py | 2 +- 43 files changed, 70 insertions(+), 71 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ad3e0cd87f2e..5c4e8579e116 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.0.285 + rev: v0.0.286 hooks: - id: ruff @@ -33,7 +33,7 @@ repos: - tomli - repo: https://github.com/tox-dev/pyproject-fmt - rev: "0.13.1" + rev: "1.1.0" hooks: - id: pyproject-fmt diff --git a/DIRECTORY.md b/DIRECTORY.md index ebb164d0496c..43da91cb818e 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -245,7 +245,6 @@ * Stacks * [Balanced Parentheses](data_structures/stacks/balanced_parentheses.py) * [Dijkstras Two Stack Algorithm](data_structures/stacks/dijkstras_two_stack_algorithm.py) - * [Evaluate Postfix Notations](data_structures/stacks/evaluate_postfix_notations.py) * [Infix To Postfix Conversion](data_structures/stacks/infix_to_postfix_conversion.py) * [Infix To Prefix Conversion](data_structures/stacks/infix_to_prefix_conversion.py) * [Next Greater Element](data_structures/stacks/next_greater_element.py) diff --git a/arithmetic_analysis/jacobi_iteration_method.py b/arithmetic_analysis/jacobi_iteration_method.py index 17edf4bf4b8b..dba8a9ff44d3 100644 --- a/arithmetic_analysis/jacobi_iteration_method.py +++ b/arithmetic_analysis/jacobi_iteration_method.py @@ -152,9 +152,9 @@ def strictly_diagonally_dominant(table: NDArray[float64]) -> bool: is_diagonally_dominant = True - for i in range(0, rows): + for i in range(rows): total = 0 - for j in range(0, cols - 1): + for j in range(cols - 1): if i == j: continue else: diff --git a/arithmetic_analysis/secant_method.py b/arithmetic_analysis/secant_method.py index d28a46206d40..d39cb0ff30ef 100644 --- a/arithmetic_analysis/secant_method.py +++ b/arithmetic_analysis/secant_method.py @@ -20,7 +20,7 @@ def secant_method(lower_bound: float, upper_bound: float, repeats: int) -> float """ x0 = lower_bound x1 = upper_bound - for _ in range(0, repeats): + for _ in range(repeats): x0, x1 = x1, x1 - (f(x1) * (x1 - x0)) / (f(x1) - f(x0)) return x1 diff --git a/backtracking/hamiltonian_cycle.py b/backtracking/hamiltonian_cycle.py index 4a4156d70b32..e9916f83f861 100644 --- a/backtracking/hamiltonian_cycle.py +++ b/backtracking/hamiltonian_cycle.py @@ -95,7 +95,7 @@ def util_hamilton_cycle(graph: list[list[int]], path: list[int], curr_ind: int) return graph[path[curr_ind - 1]][path[0]] == 1 # Recursive Step - for next_ver in range(0, len(graph)): + for next_ver in range(len(graph)): if valid_connection(graph, next_ver, curr_ind, path): # Insert current vertex into path as next transition path[curr_ind] = next_ver diff --git a/backtracking/sudoku.py b/backtracking/sudoku.py index 698dedcc2125..6e4e3e8780f2 100644 --- a/backtracking/sudoku.py +++ b/backtracking/sudoku.py @@ -48,7 +48,7 @@ def is_safe(grid: Matrix, row: int, column: int, n: int) -> bool: is found) else returns True if it is 'safe' """ for i in range(9): - if grid[row][i] == n or grid[i][column] == n: + if n in {grid[row][i], grid[i][column]}: return False for i in range(3): diff --git a/bit_manipulation/reverse_bits.py b/bit_manipulation/reverse_bits.py index a8c77c11bfdd..74b4f2563234 100644 --- a/bit_manipulation/reverse_bits.py +++ b/bit_manipulation/reverse_bits.py @@ -20,7 +20,7 @@ def get_reverse_bit_string(number: int) -> str: ) raise TypeError(msg) bit_string = "" - for _ in range(0, 32): + for _ in range(32): bit_string += str(number % 2) number = number >> 1 return bit_string diff --git a/ciphers/trafid_cipher.py b/ciphers/trafid_cipher.py index 108ac652f0e4..8aa2263ca5ac 100644 --- a/ciphers/trafid_cipher.py +++ b/ciphers/trafid_cipher.py @@ -119,7 +119,7 @@ def decrypt_message( for i in range(0, len(message) + 1, period): a, b, c = __decrypt_part(message[i : i + period], character_to_number) - for j in range(0, len(a)): + for j in range(len(a)): decrypted_numeric.append(a[j] + b[j] + c[j]) for each in decrypted_numeric: diff --git a/data_structures/binary_tree/lazy_segment_tree.py b/data_structures/binary_tree/lazy_segment_tree.py index 050dfe0a6f2f..c26b0619380c 100644 --- a/data_structures/binary_tree/lazy_segment_tree.py +++ b/data_structures/binary_tree/lazy_segment_tree.py @@ -7,10 +7,10 @@ class SegmentTree: def __init__(self, size: int) -> None: self.size = size # approximate the overall size of segment tree with given value - self.segment_tree = [0 for i in range(0, 4 * size)] + self.segment_tree = [0 for i in range(4 * size)] # create array to store lazy update - self.lazy = [0 for i in range(0, 4 * size)] - self.flag = [0 for i in range(0, 4 * size)] # flag for lazy update + self.lazy = [0 for i in range(4 * size)] + self.flag = [0 for i in range(4 * size)] # flag for lazy update def left(self, idx: int) -> int: """ diff --git a/data_structures/linked_list/circular_linked_list.py b/data_structures/linked_list/circular_linked_list.py index 325d91026137..d9544f4263a6 100644 --- a/data_structures/linked_list/circular_linked_list.py +++ b/data_structures/linked_list/circular_linked_list.py @@ -125,7 +125,7 @@ def test_circular_linked_list() -> None: circular_linked_list.insert_tail(6) assert str(circular_linked_list) == "->".join(str(i) for i in range(1, 7)) circular_linked_list.insert_head(0) - assert str(circular_linked_list) == "->".join(str(i) for i in range(0, 7)) + assert str(circular_linked_list) == "->".join(str(i) for i in range(7)) assert circular_linked_list.delete_front() == 0 assert circular_linked_list.delete_tail() == 6 diff --git a/data_structures/linked_list/doubly_linked_list.py b/data_structures/linked_list/doubly_linked_list.py index 1a6c48191c4e..bd3445f9f6c5 100644 --- a/data_structures/linked_list/doubly_linked_list.py +++ b/data_structures/linked_list/doubly_linked_list.py @@ -98,7 +98,7 @@ def insert_at_nth(self, index: int, data): self.tail = new_node else: temp = self.head - for _ in range(0, index): + for _ in range(index): temp = temp.next temp.previous.next = new_node new_node.previous = temp.previous @@ -149,7 +149,7 @@ def delete_at_nth(self, index: int): self.tail.next = None else: temp = self.head - for _ in range(0, index): + for _ in range(index): temp = temp.next delete_node = temp temp.next.previous = temp.previous @@ -215,7 +215,7 @@ def test_doubly_linked_list() -> None: linked_list.insert_at_head(0) linked_list.insert_at_tail(11) - assert str(linked_list) == "->".join(str(i) for i in range(0, 12)) + assert str(linked_list) == "->".join(str(i) for i in range(12)) assert linked_list.delete_head() == 0 assert linked_list.delete_at_nth(9) == 10 diff --git a/data_structures/linked_list/is_palindrome.py b/data_structures/linked_list/is_palindrome.py index ec19e99f78c0..d540fb69f36b 100644 --- a/data_structures/linked_list/is_palindrome.py +++ b/data_structures/linked_list/is_palindrome.py @@ -68,7 +68,7 @@ def is_palindrome_dict(head): middle += 1 else: step = 0 - for i in range(0, len(v)): + for i in range(len(v)): if v[i] + v[len(v) - 1 - step] != checksum: return False step += 1 diff --git a/data_structures/linked_list/singly_linked_list.py b/data_structures/linked_list/singly_linked_list.py index 890e21c9b404..f4b2ddce12d7 100644 --- a/data_structures/linked_list/singly_linked_list.py +++ b/data_structures/linked_list/singly_linked_list.py @@ -370,7 +370,7 @@ def test_singly_linked_list() -> None: linked_list.insert_head(0) linked_list.insert_tail(11) - assert str(linked_list) == "->".join(str(i) for i in range(0, 12)) + assert str(linked_list) == "->".join(str(i) for i in range(12)) assert linked_list.delete_head() == 0 assert linked_list.delete_nth(9) == 10 @@ -378,11 +378,11 @@ def test_singly_linked_list() -> None: 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 + assert all(linked_list[i] == i + 1 for i in range(9)) is True - for i in range(0, 9): + for i in range(9): linked_list[i] = -i - assert all(linked_list[i] == -i for i in range(0, 9)) is True + assert all(linked_list[i] == -i for i in range(9)) is True linked_list.reverse() assert str(linked_list) == "->".join(str(i) for i in range(-8, 1)) diff --git a/data_structures/stacks/stock_span_problem.py b/data_structures/stacks/stock_span_problem.py index de423c1ebf66..5efe58d25798 100644 --- a/data_structures/stacks/stock_span_problem.py +++ b/data_structures/stacks/stock_span_problem.py @@ -36,7 +36,7 @@ def calculation_span(price, s): # A utility function to print elements of array def print_array(arr, n): - for i in range(0, n): + for i in range(n): print(arr[i], end=" ") diff --git a/digital_image_processing/filters/bilateral_filter.py b/digital_image_processing/filters/bilateral_filter.py index 565da73f6b0e..199ac4d9939a 100644 --- a/digital_image_processing/filters/bilateral_filter.py +++ b/digital_image_processing/filters/bilateral_filter.py @@ -31,8 +31,8 @@ def get_slice(img: np.ndarray, x: int, y: int, kernel_size: int) -> np.ndarray: def get_gauss_kernel(kernel_size: int, spatial_variance: float) -> np.ndarray: # Creates a gaussian kernel of given dimension. arr = np.zeros((kernel_size, kernel_size)) - for i in range(0, kernel_size): - for j in range(0, kernel_size): + for i in range(kernel_size): + for j in range(kernel_size): arr[i, j] = math.sqrt( abs(i - kernel_size // 2) ** 2 + abs(j - kernel_size // 2) ** 2 ) diff --git a/digital_image_processing/filters/convolve.py b/digital_image_processing/filters/convolve.py index 299682010da6..004402f29ba9 100644 --- a/digital_image_processing/filters/convolve.py +++ b/digital_image_processing/filters/convolve.py @@ -11,8 +11,8 @@ def im2col(image, block_size): dst_width = rows - block_size[0] + 1 image_array = zeros((dst_height * dst_width, block_size[1] * block_size[0])) row = 0 - for i in range(0, dst_height): - for j in range(0, dst_width): + for i in range(dst_height): + for j in range(dst_width): window = ravel(image[i : i + block_size[0], j : j + block_size[1]]) image_array[row, :] = window row += 1 diff --git a/digital_image_processing/filters/local_binary_pattern.py b/digital_image_processing/filters/local_binary_pattern.py index 907fe2cb0555..861369ba6a32 100644 --- a/digital_image_processing/filters/local_binary_pattern.py +++ b/digital_image_processing/filters/local_binary_pattern.py @@ -71,8 +71,8 @@ def local_binary_value(image: np.ndarray, x_coordinate: int, y_coordinate: int) # Iterating through the image and calculating the # local binary pattern value for each pixel. - for i in range(0, image.shape[0]): - for j in range(0, image.shape[1]): + for i in range(image.shape[0]): + for j in range(image.shape[1]): lbp_image[i][j] = local_binary_value(image, i, j) cv2.imshow("local binary pattern", lbp_image) diff --git a/digital_image_processing/test_digital_image_processing.py b/digital_image_processing/test_digital_image_processing.py index fee7ab247b55..528b4bc3b74c 100644 --- a/digital_image_processing/test_digital_image_processing.py +++ b/digital_image_processing/test_digital_image_processing.py @@ -118,8 +118,8 @@ def test_local_binary_pattern(): # Iterating through the image and calculating the local binary pattern value # for each pixel. - for i in range(0, image.shape[0]): - for j in range(0, image.shape[1]): + for i in range(image.shape[0]): + for j in range(image.shape[1]): lbp_image[i][j] = lbp.local_binary_value(image, i, j) assert lbp_image.any() diff --git a/divide_and_conquer/strassen_matrix_multiplication.py b/divide_and_conquer/strassen_matrix_multiplication.py index cbfc7e5655db..1d03950ef9fe 100644 --- a/divide_and_conquer/strassen_matrix_multiplication.py +++ b/divide_and_conquer/strassen_matrix_multiplication.py @@ -131,7 +131,7 @@ def strassen(matrix1: list, matrix2: list) -> list: # Adding zeros to the matrices so that the arrays dimensions are the same and also # power of 2 - for i in range(0, maxim): + for i in range(maxim): if i < dimension1[0]: for _ in range(dimension1[1], maxim): new_matrix1[i].append(0) @@ -146,7 +146,7 @@ def strassen(matrix1: list, matrix2: list) -> list: final_matrix = actual_strassen(new_matrix1, new_matrix2) # Removing the additional zeros - for i in range(0, maxim): + for i in range(maxim): if i < dimension1[0]: for _ in range(dimension2[1], maxim): final_matrix[i].pop() diff --git a/dynamic_programming/floyd_warshall.py b/dynamic_programming/floyd_warshall.py index 614a3c72a992..2331f3e65483 100644 --- a/dynamic_programming/floyd_warshall.py +++ b/dynamic_programming/floyd_warshall.py @@ -5,19 +5,19 @@ class Graph: def __init__(self, n=0): # a graph with Node 0,1,...,N-1 self.n = n self.w = [ - [math.inf for j in range(0, n)] for i in range(0, n) + [math.inf for j in range(n)] for i in range(n) ] # adjacency matrix for weight self.dp = [ - [math.inf for j in range(0, n)] for i in range(0, n) + [math.inf for j in range(n)] for i in range(n) ] # dp[i][j] stores minimum distance from i to j def add_edge(self, u, v, w): self.dp[u][v] = w def floyd_warshall(self): - for k in range(0, self.n): - for i in range(0, self.n): - for j in range(0, self.n): + for k in range(self.n): + for i in range(self.n): + for j in range(self.n): self.dp[i][j] = min(self.dp[i][j], self.dp[i][k] + self.dp[k][j]) def show_min(self, u, v): diff --git a/hashes/chaos_machine.py b/hashes/chaos_machine.py index 238fdb1c0634..d2fde2f5e371 100644 --- a/hashes/chaos_machine.py +++ b/hashes/chaos_machine.py @@ -53,7 +53,7 @@ def xorshift(x, y): key = machine_time % m # Evolution (Time Length) - for _ in range(0, t): + for _ in range(t): # Variables (Position + Parameters) r = params_space[key] value = buffer_space[key] diff --git a/hashes/hamming_code.py b/hashes/hamming_code.py index dc93032183e0..8498ca920b36 100644 --- a/hashes/hamming_code.py +++ b/hashes/hamming_code.py @@ -135,7 +135,7 @@ def emitter_converter(size_par, data): # Mount the message cont_bp = 0 # parity bit counter - for x in range(0, size_par + len(data)): + for x in range(size_par + len(data)): if data_ord[x] is None: data_out.append(str(parity[cont_bp])) cont_bp += 1 @@ -228,7 +228,7 @@ def receptor_converter(size_par, data): # Mount the message cont_bp = 0 # Parity bit counter - for x in range(0, size_par + len(data_output)): + for x in range(size_par + len(data_output)): if data_ord[x] is None: data_out.append(str(parity[cont_bp])) cont_bp += 1 diff --git a/hashes/sha1.py b/hashes/sha1.py index b325ce3e43bb..8a03673f3c9f 100644 --- a/hashes/sha1.py +++ b/hashes/sha1.py @@ -97,7 +97,7 @@ def final_hash(self): for block in self.blocks: expanded_block = self.expand_block(block) a, b, c, d, e = self.h - for i in range(0, 80): + for i in range(80): if 0 <= i < 20: f = (b & c) | ((~b) & d) k = 0x5A827999 diff --git a/hashes/sha256.py b/hashes/sha256.py index 98f7c096e3b6..ba9aff8dbf41 100644 --- a/hashes/sha256.py +++ b/hashes/sha256.py @@ -138,7 +138,7 @@ def final_hash(self) -> None: a, b, c, d, e, f, g, h = self.hashes - for index in range(0, 64): + for index in range(64): if index > 15: # modify the zero-ed indexes at the end of the array s0 = ( diff --git a/machine_learning/gradient_descent.py b/machine_learning/gradient_descent.py index 5b74dad082e7..9ffc02bbc284 100644 --- a/machine_learning/gradient_descent.py +++ b/machine_learning/gradient_descent.py @@ -110,7 +110,7 @@ def run_gradient_descent(): while True: j += 1 temp_parameter_vector = [0, 0, 0, 0] - for i in range(0, len(parameter_vector)): + for i in range(len(parameter_vector)): cost_derivative = get_cost_derivative(i - 1) temp_parameter_vector[i] = ( parameter_vector[i] - LEARNING_RATE * cost_derivative diff --git a/machine_learning/linear_regression.py b/machine_learning/linear_regression.py index 75943ac9f2ad..0847112ad538 100644 --- a/machine_learning/linear_regression.py +++ b/machine_learning/linear_regression.py @@ -78,7 +78,7 @@ def run_linear_regression(data_x, data_y): theta = np.zeros((1, no_features)) - for i in range(0, iterations): + for i in range(iterations): theta = run_steep_gradient_descent(data_x, data_y, len_data, alpha, theta) error = sum_of_square_error(data_x, data_y, len_data, theta) print(f"At Iteration {i + 1} - Error is {error:.5f}") @@ -107,7 +107,7 @@ def main(): theta = run_linear_regression(data_x, data_y) len_result = theta.shape[1] print("Resultant Feature vector : ") - for i in range(0, len_result): + for i in range(len_result): print(f"{theta[0, i]:.5f}") diff --git a/machine_learning/lstm/lstm_prediction.py b/machine_learning/lstm/lstm_prediction.py index 74197c46a0ad..16530e935ea7 100644 --- a/machine_learning/lstm/lstm_prediction.py +++ b/machine_learning/lstm/lstm_prediction.py @@ -32,10 +32,10 @@ train_x, train_y = [], [] test_x, test_y = [], [] - for i in range(0, len(train_data) - forward_days - look_back + 1): + for i in range(len(train_data) - forward_days - look_back + 1): train_x.append(train_data[i : i + look_back]) train_y.append(train_data[i + look_back : i + look_back + forward_days]) - for i in range(0, len(test_data) - forward_days - look_back + 1): + for i in range(len(test_data) - forward_days - look_back + 1): test_x.append(test_data[i : i + look_back]) test_y.append(test_data[i + look_back : i + look_back + forward_days]) x_train = np.array(train_x) diff --git a/maths/entropy.py b/maths/entropy.py index 498c28f31bc4..23753d884484 100644 --- a/maths/entropy.py +++ b/maths/entropy.py @@ -101,7 +101,7 @@ def analyze_text(text: str) -> tuple[dict, dict]: # first case when we have space at start. two_char_strings[" " + text[0]] += 1 - for i in range(0, len(text) - 1): + for i in range(len(text) - 1): single_char_strings[text[i]] += 1 two_char_strings[text[i : i + 2]] += 1 return single_char_strings, two_char_strings diff --git a/maths/eulers_totient.py b/maths/eulers_totient.py index a156647037b4..00f0254c215a 100644 --- a/maths/eulers_totient.py +++ b/maths/eulers_totient.py @@ -21,7 +21,7 @@ def totient(n: int) -> list: for i in range(2, n + 1): if is_prime[i]: primes.append(i) - for j in range(0, len(primes)): + for j in range(len(primes)): if i * primes[j] >= n: break is_prime[i * primes[j]] = False diff --git a/maths/greedy_coin_change.py b/maths/greedy_coin_change.py index 7cf669bcb8cb..db2c381bc84a 100644 --- a/maths/greedy_coin_change.py +++ b/maths/greedy_coin_change.py @@ -81,7 +81,7 @@ def find_minimum_change(denominations: list[int], value: str) -> list[int]: ): n = int(input("Enter the number of denominations you want to add: ").strip()) - for i in range(0, n): + for i in range(n): denominations.append(int(input(f"Denomination {i}: ").strip())) value = input("Enter the change you want to make in Indian Currency: ").strip() else: diff --git a/maths/persistence.py b/maths/persistence.py index 607641e67200..c61a69a7c27d 100644 --- a/maths/persistence.py +++ b/maths/persistence.py @@ -28,7 +28,7 @@ def multiplicative_persistence(num: int) -> int: numbers = [int(i) for i in num_string] total = 1 - for i in range(0, len(numbers)): + for i in range(len(numbers)): total *= numbers[i] num_string = str(total) @@ -67,7 +67,7 @@ def additive_persistence(num: int) -> int: numbers = [int(i) for i in num_string] total = 0 - for i in range(0, len(numbers)): + for i in range(len(numbers)): total += numbers[i] num_string = str(total) diff --git a/maths/series/harmonic.py b/maths/series/harmonic.py index 50f29c93dd5f..35792d38af9b 100644 --- a/maths/series/harmonic.py +++ b/maths/series/harmonic.py @@ -45,7 +45,7 @@ def is_harmonic_series(series: list) -> bool: return True rec_series = [] series_len = len(series) - for i in range(0, series_len): + for i in range(series_len): if series[i] == 0: raise ValueError("Input series cannot have 0 as an element") rec_series.append(1 / series[i]) diff --git a/matrix/spiral_print.py b/matrix/spiral_print.py index 0d0be1527aec..5eef263f7aef 100644 --- a/matrix/spiral_print.py +++ b/matrix/spiral_print.py @@ -54,7 +54,7 @@ def spiral_print_clockwise(a: list[list[int]]) -> None: return # horizotal printing increasing - for i in range(0, mat_col): + for i in range(mat_col): print(a[0][i]) # vertical printing down for i in range(1, mat_row): diff --git a/other/magicdiamondpattern.py b/other/magicdiamondpattern.py index 0fc41d7a25d8..89b973bb41e8 100644 --- a/other/magicdiamondpattern.py +++ b/other/magicdiamondpattern.py @@ -7,10 +7,10 @@ def floyd(n): Parameters: n : size of pattern """ - for i in range(0, n): - for _ in range(0, n - i - 1): # printing spaces + for i in range(n): + for _ in range(n - i - 1): # printing spaces print(" ", end="") - for _ in range(0, i + 1): # printing stars + for _ in range(i + 1): # printing stars print("* ", end="") print() diff --git a/project_euler/problem_070/sol1.py b/project_euler/problem_070/sol1.py index 273f37efc5fc..57a6c1916374 100644 --- a/project_euler/problem_070/sol1.py +++ b/project_euler/problem_070/sol1.py @@ -44,7 +44,7 @@ def get_totients(max_one: int) -> list[int]: """ totients = [0] * max_one - for i in range(0, max_one): + for i in range(max_one): totients[i] = i for i in range(2, max_one): diff --git a/project_euler/problem_112/sol1.py b/project_euler/problem_112/sol1.py index b3ea6b35654a..31996d070771 100644 --- a/project_euler/problem_112/sol1.py +++ b/project_euler/problem_112/sol1.py @@ -49,7 +49,7 @@ def check_bouncy(n: int) -> bool: raise ValueError("check_bouncy() accepts only integer arguments") str_n = str(n) sorted_str_n = "".join(sorted(str_n)) - return sorted_str_n != str_n and sorted_str_n[::-1] != str_n + return str_n not in {sorted_str_n, sorted_str_n[::-1]} def solution(percent: float = 99) -> int: diff --git a/quantum/q_full_adder.py b/quantum/q_full_adder.py index 66d93198519e..ec4efa4346a5 100644 --- a/quantum/q_full_adder.py +++ b/quantum/q_full_adder.py @@ -88,7 +88,7 @@ def quantum_full_adder( quantum_circuit = qiskit.QuantumCircuit(qr, cr) - for i in range(0, 3): + for i in range(3): if entry[i] == 2: quantum_circuit.h(i) # for hadamard entries elif entry[i] == 1: diff --git a/scheduling/highest_response_ratio_next.py b/scheduling/highest_response_ratio_next.py index 9c999ec65053..057bd64cc729 100644 --- a/scheduling/highest_response_ratio_next.py +++ b/scheduling/highest_response_ratio_next.py @@ -53,7 +53,7 @@ def calculate_turn_around_time( loc = 0 # Saves the current response ratio. temp = 0 - for i in range(0, no_of_process): + for i in range(no_of_process): if finished_process[i] == 0 and arrival_time[i] <= current_time: temp = (burst_time[i] + (current_time - arrival_time[i])) / burst_time[ i @@ -87,7 +87,7 @@ def calculate_waiting_time( """ waiting_time = [0] * no_of_process - for i in range(0, no_of_process): + for i in range(no_of_process): waiting_time[i] = turn_around_time[i] - burst_time[i] return waiting_time @@ -106,7 +106,7 @@ def calculate_waiting_time( ) print("Process name \tArrival time \tBurst time \tTurn around time \tWaiting time") - for i in range(0, no_of_process): + for i in range(no_of_process): print( f"{process_name[i]}\t\t{arrival_time[i]}\t\t{burst_time[i]}\t\t" f"{turn_around_time[i]}\t\t\t{waiting_time[i]}" diff --git a/sorts/counting_sort.py b/sorts/counting_sort.py index 18c4b0323dcb..256952df52d2 100644 --- a/sorts/counting_sort.py +++ b/sorts/counting_sort.py @@ -49,7 +49,7 @@ def counting_sort(collection): # place the elements in the output, respecting the original order (stable # sort) from end to begin, updating counting_arr - for i in reversed(range(0, coll_len)): + for i in reversed(range(coll_len)): ordered[counting_arr[collection[i] - coll_min] - 1] = collection[i] counting_arr[collection[i] - coll_min] -= 1 diff --git a/sorts/cycle_sort.py b/sorts/cycle_sort.py index 806f40441d79..7177c8ea110d 100644 --- a/sorts/cycle_sort.py +++ b/sorts/cycle_sort.py @@ -19,7 +19,7 @@ def cycle_sort(array: list) -> list: [] """ array_len = len(array) - for cycle_start in range(0, array_len - 1): + for cycle_start in range(array_len - 1): item = array[cycle_start] pos = cycle_start diff --git a/sorts/double_sort.py b/sorts/double_sort.py index 5ca88a6745d5..a19641d94752 100644 --- a/sorts/double_sort.py +++ b/sorts/double_sort.py @@ -16,9 +16,9 @@ def double_sort(lst): """ no_of_elements = len(lst) for _ in range( - 0, int(((no_of_elements - 1) / 2) + 1) + int(((no_of_elements - 1) / 2) + 1) ): # we don't need to traverse to end of list as - for j in range(0, no_of_elements - 1): + for j in range(no_of_elements - 1): if ( lst[j + 1] < lst[j] ): # applying bubble sort algorithm from left to right (or forwards) diff --git a/sorts/odd_even_transposition_parallel.py b/sorts/odd_even_transposition_parallel.py index 87b0e4d1e20f..9e0d228bdc5b 100644 --- a/sorts/odd_even_transposition_parallel.py +++ b/sorts/odd_even_transposition_parallel.py @@ -33,7 +33,7 @@ def oe_process(position, value, l_send, r_send, lr_cv, rr_cv, result_pipe): # we perform n swaps since after n swaps we know we are sorted # we *could* stop early if we are sorted already, but it takes as long to # find out we are sorted as it does to sort the list with this algorithm - for i in range(0, 10): + for i in range(10): if (i + position) % 2 == 0 and r_send is not None: # send your value to your right neighbor process_lock.acquire() @@ -123,7 +123,7 @@ def odd_even_transposition(arr): p.start() # wait for the processes to end and write their values to the list - for p in range(0, len(result_pipe)): + for p in range(len(result_pipe)): arr[p] = result_pipe[p][0].recv() process_array_[p].join() return arr diff --git a/strings/rabin_karp.py b/strings/rabin_karp.py index 81ca611a76b3..532c689f8a97 100644 --- a/strings/rabin_karp.py +++ b/strings/rabin_karp.py @@ -38,7 +38,7 @@ def rabin_karp(pattern: str, text: str) -> bool: continue modulus_power = (modulus_power * alphabet_size) % modulus - for i in range(0, t_len - p_len + 1): + for i in range(t_len - p_len + 1): if text_hash == p_hash and text[i : i + p_len] == pattern: return True if i == t_len - p_len: From 5a4ea233cd30723628fb184bc05f969ad463b0af Mon Sep 17 00:00:00 2001 From: Kotmin <70173732+Kotmin@users.noreply.github.com> Date: Mon, 4 Sep 2023 19:38:26 +0200 Subject: [PATCH 183/757] Style sigmoid function in harmony with pep guideness (#6677) * Style sigmoid function in harmony with pep guideness * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Apply suggestions from code review --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Tianyi Zheng --- neural_network/back_propagation_neural_network.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/neural_network/back_propagation_neural_network.py b/neural_network/back_propagation_neural_network.py index 9dd112115f5e..bdd096b3f653 100644 --- a/neural_network/back_propagation_neural_network.py +++ b/neural_network/back_propagation_neural_network.py @@ -21,8 +21,8 @@ from matplotlib import pyplot as plt -def sigmoid(x): - return 1 / (1 + np.exp(-1 * x)) +def sigmoid(x: np.ndarray) -> np.ndarray: + return 1 / (1 + np.exp(-x)) class DenseLayer: From ac73be217863cc78af97bb86a9156ac38c4ae1e5 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 5 Sep 2023 08:27:05 +0530 Subject: [PATCH 184/757] [pre-commit.ci] pre-commit autoupdate (#9042) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5c4e8579e116..c046789463cc 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.0.286 + rev: v0.0.287 hooks: - id: ruff From 79b043d35ca266cf5053f5b62b2fe0f7bc6344d9 Mon Sep 17 00:00:00 2001 From: Rafael Zimmer Date: Tue, 5 Sep 2023 01:04:36 -0300 Subject: [PATCH 185/757] Texture analysis using Haralick Descriptors for Computer Vision tasks (#8004) * Create haralick_descriptors * Working on creating Unit Testing for Haralick Descriptors module * Type hinting for Haralick descriptors * Fixed docstrings, unit testing and formatting choices * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fixed line size formatting * Added final doctests * Changed main callable * Updated requirements.txt * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update computer_vision/haralick_descriptors.py No! What if the Kernel is empty? Example: >>> kernel = np.zeros((1)) >>> kernel or np.ones((3, 3)) array([[1., 1., 1.], [1., 1., 1.], [1., 1., 1.]]) Co-authored-by: Christian Clauss * Undone wrong commit * Update haralick_descriptors.py * Apply suggestions from code review * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix ruff errors in haralick_descriptors.py * Add type hint to haralick_descriptors.py to fix ruff error * Update haralick_descriptors.py * Update haralick_descriptors.py * Update haralick_descriptors.py * Update haralick_descriptors.py * Try to fix mypy errors in haralick_descriptors.py * Update haralick_descriptors.py * Fix type hint in haralick_descriptors.py --------- Co-authored-by: Rafael Zimmer Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss Co-authored-by: Tianyi Zheng --- computer_vision/haralick_descriptors.py | 431 ++++++++++++++++++++++++ requirements.txt | 1 + 2 files changed, 432 insertions(+) create mode 100644 computer_vision/haralick_descriptors.py diff --git a/computer_vision/haralick_descriptors.py b/computer_vision/haralick_descriptors.py new file mode 100644 index 000000000000..1a86d84ea14b --- /dev/null +++ b/computer_vision/haralick_descriptors.py @@ -0,0 +1,431 @@ +""" +https://en.wikipedia.org/wiki/Image_texture +https://en.wikipedia.org/wiki/Co-occurrence_matrix#Application_to_image_analysis +""" +import imageio.v2 as imageio +import numpy as np + + +def root_mean_square_error(original: np.ndarray, reference: np.ndarray) -> float: + """Simple implementation of Root Mean Squared Error + for two N dimensional numpy arrays. + + Examples: + >>> root_mean_square_error(np.array([1, 2, 3]), np.array([1, 2, 3])) + 0.0 + >>> root_mean_square_error(np.array([1, 2, 3]), np.array([2, 2, 2])) + 0.816496580927726 + >>> root_mean_square_error(np.array([1, 2, 3]), np.array([6, 4, 2])) + 3.1622776601683795 + """ + return np.sqrt(((original - reference) ** 2).mean()) + + +def normalize_image( + image: np.ndarray, cap: float = 255.0, data_type: np.dtype = np.uint8 +) -> np.ndarray: + """ + Normalizes image in Numpy 2D array format, between ranges 0-cap, + as to fit uint8 type. + + Args: + image: 2D numpy array representing image as matrix, with values in any range + cap: Maximum cap amount for normalization + data_type: numpy data type to set output variable to + Returns: + return 2D numpy array of type uint8, corresponding to limited range matrix + + Examples: + >>> normalize_image(np.array([[1, 2, 3], [4, 5, 10]]), + ... cap=1.0, data_type=np.float64) + array([[0. , 0.11111111, 0.22222222], + [0.33333333, 0.44444444, 1. ]]) + >>> normalize_image(np.array([[4, 4, 3], [1, 7, 2]])) + array([[127, 127, 85], + [ 0, 255, 42]], dtype=uint8) + """ + normalized = (image - np.min(image)) / (np.max(image) - np.min(image)) * cap + return normalized.astype(data_type) + + +def normalize_array(array: np.ndarray, cap: float = 1) -> np.ndarray: + """Normalizes a 1D array, between ranges 0-cap. + + Args: + array: List containing values to be normalized between cap range. + cap: Maximum cap amount for normalization. + Returns: + return 1D numpy array, corresponding to limited range array + + Examples: + >>> normalize_array(np.array([2, 3, 5, 7])) + array([0. , 0.2, 0.6, 1. ]) + >>> normalize_array(np.array([[5], [7], [11], [13]])) + array([[0. ], + [0.25], + [0.75], + [1. ]]) + """ + diff = np.max(array) - np.min(array) + return (array - np.min(array)) / (1 if diff == 0 else diff) * cap + + +def grayscale(image: np.ndarray) -> np.ndarray: + """ + Uses luminance weights to transform RGB channel to greyscale, by + taking the dot product between the channel and the weights. + + Example: + >>> grayscale(np.array([[[108, 201, 72], [255, 11, 127]], + ... [[56, 56, 56], [128, 255, 107]]])) + array([[158, 97], + [ 56, 200]], dtype=uint8) + """ + return np.dot(image[:, :, 0:3], [0.299, 0.587, 0.114]).astype(np.uint8) + + +def binarize(image: np.ndarray, threshold: float = 127.0) -> np.ndarray: + """ + Binarizes a grayscale image based on a given threshold value, + setting values to 1 or 0 accordingly. + + Examples: + >>> binarize(np.array([[128, 255], [101, 156]])) + array([[1, 1], + [0, 1]]) + >>> binarize(np.array([[0.07, 1], [0.51, 0.3]]), threshold=0.5) + array([[0, 1], + [1, 0]]) + """ + return np.where(image > threshold, 1, 0) + + +def transform(image: np.ndarray, kind: str, kernel: np.ndarray = None) -> np.ndarray: + """ + Simple image transformation using one of two available filter functions: + Erosion and Dilation. + + Args: + image: binarized input image, onto which to apply transformation + kind: Can be either 'erosion', in which case the :func:np.max + function is called, or 'dilation', when :func:np.min is used instead. + kernel: n x n kernel with shape < :attr:image.shape, + to be used when applying convolution to original image + + Returns: + returns a numpy array with same shape as input image, + corresponding to applied binary transformation. + + Examples: + >>> img = np.array([[1, 0.5], [0.2, 0.7]]) + >>> img = binarize(img, threshold=0.5) + >>> transform(img, 'erosion') + array([[1, 1], + [1, 1]], dtype=uint8) + >>> transform(img, 'dilation') + array([[0, 0], + [0, 0]], dtype=uint8) + """ + if kernel is None: + kernel = np.ones((3, 3)) + + if kind == "erosion": + constant = 1 + apply = np.max + else: + constant = 0 + apply = np.min + + center_x, center_y = (x // 2 for x in kernel.shape) + + # Use padded image when applying convolotion + # to not go out of bounds of the original the image + transformed = np.zeros(image.shape, dtype=np.uint8) + padded = np.pad(image, 1, "constant", constant_values=constant) + + for x in range(center_x, padded.shape[0] - center_x): + for y in range(center_y, padded.shape[1] - center_y): + center = padded[ + x - center_x : x + center_x + 1, y - center_y : y + center_y + 1 + ] + # Apply transformation method to the centered section of the image + transformed[x - center_x, y - center_y] = apply(center[kernel == 1]) + + return transformed + + +def opening_filter(image: np.ndarray, kernel: np.ndarray = None) -> np.ndarray: + """ + Opening filter, defined as the sequence of + erosion and then a dilation filter on the same image. + + Examples: + >>> img = np.array([[1, 0.5], [0.2, 0.7]]) + >>> img = binarize(img, threshold=0.5) + >>> opening_filter(img) + array([[1, 1], + [1, 1]], dtype=uint8) + """ + if kernel is None: + np.ones((3, 3)) + + return transform(transform(image, "dilation", kernel), "erosion", kernel) + + +def closing_filter(image: np.ndarray, kernel: np.ndarray = None) -> np.ndarray: + """ + Opening filter, defined as the sequence of + dilation and then erosion filter on the same image. + + Examples: + >>> img = np.array([[1, 0.5], [0.2, 0.7]]) + >>> img = binarize(img, threshold=0.5) + >>> closing_filter(img) + array([[0, 0], + [0, 0]], dtype=uint8) + """ + if kernel is None: + kernel = np.ones((3, 3)) + return transform(transform(image, "erosion", kernel), "dilation", kernel) + + +def binary_mask( + image_gray: np.ndarray, image_map: np.ndarray +) -> tuple[np.ndarray, np.ndarray]: + """ + Apply binary mask, or thresholding based + on bit mask value (mapping mask is binary). + + Returns the mapped true value mask and its complementary false value mask. + + Example: + >>> img = np.array([[[108, 201, 72], [255, 11, 127]], + ... [[56, 56, 56], [128, 255, 107]]]) + >>> gray = grayscale(img) + >>> binary = binarize(gray) + >>> morphological = opening_filter(binary) + >>> binary_mask(gray, morphological) + (array([[1, 1], + [1, 1]], dtype=uint8), array([[158, 97], + [ 56, 200]], dtype=uint8)) + """ + true_mask, false_mask = image_gray.copy(), image_gray.copy() + true_mask[image_map == 1] = 1 + false_mask[image_map == 0] = 0 + + return true_mask, false_mask + + +def matrix_concurrency(image: np.ndarray, coordinate: tuple[int, int]) -> np.ndarray: + """ + Calculate sample co-occurrence matrix based on input image + as well as selected coordinates on image. + + Implementation is made using basic iteration, + as function to be performed (np.max) is non-linear and therefore + not callable on the frequency domain. + + Example: + >>> img = np.array([[[108, 201, 72], [255, 11, 127]], + ... [[56, 56, 56], [128, 255, 107]]]) + >>> gray = grayscale(img) + >>> binary = binarize(gray) + >>> morphological = opening_filter(binary) + >>> mask_1 = binary_mask(gray, morphological)[0] + >>> matrix_concurrency(mask_1, (0, 1)) + array([[0., 0.], + [0., 0.]]) + """ + matrix = np.zeros([np.max(image) + 1, np.max(image) + 1]) + + offset_x, offset_y = coordinate + + for x in range(1, image.shape[0] - 1): + for y in range(1, image.shape[1] - 1): + base_pixel = image[x, y] + offset_pixel = image[x + offset_x, y + offset_y] + + matrix[base_pixel, offset_pixel] += 1 + matrix_sum = np.sum(matrix) + return matrix / (1 if matrix_sum == 0 else matrix_sum) + + +def haralick_descriptors(matrix: np.ndarray) -> list[float]: + """Calculates all 8 Haralick descriptors based on co-occurence input matrix. + All descriptors are as follows: + Maximum probability, Inverse Difference, Homogeneity, Entropy, + Energy, Dissimilarity, Contrast and Correlation + + Args: + matrix: Co-occurence matrix to use as base for calculating descriptors. + + Returns: + Reverse ordered list of resulting descriptors + + Example: + >>> img = np.array([[[108, 201, 72], [255, 11, 127]], + ... [[56, 56, 56], [128, 255, 107]]]) + >>> gray = grayscale(img) + >>> binary = binarize(gray) + >>> morphological = opening_filter(binary) + >>> mask_1 = binary_mask(gray, morphological)[0] + >>> concurrency = matrix_concurrency(mask_1, (0, 1)) + >>> haralick_descriptors(concurrency) + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] + """ + # Function np.indices could be used for bigger input types, + # but np.ogrid works just fine + i, j = np.ogrid[0 : matrix.shape[0], 0 : matrix.shape[1]] # np.indices() + + # Pre-calculate frequent multiplication and subtraction + prod = np.multiply(i, j) + sub = np.subtract(i, j) + + # Calculate numerical value of Maximum Probability + maximum_prob = np.max(matrix) + # Using the definition for each descriptor individually to calculate its matrix + correlation = prod * matrix + energy = np.power(matrix, 2) + contrast = matrix * np.power(sub, 2) + + dissimilarity = matrix * np.abs(sub) + inverse_difference = matrix / (1 + np.abs(sub)) + homogeneity = matrix / (1 + np.power(sub, 2)) + entropy = -(matrix[matrix > 0] * np.log(matrix[matrix > 0])) + + # Sum values for descriptors ranging from the first one to the last, + # as all are their respective origin matrix and not the resulting value yet. + return [ + maximum_prob, + correlation.sum(), + energy.sum(), + contrast.sum(), + dissimilarity.sum(), + inverse_difference.sum(), + homogeneity.sum(), + entropy.sum(), + ] + + +def get_descriptors( + masks: tuple[np.ndarray, np.ndarray], coordinate: tuple[int, int] +) -> np.ndarray: + """ + Calculate all Haralick descriptors for a sequence of + different co-occurrence matrices, given input masks and coordinates. + + Example: + >>> img = np.array([[[108, 201, 72], [255, 11, 127]], + ... [[56, 56, 56], [128, 255, 107]]]) + >>> gray = grayscale(img) + >>> binary = binarize(gray) + >>> morphological = opening_filter(binary) + >>> get_descriptors(binary_mask(gray, morphological), (0, 1)) + array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]) + """ + descriptors = np.array( + [haralick_descriptors(matrix_concurrency(mask, coordinate)) for mask in masks] + ) + + # Concatenate each individual descriptor into + # one single list containing sequence of descriptors + return np.concatenate(descriptors, axis=None) + + +def euclidean(point_1: np.ndarray, point_2: np.ndarray) -> np.float32: + """ + Simple method for calculating the euclidean distance between two points, + with type np.ndarray. + + Example: + >>> a = np.array([1, 0, -2]) + >>> b = np.array([2, -1, 1]) + >>> euclidean(a, b) + 3.3166247903554 + """ + return np.sqrt(np.sum(np.square(point_1 - point_2))) + + +def get_distances(descriptors: np.ndarray, base: int) -> list[tuple[int, float]]: + """ + Calculate all Euclidean distances between a selected base descriptor + and all other Haralick descriptors + The resulting comparison is return in decreasing order, + showing which descriptor is the most similar to the selected base. + + Args: + descriptors: Haralick descriptors to compare with base index + base: Haralick descriptor index to use as base when calculating respective + euclidean distance to other descriptors. + + Returns: + Ordered distances between descriptors + + Example: + >>> index = 1 + >>> img = np.array([[[108, 201, 72], [255, 11, 127]], + ... [[56, 56, 56], [128, 255, 107]]]) + >>> gray = grayscale(img) + >>> binary = binarize(gray) + >>> morphological = opening_filter(binary) + >>> get_distances(get_descriptors( + ... binary_mask(gray, morphological), (0, 1)), + ... index) + [(0, 0.0), (1, 0.0), (2, 0.0), (3, 0.0), (4, 0.0), (5, 0.0), \ +(6, 0.0), (7, 0.0), (8, 0.0), (9, 0.0), (10, 0.0), (11, 0.0), (12, 0.0), \ +(13, 0.0), (14, 0.0), (15, 0.0)] + """ + distances = np.array( + [euclidean(descriptor, descriptors[base]) for descriptor in descriptors] + ) + # Normalize distances between range [0, 1] + normalized_distances: list[float] = normalize_array(distances, 1).tolist() + enum_distances = list(enumerate(normalized_distances)) + enum_distances.sort(key=lambda tup: tup[1], reverse=True) + return enum_distances + + +if __name__ == "__main__": + # Index to compare haralick descriptors to + index = int(input()) + q_value_list = [int(value) for value in input().split()] + q_value = (q_value_list[0], q_value_list[1]) + + # Format is the respective filter to apply, + # can be either 1 for the opening filter or else for the closing + parameters = {"format": int(input()), "threshold": int(input())} + + # Number of images to perform methods on + b_number = int(input()) + + files, descriptors = [], [] + + for _ in range(b_number): + file = input().rstrip() + files.append(file) + + # Open given image and calculate morphological filter, + # respective masks and correspondent Harralick Descriptors. + image = imageio.imread(file).astype(np.float32) + gray = grayscale(image) + threshold = binarize(gray, parameters["threshold"]) + + morphological = ( + opening_filter(threshold) + if parameters["format"] == 1 + else closing_filter(threshold) + ) + masks = binary_mask(gray, morphological) + descriptors.append(get_descriptors(masks, q_value)) + + # Transform ordered distances array into a sequence of indexes + # corresponding to original file position + distances = get_distances(np.array(descriptors), index) + indexed_distances = np.array(distances).astype(np.uint8)[:, 0] + + # Finally, print distances considering the Haralick descriptions from the base + # file to all other images using the morphology method of choice. + print(f"Query: {files[index]}") + print("Ranking:") + for idx, file_idx in enumerate(indexed_distances): + print(f"({idx}) {files[file_idx]}", end="\n") diff --git a/requirements.txt b/requirements.txt index 2702523d542e..1128e9d66820 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,6 @@ beautifulsoup4 fake_useragent +imageio keras lxml matplotlib From 72f600036511c4999fa56bf007bf92ec465e94d7 Mon Sep 17 00:00:00 2001 From: Caeden Perelli-Harris Date: Tue, 5 Sep 2023 05:49:00 +0100 Subject: [PATCH 186/757] Fix get amazon product data erroring due to whitespace in headers (#9009) * updating DIRECTORY.md * fix(get-amazon-product-data): Remove whitespace in headers * refactor(get-amazon-product-data): Don't print to_csv --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- web_programming/get_amazon_product_data.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/web_programming/get_amazon_product_data.py b/web_programming/get_amazon_product_data.py index c796793f2205..a16175688667 100644 --- a/web_programming/get_amazon_product_data.py +++ b/web_programming/get_amazon_product_data.py @@ -19,11 +19,13 @@ def get_amazon_product_data(product: str = "laptop") -> DataFrame: """ url = f"https://www.amazon.in/laptop/s?k={product}" header = { - "User-Agent": """Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 - (KHTML, like Gecko)Chrome/44.0.2403.157 Safari/537.36""", + "User-Agent": ( + "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36" + "(KHTML, like Gecko)Chrome/44.0.2403.157 Safari/537.36" + ), "Accept-Language": "en-US, en;q=0.5", } - soup = BeautifulSoup(requests.get(url, headers=header).text) + soup = BeautifulSoup(requests.get(url, headers=header).text, features="lxml") # Initialize a Pandas dataframe with the column titles data_frame = DataFrame( columns=[ @@ -74,8 +76,8 @@ def get_amazon_product_data(product: str = "laptop") -> DataFrame: except ValueError: discount = float("nan") except AttributeError: - pass - data_frame.loc[len(data_frame.index)] = [ + continue + data_frame.loc[str(len(data_frame.index))] = [ product_title, product_link, product_price, From 9e4f9962a02ae584b392670a13d54ef8731e8f7f Mon Sep 17 00:00:00 2001 From: David Ekong <66387173+davidekong@users.noreply.github.com> Date: Wed, 6 Sep 2023 15:00:09 +0100 Subject: [PATCH 187/757] Created harshad_numbers.py (#9023) * Created harshad_numbers.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update harshad_numbers.py Fixed a few errors * Update harshad_numbers.py Added function type hints * Update harshad_numbers.py Fixed depreciated Tuple and List usage * Update harshad_numbers.py Fixed incompatible types in assignments * Update harshad_numbers.py Fixed incompatible type assignments * Update maths/harshad_numbers.py Co-authored-by: Tianyi Zheng * Update maths/harshad_numbers.py Co-authored-by: Tianyi Zheng * Raised Value Error for negative inputs * Update maths/harshad_numbers.py Co-authored-by: Tianyi Zheng * Update maths/harshad_numbers.py Co-authored-by: Tianyi Zheng * Update maths/harshad_numbers.py Co-authored-by: Tianyi Zheng * Update harshad_numbers.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update harshad_numbers.py * Update harshad_numbers.py * Update harshad_numbers.py * Update harshad_numbers.py Added doc test to int_to_base, fixed nested loop, other minor changes * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Tianyi Zheng --- maths/harshad_numbers.py | 158 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 maths/harshad_numbers.py diff --git a/maths/harshad_numbers.py b/maths/harshad_numbers.py new file mode 100644 index 000000000000..050c69e0bd15 --- /dev/null +++ b/maths/harshad_numbers.py @@ -0,0 +1,158 @@ +""" +A harshad number (or more specifically an n-harshad number) is a number that's +divisible by the sum of its digits in some given base n. +Reference: https://en.wikipedia.org/wiki/Harshad_number +""" + + +def int_to_base(number: int, base: int) -> str: + """ + Convert a given positive decimal integer to base 'base'. + Where 'base' ranges from 2 to 36. + + Examples: + >>> int_to_base(23, 2) + '10111' + >>> int_to_base(58, 5) + '213' + >>> int_to_base(167, 16) + 'A7' + >>> # bases below 2 and beyond 36 will error + >>> int_to_base(98, 1) + Traceback (most recent call last): + ... + ValueError: 'base' must be between 2 and 36 inclusive + >>> int_to_base(98, 37) + Traceback (most recent call last): + ... + ValueError: 'base' must be between 2 and 36 inclusive + """ + + if base < 2 or base > 36: + raise ValueError("'base' must be between 2 and 36 inclusive") + + digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" + result = "" + + if number < 0: + raise ValueError("number must be a positive integer") + + while number > 0: + number, remainder = divmod(number, base) + result = digits[remainder] + result + + if result == "": + result = "0" + + return result + + +def sum_of_digits(num: int, base: int) -> str: + """ + Calculate the sum of digit values in a positive integer + converted to the given 'base'. + Where 'base' ranges from 2 to 36. + + Examples: + >>> sum_of_digits(103, 12) + '13' + >>> sum_of_digits(1275, 4) + '30' + >>> sum_of_digits(6645, 2) + '1001' + >>> # bases below 2 and beyond 36 will error + >>> sum_of_digits(543, 1) + Traceback (most recent call last): + ... + ValueError: 'base' must be between 2 and 36 inclusive + >>> sum_of_digits(543, 37) + Traceback (most recent call last): + ... + ValueError: 'base' must be between 2 and 36 inclusive + """ + + if base < 2 or base > 36: + raise ValueError("'base' must be between 2 and 36 inclusive") + + num_str = int_to_base(num, base) + res = sum(int(char, base) for char in num_str) + res_str = int_to_base(res, base) + return res_str + + +def harshad_numbers_in_base(limit: int, base: int) -> list[str]: + """ + Finds all Harshad numbers smaller than num in base 'base'. + Where 'base' ranges from 2 to 36. + + Examples: + >>> harshad_numbers_in_base(15, 2) + ['1', '10', '100', '110', '1000', '1010', '1100'] + >>> harshad_numbers_in_base(12, 34) + ['1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B'] + >>> harshad_numbers_in_base(12, 4) + ['1', '2', '3', '10', '12', '20', '21'] + >>> # bases below 2 and beyond 36 will error + >>> harshad_numbers_in_base(234, 37) + Traceback (most recent call last): + ... + ValueError: 'base' must be between 2 and 36 inclusive + >>> harshad_numbers_in_base(234, 1) + Traceback (most recent call last): + ... + ValueError: 'base' must be between 2 and 36 inclusive + """ + + if base < 2 or base > 36: + raise ValueError("'base' must be between 2 and 36 inclusive") + + if limit < 0: + return [] + + numbers = [ + int_to_base(i, base) + for i in range(1, limit) + if i % int(sum_of_digits(i, base), base) == 0 + ] + + return numbers + + +def is_harshad_number_in_base(num: int, base: int) -> bool: + """ + Determines whether n in base 'base' is a harshad number. + Where 'base' ranges from 2 to 36. + + Examples: + >>> is_harshad_number_in_base(18, 10) + True + >>> is_harshad_number_in_base(21, 10) + True + >>> is_harshad_number_in_base(-21, 5) + False + >>> # bases below 2 and beyond 36 will error + >>> is_harshad_number_in_base(45, 37) + Traceback (most recent call last): + ... + ValueError: 'base' must be between 2 and 36 inclusive + >>> is_harshad_number_in_base(45, 1) + Traceback (most recent call last): + ... + ValueError: 'base' must be between 2 and 36 inclusive + """ + + if base < 2 or base > 36: + raise ValueError("'base' must be between 2 and 36 inclusive") + + if num < 0: + return False + + n = int_to_base(num, base) + d = sum_of_digits(num, base) + return int(n, base) % int(d, base) == 0 + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 153c35eac02b5f043824dfa72e071d2b3f756607 Mon Sep 17 00:00:00 2001 From: Adarsh Acharya <132294330+AdarshAcharya5@users.noreply.github.com> Date: Thu, 7 Sep 2023 00:46:51 +0530 Subject: [PATCH 188/757] Added Scaled Exponential Linear Unit Activation Function (#9027) * Added Scaled Exponential Linear Unit Activation Function * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update scaled_exponential_linear_unit.py * Update scaled_exponential_linear_unit.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update scaled_exponential_linear_unit.py * Update scaled_exponential_linear_unit.py * Update scaled_exponential_linear_unit.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update scaled_exponential_linear_unit.py * Update scaled_exponential_linear_unit.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .../scaled_exponential_linear_unit.py | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 neural_network/activation_functions/scaled_exponential_linear_unit.py diff --git a/neural_network/activation_functions/scaled_exponential_linear_unit.py b/neural_network/activation_functions/scaled_exponential_linear_unit.py new file mode 100644 index 000000000000..f91dc6852136 --- /dev/null +++ b/neural_network/activation_functions/scaled_exponential_linear_unit.py @@ -0,0 +1,44 @@ +""" +Implements the Scaled Exponential Linear Unit or SELU function. +The function takes a vector of K real numbers and two real numbers +alpha(default = 1.6732) & lambda (default = 1.0507) as input and +then applies the SELU function to each element of the vector. +SELU is a self-normalizing activation function. It is a variant +of the ELU. The main advantage of SELU is that we can be sure +that the output will always be standardized due to its +self-normalizing behavior. That means there is no need to +include Batch-Normalization layers. +References : +https://iq.opengenus.org/scaled-exponential-linear-unit/ +""" + +import numpy as np + + +def scaled_exponential_linear_unit( + vector: np.ndarray, alpha: float = 1.6732, lambda_: float = 1.0507 +) -> np.ndarray: + """ + Applies the Scaled Exponential Linear Unit function to each element of the vector. + Parameters : + vector : np.ndarray + alpha : float (default = 1.6732) + lambda_ : float (default = 1.0507) + + Returns : np.ndarray + Formula : f(x) = lambda_ * x if x > 0 + lambda_ * alpha * (e**x - 1) if x <= 0 + Examples : + >>> scaled_exponential_linear_unit(vector=np.array([1.3, 3.7, 2.4])) + array([1.36591, 3.88759, 2.52168]) + + >>> scaled_exponential_linear_unit(vector=np.array([1.3, 4.7, 8.2])) + array([1.36591, 4.93829, 8.61574]) + """ + return lambda_ * np.where(vector > 0, vector, alpha * (np.exp(vector) - 1)) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 0cae02451a214cd70b36f2bf0b7a043c25aea99d Mon Sep 17 00:00:00 2001 From: Rohan Saraogi <62804340+r0sa2@users.noreply.github.com> Date: Thu, 7 Sep 2023 03:52:36 -0400 Subject: [PATCH 189/757] Added nth_sgonal_num.py (#8753) * Added nth_sgonal_num.py * Update and rename nth_sgonal_num.py to polygonal_numbers.py --------- Co-authored-by: Tianyi Zheng --- maths/polygonal_numbers.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 maths/polygonal_numbers.py diff --git a/maths/polygonal_numbers.py b/maths/polygonal_numbers.py new file mode 100644 index 000000000000..7a7dc91acb26 --- /dev/null +++ b/maths/polygonal_numbers.py @@ -0,0 +1,32 @@ +def polygonal_num(num: int, sides: int) -> int: + """ + Returns the `num`th `sides`-gonal number. It is assumed that `num` >= 0 and + `sides` >= 3 (see for reference https://en.wikipedia.org/wiki/Polygonal_number). + + >>> polygonal_num(0, 3) + 0 + >>> polygonal_num(3, 3) + 6 + >>> polygonal_num(5, 4) + 25 + >>> polygonal_num(2, 5) + 5 + >>> polygonal_num(-1, 0) + Traceback (most recent call last): + ... + ValueError: Invalid input: num must be >= 0 and sides must be >= 3. + >>> polygonal_num(0, 2) + Traceback (most recent call last): + ... + ValueError: Invalid input: num must be >= 0 and sides must be >= 3. + """ + if num < 0 or sides < 3: + raise ValueError("Invalid input: num must be >= 0 and sides must be >= 3.") + + return ((sides - 2) * num**2 - (sides - 4) * num) // 2 + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From c9b4b8002f24a33ea49c16dff5ef9cbebbd64b1d Mon Sep 17 00:00:00 2001 From: Saksham Saha Date: Fri, 8 Sep 2023 17:50:28 +0530 Subject: [PATCH 190/757] Added an add at position subroutiune to linked list (#9020) * added addAtPosition to simple linked list * added addAtPosition to simple linked list * modified the add function to take an optional position command * fixed type safety errors: * fixed type safety errors: * fixed type safety errors: * fixed type safety errors: * fixed size error * fixed size error * added doctest and updates the else after checking if posiiton argument less than 0 or not * added doctest and updates the else after checking if posiiton argument less than 0 or not * fixed the contributing.md mistake * added doctest for out of bounds position value, both negative and positive --- data_structures/linked_list/__init__.py | 52 ++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/data_structures/linked_list/__init__.py b/data_structures/linked_list/__init__.py index 56b0e51baa93..225113f72cee 100644 --- a/data_structures/linked_list/__init__.py +++ b/data_structures/linked_list/__init__.py @@ -21,8 +21,56 @@ def __init__(self) -> None: self.head: Node | None = None self.size = 0 - def add(self, item: Any) -> None: - self.head = Node(item, self.head) + def add(self, item: Any, position: int = 0) -> None: + """ + Add an item to the LinkedList at the specified position. + Default position is 0 (the head). + + Args: + item (Any): The item to add to the LinkedList. + position (int, optional): The position at which to add the item. + Defaults to 0. + + Raises: + ValueError: If the position is negative or out of bounds. + + >>> linked_list = LinkedList() + >>> linked_list.add(1) + >>> linked_list.add(2) + >>> linked_list.add(3) + >>> linked_list.add(4, 2) + >>> print(linked_list) + 3 --> 2 --> 4 --> 1 + + # Test adding to a negative position + >>> linked_list.add(5, -3) + Traceback (most recent call last): + ... + ValueError: Position must be non-negative + + # Test adding to an out-of-bounds position + >>> linked_list.add(5,7) + Traceback (most recent call last): + ... + ValueError: Out of bounds + >>> linked_list.add(5, 4) + >>> print(linked_list) + 3 --> 2 --> 4 --> 1 --> 5 + """ + if position < 0: + raise ValueError("Position must be non-negative") + + if position == 0 or self.head is None: + new_node = Node(item, self.head) + self.head = new_node + else: + current = self.head + for _ in range(position - 1): + current = current.next + if current is None: + raise ValueError("Out of bounds") + new_node = Node(item, current.next) + current.next = new_node self.size += 1 def remove(self) -> Any: From 5a5ca06944148ad7232dd61dcf7c609c0c74c252 Mon Sep 17 00:00:00 2001 From: Saransh Chopra Date: Sat, 9 Sep 2023 23:28:43 +0530 Subject: [PATCH 191/757] Update `actions/checkout` with `fetch-depth: 0` (#9046) * Update `actions/checkout` with `fetch-depth: 0` * Update directory_writer.yml * Create junk.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update directory_writer.yml * Update directory_writer.yml --------- Co-authored-by: Christian Clauss Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .github/workflows/directory_writer.yml | 4 +++- arithmetic_analysis/junk.py | 0 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 arithmetic_analysis/junk.py diff --git a/.github/workflows/directory_writer.yml b/.github/workflows/directory_writer.yml index 331962cef11e..702c15f1e29b 100644 --- a/.github/workflows/directory_writer.yml +++ b/.github/workflows/directory_writer.yml @@ -6,7 +6,9 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v1 # v1, NOT v2 or v3 + - uses: actions/checkout@v4 + with: + fetch-depth: 0 - uses: actions/setup-python@v4 with: python-version: 3.x diff --git a/arithmetic_analysis/junk.py b/arithmetic_analysis/junk.py new file mode 100644 index 000000000000..e69de29bb2d1 From 97e2de0763d75b1875428d87818ef111481d5953 Mon Sep 17 00:00:00 2001 From: Kamil <32775019+quant12345@users.noreply.github.com> Date: Mon, 11 Sep 2023 15:11:22 +0500 Subject: [PATCH 192/757] Euler 070 partial replacement of numpy loops. (#9055) * Euler 070 partial replacement of numpy loops. * Update project_euler/problem_070/sol1.py * project_euler.yml: Upgrade actions/checkout@v4 and add numpy * Update project_euler.yml --------- Co-authored-by: Christian Clauss --- .github/workflows/project_euler.yml | 8 ++++---- project_euler/problem_070/sol1.py | 13 ++++++------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/.github/workflows/project_euler.yml b/.github/workflows/project_euler.yml index 460938219c14..7bbccf76e192 100644 --- a/.github/workflows/project_euler.yml +++ b/.github/workflows/project_euler.yml @@ -14,26 +14,26 @@ jobs: project-euler: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-python@v4 with: python-version: 3.x - name: Install pytest and pytest-cov run: | python -m pip install --upgrade pip - python -m pip install --upgrade pytest pytest-cov + python -m pip install --upgrade numpy pytest pytest-cov - run: pytest --doctest-modules --cov-report=term-missing:skip-covered --cov=project_euler/ project_euler/ validate-solutions: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-python@v4 with: python-version: 3.x - name: Install pytest and requests run: | python -m pip install --upgrade pip - python -m pip install --upgrade pytest requests + python -m pip install --upgrade numpy pytest requests - run: pytest scripts/validate_solutions.py env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/project_euler/problem_070/sol1.py b/project_euler/problem_070/sol1.py index 57a6c1916374..f1114a280a31 100644 --- a/project_euler/problem_070/sol1.py +++ b/project_euler/problem_070/sol1.py @@ -30,6 +30,8 @@ """ from __future__ import annotations +import numpy as np + def get_totients(max_one: int) -> list[int]: """ @@ -42,17 +44,14 @@ def get_totients(max_one: int) -> list[int]: >>> get_totients(10) [0, 1, 1, 2, 2, 4, 2, 6, 4, 6] """ - totients = [0] * max_one - - for i in range(max_one): - totients[i] = i + totients = np.arange(max_one) for i in range(2, max_one): if totients[i] == i: - for j in range(i, max_one, i): - totients[j] -= totients[j] // i + x = np.arange(i, max_one, i) # array of indexes to select + totients[x] -= totients[x] // i - return totients + return totients.tolist() def has_same_digits(num1: int, num2: int) -> bool: From 4246da387f8b48da5147320344d336886787aea1 Mon Sep 17 00:00:00 2001 From: Kamil <32775019+quant12345@users.noreply.github.com> Date: Mon, 11 Sep 2023 16:05:32 +0500 Subject: [PATCH 193/757] jacobi_iteration_method.py the use of vector operations, which reduces the calculation time by dozens of times (#8938) * Replaced loops in jacobi_iteration_method function with vector operations. That gives a reduction in the time for calculating the algorithm. * Replaced loops in jacobi_iteration_method function with vector operations. That gives a reduction in the time for calculating the algorithm. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Delete main.py * Update jacobi_iteration_method.py Changed a line that was too long. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update jacobi_iteration_method.py Changed the type of the returned list as required. * Update jacobi_iteration_method.py Replaced init_val with new_val. * Update jacobi_iteration_method.py Fixed bug: init_val: list[int] to list[float]. Since the numbers are fractional: init_val = [0.5, -0.5, -0.5]. * Update jacobi_iteration_method.py Changed comments, made variable names more understandable. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update jacobi_iteration_method.py left the old algorithm commented out, as it clearly shows what is being done. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update jacobi_iteration_method.py Edits upon request. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .../jacobi_iteration_method.py | 34 +++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/arithmetic_analysis/jacobi_iteration_method.py b/arithmetic_analysis/jacobi_iteration_method.py index dba8a9ff44d3..44c52dd44640 100644 --- a/arithmetic_analysis/jacobi_iteration_method.py +++ b/arithmetic_analysis/jacobi_iteration_method.py @@ -12,7 +12,7 @@ def jacobi_iteration_method( coefficient_matrix: NDArray[float64], constant_matrix: NDArray[float64], - init_val: list[int], + init_val: list[float], iterations: int, ) -> list[float]: """ @@ -115,6 +115,7 @@ def jacobi_iteration_method( strictly_diagonally_dominant(table) + """ # Iterates the whole matrix for given number of times for _ in range(iterations): new_val = [] @@ -130,8 +131,37 @@ def jacobi_iteration_method( temp = (temp + val) / denom new_val.append(temp) init_val = new_val + """ + + # denominator - a list of values along the diagonal + denominator = np.diag(coefficient_matrix) + + # val_last - values of the last column of the table array + val_last = table[:, -1] + + # masks - boolean mask of all strings without diagonal + # elements array coefficient_matrix + masks = ~np.eye(coefficient_matrix.shape[0], dtype=bool) + + # no_diagonals - coefficient_matrix array values without diagonal elements + no_diagonals = coefficient_matrix[masks].reshape(-1, rows - 1) + + # Here we get 'i_col' - these are the column numbers, for each row + # without diagonal elements, except for the last column. + i_row, i_col = np.where(masks) + ind = i_col.reshape(-1, rows - 1) + + #'i_col' is converted to a two-dimensional list 'ind', which will be + # used to make selections from 'init_val' ('arr' array see below). + + # Iterates the whole matrix for given number of times + for _ in range(iterations): + arr = np.take(init_val, ind) + sum_product_rows = np.sum((-1) * no_diagonals * arr, axis=1) + new_val = (sum_product_rows + val_last) / denominator + init_val = new_val - return [float(i) for i in new_val] + return new_val.tolist() # Checks if the given matrix is strictly diagonally dominant From 1488cdea708485eb1d81c73126eab13cb9b04a47 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 12 Sep 2023 01:56:50 +0200 Subject: [PATCH 194/757] [pre-commit.ci] pre-commit autoupdate (#9056) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/astral-sh/ruff-pre-commit: v0.0.287 → v0.0.288](https://github.com/astral-sh/ruff-pre-commit/compare/v0.0.287...v0.0.288) - [github.com/psf/black: 23.7.0 → 23.9.1](https://github.com/psf/black/compare/23.7.0...23.9.1) * updating DIRECTORY.md --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- .pre-commit-config.yaml | 4 ++-- DIRECTORY.md | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c046789463cc..722b408ee9e9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,12 +16,12 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.0.287 + rev: v0.0.288 hooks: - id: ruff - repo: https://github.com/psf/black - rev: 23.7.0 + rev: 23.9.1 hooks: - id: black diff --git a/DIRECTORY.md b/DIRECTORY.md index 43da91cb818e..1b802564f939 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -5,6 +5,7 @@ * [In Static Equilibrium](arithmetic_analysis/in_static_equilibrium.py) * [Intersection](arithmetic_analysis/intersection.py) * [Jacobi Iteration Method](arithmetic_analysis/jacobi_iteration_method.py) + * [Junk](arithmetic_analysis/junk.py) * [Lu Decomposition](arithmetic_analysis/lu_decomposition.py) * [Newton Forward Interpolation](arithmetic_analysis/newton_forward_interpolation.py) * [Newton Method](arithmetic_analysis/newton_method.py) @@ -133,6 +134,7 @@ ## Computer Vision * [Cnn Classification](computer_vision/cnn_classification.py) * [Flip Augmentation](computer_vision/flip_augmentation.py) + * [Haralick Descriptors](computer_vision/haralick_descriptors.py) * [Harris Corner](computer_vision/harris_corner.py) * [Horn Schunck](computer_vision/horn_schunck.py) * [Mean Threshold](computer_vision/mean_threshold.py) @@ -586,6 +588,7 @@ * [Greedy Coin Change](maths/greedy_coin_change.py) * [Hamming Numbers](maths/hamming_numbers.py) * [Hardy Ramanujanalgo](maths/hardy_ramanujanalgo.py) + * [Harshad Numbers](maths/harshad_numbers.py) * [Hexagonal Number](maths/hexagonal_number.py) * [Integration By Simpson Approx](maths/integration_by_simpson_approx.py) * [Interquartile Range](maths/interquartile_range.py) @@ -626,6 +629,7 @@ * [Pi Monte Carlo Estimation](maths/pi_monte_carlo_estimation.py) * [Points Are Collinear 3D](maths/points_are_collinear_3d.py) * [Pollard Rho](maths/pollard_rho.py) + * [Polygonal Numbers](maths/polygonal_numbers.py) * [Polynomial Evaluation](maths/polynomial_evaluation.py) * Polynomials * [Single Indeterminate Operations](maths/polynomials/single_indeterminate_operations.py) @@ -712,6 +716,7 @@ * Activation Functions * [Exponential Linear Unit](neural_network/activation_functions/exponential_linear_unit.py) * [Leaky Rectified Linear Unit](neural_network/activation_functions/leaky_rectified_linear_unit.py) + * [Scaled Exponential Linear Unit](neural_network/activation_functions/scaled_exponential_linear_unit.py) * [Back Propagation Neural Network](neural_network/back_propagation_neural_network.py) * [Convolution Neural Network](neural_network/convolution_neural_network.py) * [Perceptron](neural_network/perceptron.py) From fbad85d3ecbbb826a5891807c823149d38bbaed3 Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Sat, 16 Sep 2023 18:12:31 -0400 Subject: [PATCH 195/757] Delete empty junk file (#9062) * updating DIRECTORY.md * updating DIRECTORY.md * Delete empty junk file * updating DIRECTORY.md * Fix ruff errors * Fix more ruff errors --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 1 - arithmetic_analysis/junk.py | 0 computer_vision/haralick_descriptors.py | 8 +++++--- conversions/convert_number_to_words.py | 6 +++--- graphs/tarjans_scc.py | 2 +- 5 files changed, 9 insertions(+), 8 deletions(-) delete mode 100644 arithmetic_analysis/junk.py diff --git a/DIRECTORY.md b/DIRECTORY.md index 1b802564f939..d81e4ec1ee83 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -5,7 +5,6 @@ * [In Static Equilibrium](arithmetic_analysis/in_static_equilibrium.py) * [Intersection](arithmetic_analysis/intersection.py) * [Jacobi Iteration Method](arithmetic_analysis/jacobi_iteration_method.py) - * [Junk](arithmetic_analysis/junk.py) * [Lu Decomposition](arithmetic_analysis/lu_decomposition.py) * [Newton Forward Interpolation](arithmetic_analysis/newton_forward_interpolation.py) * [Newton Method](arithmetic_analysis/newton_method.py) diff --git a/arithmetic_analysis/junk.py b/arithmetic_analysis/junk.py deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/computer_vision/haralick_descriptors.py b/computer_vision/haralick_descriptors.py index 1a86d84ea14b..413cea304f6c 100644 --- a/computer_vision/haralick_descriptors.py +++ b/computer_vision/haralick_descriptors.py @@ -100,7 +100,9 @@ def binarize(image: np.ndarray, threshold: float = 127.0) -> np.ndarray: return np.where(image > threshold, 1, 0) -def transform(image: np.ndarray, kind: str, kernel: np.ndarray = None) -> np.ndarray: +def transform( + image: np.ndarray, kind: str, kernel: np.ndarray | None = None +) -> np.ndarray: """ Simple image transformation using one of two available filter functions: Erosion and Dilation. @@ -154,7 +156,7 @@ def transform(image: np.ndarray, kind: str, kernel: np.ndarray = None) -> np.nda return transformed -def opening_filter(image: np.ndarray, kernel: np.ndarray = None) -> np.ndarray: +def opening_filter(image: np.ndarray, kernel: np.ndarray | None = None) -> np.ndarray: """ Opening filter, defined as the sequence of erosion and then a dilation filter on the same image. @@ -172,7 +174,7 @@ def opening_filter(image: np.ndarray, kernel: np.ndarray = None) -> np.ndarray: return transform(transform(image, "dilation", kernel), "erosion", kernel) -def closing_filter(image: np.ndarray, kernel: np.ndarray = None) -> np.ndarray: +def closing_filter(image: np.ndarray, kernel: np.ndarray | None = None) -> np.ndarray: """ Opening filter, defined as the sequence of dilation and then erosion filter on the same image. diff --git a/conversions/convert_number_to_words.py b/conversions/convert_number_to_words.py index 0e4405319f1f..0c428928b31d 100644 --- a/conversions/convert_number_to_words.py +++ b/conversions/convert_number_to_words.py @@ -54,7 +54,7 @@ def max_value(cls, system: str) -> int: class NumberWords(Enum): - ONES: ClassVar = { + ONES: ClassVar[dict[int, str]] = { 0: "", 1: "one", 2: "two", @@ -67,7 +67,7 @@ class NumberWords(Enum): 9: "nine", } - TEENS: ClassVar = { + TEENS: ClassVar[dict[int, str]] = { 0: "ten", 1: "eleven", 2: "twelve", @@ -80,7 +80,7 @@ class NumberWords(Enum): 9: "nineteen", } - TENS: ClassVar = { + TENS: ClassVar[dict[int, str]] = { 2: "twenty", 3: "thirty", 4: "forty", diff --git a/graphs/tarjans_scc.py b/graphs/tarjans_scc.py index 30f8ca8a204f..dfd2e52704d5 100644 --- a/graphs/tarjans_scc.py +++ b/graphs/tarjans_scc.py @@ -77,7 +77,7 @@ def create_graph(n, edges): n_vertices = 7 source = [0, 0, 1, 2, 3, 3, 4, 4, 6] target = [1, 3, 2, 0, 1, 4, 5, 6, 5] - edges = [(u, v) for u, v in zip(source, target)] + edges = list(zip(source, target)) g = create_graph(n_vertices, edges) assert [[5], [6], [4], [3, 2, 1, 0]] == tarjan(g) From dc50add8a78ebf34bc7bb050c1a0e61d207b9544 Mon Sep 17 00:00:00 2001 From: Rohan Anand <96521078+rohan472000@users.noreply.github.com> Date: Sat, 23 Sep 2023 14:21:36 +0530 Subject: [PATCH 196/757] Update xgboost_regressor.py (#9078) * Update xgboost_regressor.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- machine_learning/xgboost_regressor.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/machine_learning/xgboost_regressor.py b/machine_learning/xgboost_regressor.py index 023984fc1f59..a540e3ab03eb 100644 --- a/machine_learning/xgboost_regressor.py +++ b/machine_learning/xgboost_regressor.py @@ -27,7 +27,9 @@ def xgboost( ... 1.14300000e+03, 2.60958904e+00, 3.67800000e+01, -1.19780000e+02]])) array([[1.1139996]], dtype=float32) """ - xgb = XGBRegressor(verbosity=0, random_state=42) + xgb = XGBRegressor( + verbosity=0, random_state=42, tree_method="exact", base_score=0.5 + ) xgb.fit(features, target) # Predict target for test data predictions = xgb.predict(test_features) From b203150ac481743a6d8c1ef01091712a54dfbf6c Mon Sep 17 00:00:00 2001 From: omahs <73983677+omahs@users.noreply.github.com> Date: Sat, 23 Sep 2023 10:53:09 +0200 Subject: [PATCH 197/757] Fix typos (#9076) * fix typo * fix typo * fix typos * fix typo --- cellular_automata/langtons_ant.py | 2 +- compression/README.md | 4 ++-- hashes/README.md | 4 ++-- sorts/README.md | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cellular_automata/langtons_ant.py b/cellular_automata/langtons_ant.py index 983c626546ad..9847c50a5c3e 100644 --- a/cellular_automata/langtons_ant.py +++ b/cellular_automata/langtons_ant.py @@ -30,7 +30,7 @@ def __init__(self, width: int, height: int) -> None: self.board = [[True] * width for _ in range(height)] self.ant_position: tuple[int, int] = (width // 2, height // 2) - # Initially pointing left (similar to the the wikipedia image) + # Initially pointing left (similar to the wikipedia image) # (0 = 0° | 1 = 90° | 2 = 180 ° | 3 = 270°) self.ant_direction: int = 3 diff --git a/compression/README.md b/compression/README.md index cf54ea986175..bad7ae1a2f76 100644 --- a/compression/README.md +++ b/compression/README.md @@ -1,9 +1,9 @@ # Compression Data compression is everywhere, you need it to store data without taking too much space. -Either the compression lose some data (then we talk about lossy compression, such as .jpg) or it does not (and then it is lossless compression, such as .png) +Either the compression loses some data (then we talk about lossy compression, such as .jpg) or it does not (and then it is lossless compression, such as .png) -Lossless compression is mainly used for archive purpose as it allow storing data without losing information about the file archived. On the other hand, lossy compression is used for transfer of file where quality isn't necessarily what is required (i.e: images on Twitter). +Lossless compression is mainly used for archive purpose as it allows storing data without losing information about the file archived. On the other hand, lossy compression is used for transfer of file where quality isn't necessarily what is required (i.e: images on Twitter). * * diff --git a/hashes/README.md b/hashes/README.md index 6df9a2fb6360..0237260eaa67 100644 --- a/hashes/README.md +++ b/hashes/README.md @@ -7,11 +7,11 @@ Unlike encryption, which is intended to protect data in transit, hashing is inte This is one of the first algorithms that has gained widespread acceptance. MD5 is hashing algorithm made by Ray Rivest that is known to suffer vulnerabilities. It was created in 1992 as the successor to MD4. Currently MD6 is in the works, but as of 2009 Rivest had removed it from NIST consideration for SHA-3. ### SHA -SHA stands for Security Hashing Algorithm and it’s probably best known as the hashing algorithm used in most SSL/TLS cipher suites. A cipher suite is a collection of ciphers and algorithms that are used for SSL/TLS connections. SHA handles the hashing aspects. SHA-1, as we mentioned earlier, is now deprecated. SHA-2 is now mandatory. SHA-2 is sometimes known has SHA-256, though variants with longer bit lengths are also available. +SHA stands for Security Hashing Algorithm and it’s probably best known as the hashing algorithm used in most SSL/TLS cipher suites. A cipher suite is a collection of ciphers and algorithms that are used for SSL/TLS connections. SHA handles the hashing aspects. SHA-1, as we mentioned earlier, is now deprecated. SHA-2 is now mandatory. SHA-2 is sometimes known as SHA-256, though variants with longer bit lengths are also available. ### SHA256 SHA 256 is a member of the SHA 2 algorithm family, under which SHA stands for Secure Hash Algorithm. It was a collaborative effort between both the NSA and NIST to implement a successor to the SHA 1 family, which was beginning to lose potency against brute force attacks. It was published in 2001. The importance of the 256 in the name refers to the final hash digest value, i.e. the hash value will remain 256 bits regardless of the size of the plaintext/cleartext. Other algorithms in the SHA family are similar to SHA 256 in some ways. ### Luhn -The Luhn algorithm, also renowned as the modulus 10 or mod 10 algorithm, is a straightforward checksum formula used to validate a wide range of identification numbers, including credit card numbers, IMEI numbers, and Canadian Social Insurance Numbers. A community of mathematicians developed the LUHN formula in the late 1960s. Companies offering credit cards quickly followed suit. Since the algorithm is in the public interest, anyone can use it. The algorithm is used by most credit cards and many government identification numbers as a simple method of differentiating valid figures from mistyped or otherwise incorrect numbers. It was created to guard against unintentional errors, not malicious attacks. \ No newline at end of file +The Luhn algorithm, also renowned as the modulus 10 or mod 10 algorithm, is a straightforward checksum formula used to validate a wide range of identification numbers, including credit card numbers, IMEI numbers, and Canadian Social Insurance Numbers. A community of mathematicians developed the LUHN formula in the late 1960s. Companies offering credit cards quickly followed suit. Since the algorithm is in the public interest, anyone can use it. The algorithm is used by most credit cards and many government identification numbers as a simple method of differentiating valid figures from mistyped or otherwise incorrect numbers. It was created to guard against unintentional errors, not malicious attacks. diff --git a/sorts/README.md b/sorts/README.md index ceb0207c2be4..f24427d582e7 100644 --- a/sorts/README.md +++ b/sorts/README.md @@ -4,7 +4,7 @@ is specified by the sorting algorithm. The most typical orders are lexical or nu of sorting lies in the fact that, if data is stored in a sorted manner, data searching can be highly optimised. Another use for sorting is to represent data in a more readable manner. -This section contains a lot of important algorithms that helps us to use sorting algorithms in various scenarios. +This section contains a lot of important algorithms that help us to use sorting algorithms in various scenarios. ## References * * From 53a51b3529ad5f985e6f65b5b3a4e155af1d2d63 Mon Sep 17 00:00:00 2001 From: Chris O <46587501+ChrisO345@users.noreply.github.com> Date: Sun, 24 Sep 2023 19:09:32 +1300 Subject: [PATCH 198/757] Rewrite of base32.py algorithm (#9068) * rewrite of base32.py * changed maps to list comprehension * Apply suggestions from code review Co-authored-by: Tianyi Zheng --------- Co-authored-by: Tianyi Zheng --- ciphers/base32.py | 51 +++++++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/ciphers/base32.py b/ciphers/base32.py index fee53ccaf0c4..1924d1e185d7 100644 --- a/ciphers/base32.py +++ b/ciphers/base32.py @@ -1,42 +1,45 @@ -import base64 +""" +Base32 encoding and decoding +https://en.wikipedia.org/wiki/Base32 +""" +B32_CHARSET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567" -def base32_encode(string: str) -> bytes: + +def base32_encode(data: bytes) -> bytes: """ - Encodes a given string to base32, returning a bytes-like object - >>> base32_encode("Hello World!") + >>> base32_encode(b"Hello World!") b'JBSWY3DPEBLW64TMMQQQ====' - >>> base32_encode("123456") + >>> base32_encode(b"123456") b'GEZDGNBVGY======' - >>> base32_encode("some long complex string") + >>> base32_encode(b"some long complex string") b'ONXW2ZJANRXW4ZZAMNXW24DMMV4CA43UOJUW4ZY=' """ - - # encoded the input (we need a bytes like object) - # then, b32encoded the bytes-like object - return base64.b32encode(string.encode("utf-8")) + binary_data = "".join(bin(ord(d))[2:].zfill(8) for d in data.decode("utf-8")) + binary_data = binary_data.ljust(5 * ((len(binary_data) // 5) + 1), "0") + b32_chunks = map("".join, zip(*[iter(binary_data)] * 5)) + b32_result = "".join(B32_CHARSET[int(chunk, 2)] for chunk in b32_chunks) + return bytes(b32_result.ljust(8 * ((len(b32_result) // 8) + 1), "="), "utf-8") -def base32_decode(encoded_bytes: bytes) -> str: +def base32_decode(data: bytes) -> bytes: """ - Decodes a given bytes-like object to a string, returning a string >>> base32_decode(b'JBSWY3DPEBLW64TMMQQQ====') - 'Hello World!' + b'Hello World!' >>> base32_decode(b'GEZDGNBVGY======') - '123456' + b'123456' >>> base32_decode(b'ONXW2ZJANRXW4ZZAMNXW24DMMV4CA43UOJUW4ZY=') - 'some long complex string' + b'some long complex string' """ - - # decode the bytes from base32 - # then, decode the bytes-like object to return as a string - return base64.b32decode(encoded_bytes).decode("utf-8") + binary_chunks = "".join( + bin(B32_CHARSET.index(_d))[2:].zfill(5) + for _d in data.decode("utf-8").strip("=") + ) + binary_data = list(map("".join, zip(*[iter(binary_chunks)] * 8))) + return bytes("".join([chr(int(_d, 2)) for _d in binary_data]), "utf-8") if __name__ == "__main__": - test = "Hello World!" - encoded = base32_encode(test) - print(encoded) + import doctest - decoded = base32_decode(encoded) - print(decoded) + doctest.testmod() From 708d9061413a5c049d63b97b08540aa4867d5523 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 24 Sep 2023 12:04:47 +0530 Subject: [PATCH 199/757] [pre-commit.ci] pre-commit autoupdate (#9067) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/astral-sh/ruff-pre-commit: v0.0.288 → v0.0.290](https://github.com/astral-sh/ruff-pre-commit/compare/v0.0.288...v0.0.290) * Update .pre-commit-config.yaml --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 722b408ee9e9..809b841d0ea3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.0.288 + rev: v0.0.291 hooks: - id: ruff From 882fb2f3c972e67303dd65873f05b8f3d58724e1 Mon Sep 17 00:00:00 2001 From: Chris O <46587501+ChrisO345@users.noreply.github.com> Date: Sun, 24 Sep 2023 20:36:06 +1300 Subject: [PATCH 200/757] Rewrite of base85.py algorithm (#9069) * rewrite of base85.py * changed maps to list comprehension * Apply suggestions from code review Co-authored-by: Tianyi Zheng --------- Co-authored-by: Tianyi Zheng --- ciphers/base85.py | 57 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 41 insertions(+), 16 deletions(-) diff --git a/ciphers/base85.py b/ciphers/base85.py index afd1aff79d11..f0228e5052dd 100644 --- a/ciphers/base85.py +++ b/ciphers/base85.py @@ -1,30 +1,55 @@ -import base64 +""" +Base85 (Ascii85) encoding and decoding +https://en.wikipedia.org/wiki/Ascii85 +""" -def base85_encode(string: str) -> bytes: + +def _base10_to_85(d: int) -> str: + return "".join(chr(d % 85 + 33)) + _base10_to_85(d // 85) if d > 0 else "" + + +def _base85_to_10(digits: list) -> int: + return sum(char * 85**i for i, char in enumerate(reversed(digits))) + + +def ascii85_encode(data: bytes) -> bytes: """ - >>> base85_encode("") + >>> ascii85_encode(b"") b'' - >>> base85_encode("12345") + >>> ascii85_encode(b"12345") b'0etOA2#' - >>> base85_encode("base 85") + >>> ascii85_encode(b"base 85") b'@UX=h+?24' """ - # encoded the input to a bytes-like object and then a85encode that - return base64.a85encode(string.encode("utf-8")) + binary_data = "".join(bin(ord(d))[2:].zfill(8) for d in data.decode("utf-8")) + null_values = (32 * ((len(binary_data) // 32) + 1) - len(binary_data)) // 8 + binary_data = binary_data.ljust(32 * ((len(binary_data) // 32) + 1), "0") + b85_chunks = [int(_s, 2) for _s in map("".join, zip(*[iter(binary_data)] * 32))] + result = "".join(_base10_to_85(chunk)[::-1] for chunk in b85_chunks) + return bytes(result[:-null_values] if null_values % 4 != 0 else result, "utf-8") -def base85_decode(a85encoded: bytes) -> str: +def ascii85_decode(data: bytes) -> bytes: """ - >>> base85_decode(b"") - '' - >>> base85_decode(b"0etOA2#") - '12345' - >>> base85_decode(b"@UX=h+?24") - 'base 85' + >>> ascii85_decode(b"") + b'' + >>> ascii85_decode(b"0etOA2#") + b'12345' + >>> ascii85_decode(b"@UX=h+?24") + b'base 85' """ - # a85decode the input into bytes and decode that into a human readable string - return base64.a85decode(a85encoded).decode("utf-8") + null_values = 5 * ((len(data) // 5) + 1) - len(data) + binary_data = data.decode("utf-8") + "u" * null_values + b85_chunks = map("".join, zip(*[iter(binary_data)] * 5)) + b85_segments = [[ord(_s) - 33 for _s in chunk] for chunk in b85_chunks] + results = [bin(_base85_to_10(chunk))[2::].zfill(32) for chunk in b85_segments] + char_chunks = [ + [chr(int(_s, 2)) for _s in map("".join, zip(*[iter(r)] * 8))] for r in results + ] + result = "".join("".join(char) for char in char_chunks) + offset = int(null_values % 5 == 0) + return bytes(result[: offset - null_values], "utf-8") if __name__ == "__main__": From 211247ef82fd54540e4cb832fbbb612ca5845700 Mon Sep 17 00:00:00 2001 From: Amir Lavasani Date: Mon, 25 Sep 2023 00:38:51 +0330 Subject: [PATCH 201/757] Add MFCC Feature Extraction Algorithm (#9057) * Add MFCC feature extraction to machine learning * Add standalone usage in comments * Apply suggestions from code review Co-authored-by: Christian Clauss * Delete empty junk file (#9062) * updating DIRECTORY.md * updating DIRECTORY.md * Delete empty junk file * updating DIRECTORY.md * Fix ruff errors * Fix more ruff errors --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> * [main] Fix typo due to auto review change * Add doctests for all functions * Add MFCC feature extraction to machine learning * Add standalone usage in comments * Apply suggestions from code review Co-authored-by: Christian Clauss * [main] Fix typo due to auto review change * Add doctests for all functions * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix some pre-commit issues * Update review issues * Remove types from docstring * Rename dct * Add mfcc docstring * Add typing to several functions * Apply suggestions from code review * Update mfcc.py * get_filter_points() -> tuple[np.ndarray, np.ndarray]: * algorithm --------- Co-authored-by: Christian Clauss Co-authored-by: Tianyi Zheng Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- machine_learning/mfcc.py | 479 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 479 insertions(+) create mode 100644 machine_learning/mfcc.py diff --git a/machine_learning/mfcc.py b/machine_learning/mfcc.py new file mode 100644 index 000000000000..7ce8ceb50ff2 --- /dev/null +++ b/machine_learning/mfcc.py @@ -0,0 +1,479 @@ +""" +Mel Frequency Cepstral Coefficients (MFCC) Calculation + +MFCC is an algorithm widely used in audio and speech processing to represent the +short-term power spectrum of a sound signal in a more compact and +discriminative way. It is particularly popular in speech and audio processing +tasks such as speech recognition and speaker identification. + +How Mel Frequency Cepstral Coefficients are Calculated: +1. Preprocessing: + - Load an audio signal and normalize it to ensure that the values fall + within a specific range (e.g., between -1 and 1). + - Frame the audio signal into overlapping, fixed-length segments, typically + using a technique like windowing to reduce spectral leakage. + +2. Fourier Transform: + - Apply a Fast Fourier Transform (FFT) to each audio frame to convert it + from the time domain to the frequency domain. This results in a + representation of the audio frame as a sequence of frequency components. + +3. Power Spectrum: + - Calculate the power spectrum by taking the squared magnitude of each + frequency component obtained from the FFT. This step measures the energy + distribution across different frequency bands. + +4. Mel Filterbank: + - Apply a set of triangular filterbanks spaced in the Mel frequency scale + to the power spectrum. These filters mimic the human auditory system's + frequency response. Each filterbank sums the power spectrum values within + its band. + +5. Logarithmic Compression: + - Take the logarithm (typically base 10) of the filterbank values to + compress the dynamic range. This step mimics the logarithmic response of + the human ear to sound intensity. + +6. Discrete Cosine Transform (DCT): + - Apply the Discrete Cosine Transform to the log filterbank energies to + obtain the MFCC coefficients. This transformation helps decorrelate the + filterbank energies and captures the most important features of the audio + signal. + +7. Feature Extraction: + - Select a subset of the DCT coefficients to form the feature vector. + Often, the first few coefficients (e.g., 12-13) are used for most + applications. + +References: +- Mel-Frequency Cepstral Coefficients (MFCCs): + https://en.wikipedia.org/wiki/Mel-frequency_cepstrum +- Speech and Language Processing by Daniel Jurafsky & James H. Martin: + https://web.stanford.edu/~jurafsky/slp3/ +- Mel Frequency Cepstral Coefficient (MFCC) tutorial + http://practicalcryptography.com/miscellaneous/machine-learning + /guide-mel-frequency-cepstral-coefficients-mfccs/ + +Author: Amir Lavasani +""" + + +import logging + +import numpy as np +import scipy.fftpack as fft +from scipy.signal import get_window + +logging.basicConfig(filename=f"{__file__}.log", level=logging.INFO) + + +def mfcc( + audio: np.ndarray, + sample_rate: int, + ftt_size: int = 1024, + hop_length: int = 20, + mel_filter_num: int = 10, + dct_filter_num: int = 40, +) -> np.ndarray: + """ + Calculate Mel Frequency Cepstral Coefficients (MFCCs) from an audio signal. + + Args: + audio: The input audio signal. + sample_rate: The sample rate of the audio signal (in Hz). + ftt_size: The size of the FFT window (default is 1024). + hop_length: The hop length for frame creation (default is 20ms). + mel_filter_num: The number of Mel filters (default is 10). + dct_filter_num: The number of DCT filters (default is 40). + + Returns: + A matrix of MFCCs for the input audio. + + Raises: + ValueError: If the input audio is empty. + + Example: + >>> sample_rate = 44100 # Sample rate of 44.1 kHz + >>> duration = 2.0 # Duration of 1 second + >>> t = np.linspace(0, duration, int(sample_rate * duration), endpoint=False) + >>> audio = 0.5 * np.sin(2 * np.pi * 440.0 * t) # Generate a 440 Hz sine wave + >>> mfccs = mfcc(audio, sample_rate) + >>> mfccs.shape + (40, 101) + """ + logging.info(f"Sample rate: {sample_rate}Hz") + logging.info(f"Audio duration: {len(audio) / sample_rate}s") + logging.info(f"Audio min: {np.min(audio)}") + logging.info(f"Audio max: {np.max(audio)}") + + # normalize audio + audio_normalized = normalize(audio) + + logging.info(f"Normalized audio min: {np.min(audio_normalized)}") + logging.info(f"Normalized audio max: {np.max(audio_normalized)}") + + # frame audio into + audio_framed = audio_frames( + audio_normalized, sample_rate, ftt_size=ftt_size, hop_length=hop_length + ) + + logging.info(f"Framed audio shape: {audio_framed.shape}") + logging.info(f"First frame: {audio_framed[0]}") + + # convert to frequency domain + # For simplicity we will choose the Hanning window. + window = get_window("hann", ftt_size, fftbins=True) + audio_windowed = audio_framed * window + + logging.info(f"Windowed audio shape: {audio_windowed.shape}") + logging.info(f"First frame: {audio_windowed[0]}") + + audio_fft = calculate_fft(audio_windowed, ftt_size) + logging.info(f"fft audio shape: {audio_fft.shape}") + logging.info(f"First frame: {audio_fft[0]}") + + audio_power = calculate_signal_power(audio_fft) + logging.info(f"power audio shape: {audio_power.shape}") + logging.info(f"First frame: {audio_power[0]}") + + filters = mel_spaced_filterbank(sample_rate, mel_filter_num, ftt_size) + logging.info(f"filters shape: {filters.shape}") + + audio_filtered = np.dot(filters, np.transpose(audio_power)) + audio_log = 10.0 * np.log10(audio_filtered) + logging.info(f"audio_log shape: {audio_log.shape}") + + dct_filters = discrete_cosine_transform(dct_filter_num, mel_filter_num) + cepstral_coefficents = np.dot(dct_filters, audio_log) + + logging.info(f"cepstral_coefficents shape: {cepstral_coefficents.shape}") + return cepstral_coefficents + + +def normalize(audio: np.ndarray) -> np.ndarray: + """ + Normalize an audio signal by scaling it to have values between -1 and 1. + + Args: + audio: The input audio signal. + + Returns: + The normalized audio signal. + + Examples: + >>> audio = np.array([1, 2, 3, 4, 5]) + >>> normalized_audio = normalize(audio) + >>> np.max(normalized_audio) + 1.0 + >>> np.min(normalized_audio) + 0.2 + """ + # Divide the entire audio signal by the maximum absolute value + return audio / np.max(np.abs(audio)) + + +def audio_frames( + audio: np.ndarray, + sample_rate: int, + hop_length: int = 20, + ftt_size: int = 1024, +) -> np.ndarray: + """ + Split an audio signal into overlapping frames. + + Args: + audio: The input audio signal. + sample_rate: The sample rate of the audio signal. + hop_length: The length of the hopping (default is 20ms). + ftt_size: The size of the FFT window (default is 1024). + + Returns: + An array of overlapping frames. + + Examples: + >>> audio = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]*1000) + >>> sample_rate = 8000 + >>> frames = audio_frames(audio, sample_rate, hop_length=10, ftt_size=512) + >>> frames.shape + (126, 512) + """ + + hop_size = np.round(sample_rate * hop_length / 1000).astype(int) + + # Pad the audio signal to handle edge cases + audio = np.pad(audio, int(ftt_size / 2), mode="reflect") + + # Calculate the number of frames + frame_count = int((len(audio) - ftt_size) / hop_size) + 1 + + # Initialize an array to store the frames + frames = np.zeros((frame_count, ftt_size)) + + # Split the audio signal into frames + for n in range(frame_count): + frames[n] = audio[n * hop_size : n * hop_size + ftt_size] + + return frames + + +def calculate_fft(audio_windowed: np.ndarray, ftt_size: int = 1024) -> np.ndarray: + """ + Calculate the Fast Fourier Transform (FFT) of windowed audio data. + + Args: + audio_windowed: The windowed audio signal. + ftt_size: The size of the FFT (default is 1024). + + Returns: + The FFT of the audio data. + + Examples: + >>> audio_windowed = np.array([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]) + >>> audio_fft = calculate_fft(audio_windowed, ftt_size=4) + >>> np.allclose(audio_fft[0], np.array([6.0+0.j, -1.5+0.8660254j, -1.5-0.8660254j])) + True + """ + # Transpose the audio data to have time in rows and channels in columns + audio_transposed = np.transpose(audio_windowed) + + # Initialize an array to store the FFT results + audio_fft = np.empty( + (int(1 + ftt_size // 2), audio_transposed.shape[1]), + dtype=np.complex64, + order="F", + ) + + # Compute FFT for each channel + for n in range(audio_fft.shape[1]): + audio_fft[:, n] = fft.fft(audio_transposed[:, n], axis=0)[: audio_fft.shape[0]] + + # Transpose the FFT results back to the original shape + return np.transpose(audio_fft) + + +def calculate_signal_power(audio_fft: np.ndarray) -> np.ndarray: + """ + Calculate the power of the audio signal from its FFT. + + Args: + audio_fft: The FFT of the audio signal. + + Returns: + The power of the audio signal. + + Examples: + >>> audio_fft = np.array([1+2j, 2+3j, 3+4j, 4+5j]) + >>> power = calculate_signal_power(audio_fft) + >>> np.allclose(power, np.array([5, 13, 25, 41])) + True + """ + # Calculate the power by squaring the absolute values of the FFT coefficients + return np.square(np.abs(audio_fft)) + + +def freq_to_mel(freq: float) -> float: + """ + Convert a frequency in Hertz to the mel scale. + + Args: + freq: The frequency in Hertz. + + Returns: + The frequency in mel scale. + + Examples: + >>> round(freq_to_mel(1000), 2) + 999.99 + """ + # Use the formula to convert frequency to the mel scale + return 2595.0 * np.log10(1.0 + freq / 700.0) + + +def mel_to_freq(mels: float) -> float: + """ + Convert a frequency in the mel scale to Hertz. + + Args: + mels: The frequency in mel scale. + + Returns: + The frequency in Hertz. + + Examples: + >>> round(mel_to_freq(999.99), 2) + 1000.01 + """ + # Use the formula to convert mel scale to frequency + return 700.0 * (10.0 ** (mels / 2595.0) - 1.0) + + +def mel_spaced_filterbank( + sample_rate: int, mel_filter_num: int = 10, ftt_size: int = 1024 +) -> np.ndarray: + """ + Create a Mel-spaced filter bank for audio processing. + + Args: + sample_rate: The sample rate of the audio. + mel_filter_num: The number of mel filters (default is 10). + ftt_size: The size of the FFT (default is 1024). + + Returns: + Mel-spaced filter bank. + + Examples: + >>> round(mel_spaced_filterbank(8000, 10, 1024)[0][1], 10) + 0.0004603981 + """ + freq_min = 0 + freq_high = sample_rate // 2 + + logging.info(f"Minimum frequency: {freq_min}") + logging.info(f"Maximum frequency: {freq_high}") + + # Calculate filter points and mel frequencies + filter_points, mel_freqs = get_filter_points( + sample_rate, + freq_min, + freq_high, + mel_filter_num, + ftt_size, + ) + + filters = get_filters(filter_points, ftt_size) + + # normalize filters + # taken from the librosa library + enorm = 2.0 / (mel_freqs[2 : mel_filter_num + 2] - mel_freqs[:mel_filter_num]) + return filters * enorm[:, np.newaxis] + + +def get_filters(filter_points: np.ndarray, ftt_size: int) -> np.ndarray: + """ + Generate filters for audio processing. + + Args: + filter_points: A list of filter points. + ftt_size: The size of the FFT. + + Returns: + A matrix of filters. + + Examples: + >>> get_filters(np.array([0, 20, 51, 95, 161, 256], dtype=int), 512).shape + (4, 257) + """ + num_filters = len(filter_points) - 2 + filters = np.zeros((num_filters, int(ftt_size / 2) + 1)) + + for n in range(num_filters): + start = filter_points[n] + mid = filter_points[n + 1] + end = filter_points[n + 2] + + # Linearly increase values from 0 to 1 + filters[n, start:mid] = np.linspace(0, 1, mid - start) + + # Linearly decrease values from 1 to 0 + filters[n, mid:end] = np.linspace(1, 0, end - mid) + + return filters + + +def get_filter_points( + sample_rate: int, + freq_min: int, + freq_high: int, + mel_filter_num: int = 10, + ftt_size: int = 1024, +) -> tuple[np.ndarray, np.ndarray]: + """ + Calculate the filter points and frequencies for mel frequency filters. + + Args: + sample_rate: The sample rate of the audio. + freq_min: The minimum frequency in Hertz. + freq_high: The maximum frequency in Hertz. + mel_filter_num: The number of mel filters (default is 10). + ftt_size: The size of the FFT (default is 1024). + + Returns: + Filter points and corresponding frequencies. + + Examples: + >>> filter_points = get_filter_points(8000, 0, 4000, mel_filter_num=4, ftt_size=512) + >>> filter_points[0] + array([ 0, 20, 51, 95, 161, 256]) + >>> filter_points[1] + array([ 0. , 324.46707094, 799.33254207, 1494.30973963, + 2511.42581671, 4000. ]) + """ + # Convert minimum and maximum frequencies to mel scale + fmin_mel = freq_to_mel(freq_min) + fmax_mel = freq_to_mel(freq_high) + + logging.info(f"MEL min: {fmin_mel}") + logging.info(f"MEL max: {fmax_mel}") + + # Generate equally spaced mel frequencies + mels = np.linspace(fmin_mel, fmax_mel, num=mel_filter_num + 2) + + # Convert mel frequencies back to Hertz + freqs = mel_to_freq(mels) + + # Calculate filter points as integer values + filter_points = np.floor((ftt_size + 1) / sample_rate * freqs).astype(int) + + return filter_points, freqs + + +def discrete_cosine_transform(dct_filter_num: int, filter_num: int) -> np.ndarray: + """ + Compute the Discrete Cosine Transform (DCT) basis matrix. + + Args: + dct_filter_num: The number of DCT filters to generate. + filter_num: The number of the fbank filters. + + Returns: + The DCT basis matrix. + + Examples: + >>> round(discrete_cosine_transform(3, 5)[0][0], 5) + 0.44721 + """ + basis = np.empty((dct_filter_num, filter_num)) + basis[0, :] = 1.0 / np.sqrt(filter_num) + + samples = np.arange(1, 2 * filter_num, 2) * np.pi / (2.0 * filter_num) + + for i in range(1, dct_filter_num): + basis[i, :] = np.cos(i * samples) * np.sqrt(2.0 / filter_num) + + return basis + + +def example(wav_file_path: str = "./path-to-file/sample.wav") -> np.ndarray: + """ + Example function to calculate Mel Frequency Cepstral Coefficients + (MFCCs) from an audio file. + + Args: + wav_file_path: The path to the WAV audio file. + + Returns: + np.ndarray: The computed MFCCs for the audio. + """ + from scipy.io import wavfile + + # Load the audio from the WAV file + sample_rate, audio = wavfile.read(wav_file_path) + + # Calculate MFCCs + return mfcc(audio, sample_rate) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From eace4cea32b831a1683b4c431379f0cd7b9061db Mon Sep 17 00:00:00 2001 From: gudlu1925 <120262240+gudlu1925@users.noreply.github.com> Date: Wed, 27 Sep 2023 11:14:06 +0530 Subject: [PATCH 202/757] Added Coulomb_Law (#8714) * Create coulomb_law.py * Update coulomb_law.py * Update coulomb_law.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update and rename coulomb_law.py to coulombs_law.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update coulombs_law.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update coulombs_law.py * Update coulombs_law.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update coulombs_law.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update coulombs_law.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Tianyi Zheng --- physics/coulombs_law.py | 42 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 physics/coulombs_law.py diff --git a/physics/coulombs_law.py b/physics/coulombs_law.py new file mode 100644 index 000000000000..252e8ec0f74e --- /dev/null +++ b/physics/coulombs_law.py @@ -0,0 +1,42 @@ +""" +Coulomb's law states that the magnitude of the electrostatic force of attraction +or repulsion between two point charges is directly proportional to the product +of the magnitudes of charges and inversely proportional to the square of the +distance between them. + +F = k * q1 * q2 / r^2 + +k is Coulomb's constant and equals 1/(4π*ε0) +q1 is charge of first body (C) +q2 is charge of second body (C) +r is distance between two charged bodies (m) + +Reference: https://en.wikipedia.org/wiki/Coulomb%27s_law +""" + + +def coulombs_law(q1: float, q2: float, radius: float) -> float: + """ + Calculate the electrostatic force of attraction or repulsion + between two point charges + + >>> coulombs_law(15.5, 20, 15) + 12382849136.06 + >>> coulombs_law(1, 15, 5) + 5392531075.38 + >>> coulombs_law(20, -50, 15) + -39944674632.44 + >>> coulombs_law(-5, -8, 10) + 3595020716.92 + >>> coulombs_law(50, 100, 50) + 17975103584.6 + """ + if radius <= 0: + raise ValueError("The radius is always a positive non zero integer") + return round(((8.9875517923 * 10**9) * q1 * q2) / (radius**2), 2) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From b2e186f4b769ae98d04f7f2408d3ac86da44c06f Mon Sep 17 00:00:00 2001 From: Okza Pradhana Date: Wed, 27 Sep 2023 13:06:19 +0700 Subject: [PATCH 203/757] feat(maths): add function to perform calculation (#6602) * feat(maths): add function to perform calculation - Add single function to calculate sum of two positive numbers using bitwise operator * docs: add wikipedia url as explanation * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Apply suggestions from code review Co-authored-by: Caeden Perelli-Harris * Update sum_of_two_positive_numbers_bitwise.py * Update sum_of_two_positive_numbers_bitwise.py * Update sum_of_two_positive_numbers_bitwise.py --------- Co-authored-by: Okza Pradhana Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Tianyi Zheng Co-authored-by: Caeden Perelli-Harris --- maths/sum_of_two_positive_numbers_bitwise.py | 55 ++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 maths/sum_of_two_positive_numbers_bitwise.py diff --git a/maths/sum_of_two_positive_numbers_bitwise.py b/maths/sum_of_two_positive_numbers_bitwise.py new file mode 100644 index 000000000000..70eaf6887b64 --- /dev/null +++ b/maths/sum_of_two_positive_numbers_bitwise.py @@ -0,0 +1,55 @@ +""" +Calculates the sum of two non-negative integers using bitwise operators +Wikipedia explanation: https://en.wikipedia.org/wiki/Binary_number +""" + + +def bitwise_addition_recursive(number: int, other_number: int) -> int: + """ + >>> bitwise_addition_recursive(4, 5) + 9 + >>> bitwise_addition_recursive(8, 9) + 17 + >>> bitwise_addition_recursive(0, 4) + 4 + >>> bitwise_addition_recursive(4.5, 9) + Traceback (most recent call last): + ... + TypeError: Both arguments MUST be integers! + >>> bitwise_addition_recursive('4', 9) + Traceback (most recent call last): + ... + TypeError: Both arguments MUST be integers! + >>> bitwise_addition_recursive('4.5', 9) + Traceback (most recent call last): + ... + TypeError: Both arguments MUST be integers! + >>> bitwise_addition_recursive(-1, 9) + Traceback (most recent call last): + ... + ValueError: Both arguments MUST be non-negative! + >>> bitwise_addition_recursive(1, -9) + Traceback (most recent call last): + ... + ValueError: Both arguments MUST be non-negative! + """ + + if not isinstance(number, int) or not isinstance(other_number, int): + raise TypeError("Both arguments MUST be integers!") + + if number < 0 or other_number < 0: + raise ValueError("Both arguments MUST be non-negative!") + + bitwise_sum = number ^ other_number + carry = number & other_number + + if carry == 0: + return bitwise_sum + + return bitwise_addition_recursive(bitwise_sum, carry << 1) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 84ec9414e45380a5e946d4f73b921b274ecd4be7 Mon Sep 17 00:00:00 2001 From: thor-harsh <105957576+thor-harsh@users.noreply.github.com> Date: Wed, 27 Sep 2023 12:01:42 +0530 Subject: [PATCH 204/757] Update k_means_clust.py (#8996) * Update k_means_clust.py * Apply suggestions from code review --------- Co-authored-by: Tianyi Zheng --- machine_learning/k_means_clust.py | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/machine_learning/k_means_clust.py b/machine_learning/k_means_clust.py index 7c8142aab878..d93c5addf2ee 100644 --- a/machine_learning/k_means_clust.py +++ b/machine_learning/k_means_clust.py @@ -11,10 +11,10 @@ - initial_centroids , initial centroid values generated by utility function(mentioned in usage). - maxiter , maximum number of iterations to process. - - heterogeneity , empty list that will be filled with hetrogeneity values if passed + - heterogeneity , empty list that will be filled with heterogeneity values if passed to kmeans func. Usage: - 1. define 'k' value, 'X' features array and 'hetrogeneity' empty list + 1. define 'k' value, 'X' features array and 'heterogeneity' empty list 2. create initial_centroids, initial_centroids = get_initial_centroids( X, @@ -31,8 +31,8 @@ record_heterogeneity=heterogeneity, verbose=True # whether to print logs in console or not.(default=False) ) - 4. Plot the loss function, hetrogeneity values for every iteration saved in - hetrogeneity list. + 4. Plot the loss function and heterogeneity values for every iteration saved in + heterogeneity list. plot_heterogeneity( heterogeneity, k @@ -198,13 +198,10 @@ def report_generator( df: pd.DataFrame, clustering_variables: np.ndarray, fill_missing_report=None ) -> pd.DataFrame: """ - Function generates easy-erading clustering report. It takes 2 arguments as an input: - DataFrame - dataframe with predicted cluester column; - FillMissingReport - dictionary of rules how we are going to fill missing - values of for final report generate (not included in modeling); - in order to run the function following libraries must be imported: - import pandas as pd - import numpy as np + Generates a clustering report. This function takes 2 arguments as input: + df - dataframe with predicted cluster column + fill_missing_report - dictionary of rules on how we are going to fill in missing + values for final generated report (not included in modelling); >>> data = pd.DataFrame() >>> data['numbers'] = [1, 2, 3] >>> data['col1'] = [0.5, 2.5, 4.5] @@ -306,10 +303,10 @@ def report_generator( a.columns = report.columns # rename columns to match report report = report.drop( report[report.Type == "count"].index - ) # drop count values except cluster size + ) # drop count values except for cluster size report = pd.concat( [report, a, clustersize, clusterproportion], axis=0 - ) # concat report with clustert size and nan values + ) # concat report with cluster size and nan values report["Mark"] = report["Features"].isin(clustering_variables) cols = report.columns.tolist() cols = cols[0:2] + cols[-1:] + cols[2:-1] From 5830b29e7ecf5437ce46bcdefda88eedea693043 Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Wed, 27 Sep 2023 08:00:34 -0400 Subject: [PATCH 205/757] Fix `mypy` errors in `erosion_operation.py` (#8603) * updating DIRECTORY.md * Fix mypy errors in erosion_operation.py * Rename functions to use snake case * updating DIRECTORY.md * updating DIRECTORY.md * Replace raw file string with pathlib Path * Fix function name in erosion_operation.py doctest --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- .../erosion_operation.py | 39 +++++++++++-------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/digital_image_processing/morphological_operations/erosion_operation.py b/digital_image_processing/morphological_operations/erosion_operation.py index c0e1ef847237..53001da83468 100644 --- a/digital_image_processing/morphological_operations/erosion_operation.py +++ b/digital_image_processing/morphological_operations/erosion_operation.py @@ -1,34 +1,37 @@ +from pathlib import Path + import numpy as np from PIL import Image -def rgb2gray(rgb: np.array) -> np.array: +def rgb_to_gray(rgb: np.ndarray) -> np.ndarray: """ Return gray image from rgb image - >>> rgb2gray(np.array([[[127, 255, 0]]])) + + >>> rgb_to_gray(np.array([[[127, 255, 0]]])) array([[187.6453]]) - >>> rgb2gray(np.array([[[0, 0, 0]]])) + >>> rgb_to_gray(np.array([[[0, 0, 0]]])) array([[0.]]) - >>> rgb2gray(np.array([[[2, 4, 1]]])) + >>> rgb_to_gray(np.array([[[2, 4, 1]]])) array([[3.0598]]) - >>> rgb2gray(np.array([[[26, 255, 14], [5, 147, 20], [1, 200, 0]]])) + >>> rgb_to_gray(np.array([[[26, 255, 14], [5, 147, 20], [1, 200, 0]]])) array([[159.0524, 90.0635, 117.6989]]) """ r, g, b = rgb[:, :, 0], rgb[:, :, 1], rgb[:, :, 2] return 0.2989 * r + 0.5870 * g + 0.1140 * b -def gray2binary(gray: np.array) -> np.array: +def gray_to_binary(gray: np.ndarray) -> np.ndarray: """ Return binary image from gray image - >>> gray2binary(np.array([[127, 255, 0]])) + >>> gray_to_binary(np.array([[127, 255, 0]])) array([[False, True, False]]) - >>> gray2binary(np.array([[0]])) + >>> gray_to_binary(np.array([[0]])) array([[False]]) - >>> gray2binary(np.array([[26.2409, 4.9315, 1.4729]])) + >>> gray_to_binary(np.array([[26.2409, 4.9315, 1.4729]])) array([[False, False, False]]) - >>> gray2binary(np.array([[26, 255, 14], [5, 147, 20], [1, 200, 0]])) + >>> gray_to_binary(np.array([[26, 255, 14], [5, 147, 20], [1, 200, 0]])) array([[False, True, False], [False, True, False], [False, True, False]]) @@ -36,9 +39,10 @@ def gray2binary(gray: np.array) -> np.array: return (gray > 127) & (gray <= 255) -def erosion(image: np.array, kernel: np.array) -> np.array: +def erosion(image: np.ndarray, kernel: np.ndarray) -> np.ndarray: """ Return eroded image + >>> erosion(np.array([[True, True, False]]), np.array([[0, 1, 0]])) array([[False, False, False]]) >>> erosion(np.array([[True, False, False]]), np.array([[1, 1, 0]])) @@ -62,14 +66,17 @@ def erosion(image: np.array, kernel: np.array) -> np.array: return output -# kernel to be applied -structuring_element = np.array([[0, 1, 0], [1, 1, 1], [0, 1, 0]]) - if __name__ == "__main__": # read original image - image = np.array(Image.open(r"..\image_data\lena.jpg")) + lena_path = Path(__file__).resolve().parent / "image_data" / "lena.jpg" + lena = np.array(Image.open(lena_path)) + + # kernel to be applied + structuring_element = np.array([[0, 1, 0], [1, 1, 1], [0, 1, 0]]) + # Apply erosion operation to a binary image - output = erosion(gray2binary(rgb2gray(image)), structuring_element) + output = erosion(gray_to_binary(rgb_to_gray(lena)), structuring_element) + # Save the output image pil_img = Image.fromarray(output).convert("RGB") pil_img.save("result_erosion.png") From 76767d2f09d15aeff0a54cfc44652207eda2314e Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Wed, 27 Sep 2023 08:01:18 -0400 Subject: [PATCH 206/757] Consolidate the two existing kNN implementations (#8903) * Add type hints to k_nearest_neighbours.py * Refactor k_nearest_neighbours.py into class * Add documentation to k_nearest_neighbours.py * Use heap-based priority queue for k_nearest_neighbours.py * Delete knn_sklearn.py * updating DIRECTORY.md * Use optional args in k_nearest_neighbours.py for demo purposes * Fix wrong function arg in k_nearest_neighbours.py --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 1 - machine_learning/k_nearest_neighbours.py | 128 ++++++++++++++--------- machine_learning/knn_sklearn.py | 31 ------ 3 files changed, 79 insertions(+), 81 deletions(-) delete mode 100644 machine_learning/knn_sklearn.py diff --git a/DIRECTORY.md b/DIRECTORY.md index d81e4ec1ee83..902999460fe5 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -507,7 +507,6 @@ * [Gradient Descent](machine_learning/gradient_descent.py) * [K Means Clust](machine_learning/k_means_clust.py) * [K Nearest Neighbours](machine_learning/k_nearest_neighbours.py) - * [Knn Sklearn](machine_learning/knn_sklearn.py) * [Linear Discriminant Analysis](machine_learning/linear_discriminant_analysis.py) * [Linear Regression](machine_learning/linear_regression.py) * Local Weighted Learning diff --git a/machine_learning/k_nearest_neighbours.py b/machine_learning/k_nearest_neighbours.py index 2a90cfe5987a..a43757c5c20e 100644 --- a/machine_learning/k_nearest_neighbours.py +++ b/machine_learning/k_nearest_neighbours.py @@ -1,58 +1,88 @@ +""" +k-Nearest Neighbours (kNN) is a simple non-parametric supervised learning +algorithm used for classification. Given some labelled training data, a given +point is classified using its k nearest neighbours according to some distance +metric. The most commonly occurring label among the neighbours becomes the label +of the given point. In effect, the label of the given point is decided by a +majority vote. + +This implementation uses the commonly used Euclidean distance metric, but other +distance metrics can also be used. + +Reference: https://en.wikipedia.org/wiki/K-nearest_neighbors_algorithm +""" + from collections import Counter +from heapq import nsmallest import numpy as np from sklearn import datasets from sklearn.model_selection import train_test_split -data = datasets.load_iris() - -X = np.array(data["data"]) -y = np.array(data["target"]) -classes = data["target_names"] - -X_train, X_test, y_train, y_test = train_test_split(X, y) - - -def euclidean_distance(a, b): - """ - Gives the euclidean distance between two points - >>> euclidean_distance([0, 0], [3, 4]) - 5.0 - >>> euclidean_distance([1, 2, 3], [1, 8, 11]) - 10.0 - """ - return np.linalg.norm(np.array(a) - np.array(b)) - - -def classifier(train_data, train_target, classes, point, k=5): - """ - Classifies the point using the KNN algorithm - k closest points are found (ranked in ascending order of euclidean distance) - Params: - :train_data: Set of points that are classified into two or more classes - :train_target: List of classes in the order of train_data points - :classes: Labels of the classes - :point: The data point that needs to be classified - - >>> X_train = [[0, 0], [1, 0], [0, 1], [0.5, 0.5], [3, 3], [2, 3], [3, 2]] - >>> y_train = [0, 0, 0, 0, 1, 1, 1] - >>> classes = ['A','B']; point = [1.2,1.2] - >>> classifier(X_train, y_train, classes,point) - 'A' - """ - data = zip(train_data, train_target) - # List of distances of all points from the point to be classified - distances = [] - for data_point in data: - distance = euclidean_distance(data_point[0], point) - distances.append((distance, data_point[1])) - # Choosing 'k' points with the least distances. - votes = [i[1] for i in sorted(distances)[:k]] - # Most commonly occurring class among them - # is the class into which the point is classified - result = Counter(votes).most_common(1)[0][0] - return classes[result] + +class KNN: + def __init__( + self, + train_data: np.ndarray[float], + train_target: np.ndarray[int], + class_labels: list[str], + ) -> None: + """ + Create a kNN classifier using the given training data and class labels + """ + self.data = zip(train_data, train_target) + self.labels = class_labels + + @staticmethod + def _euclidean_distance(a: np.ndarray[float], b: np.ndarray[float]) -> float: + """ + Calculate the Euclidean distance between two points + >>> KNN._euclidean_distance(np.array([0, 0]), np.array([3, 4])) + 5.0 + >>> KNN._euclidean_distance(np.array([1, 2, 3]), np.array([1, 8, 11])) + 10.0 + """ + return np.linalg.norm(a - b) + + def classify(self, pred_point: np.ndarray[float], k: int = 5) -> str: + """ + Classify a given point using the kNN algorithm + >>> train_X = np.array( + ... [[0, 0], [1, 0], [0, 1], [0.5, 0.5], [3, 3], [2, 3], [3, 2]] + ... ) + >>> train_y = np.array([0, 0, 0, 0, 1, 1, 1]) + >>> classes = ['A', 'B'] + >>> knn = KNN(train_X, train_y, classes) + >>> point = np.array([1.2, 1.2]) + >>> knn.classify(point) + 'A' + """ + # Distances of all points from the point to be classified + distances = ( + (self._euclidean_distance(data_point[0], pred_point), data_point[1]) + for data_point in self.data + ) + + # Choosing k points with the shortest distances + votes = (i[1] for i in nsmallest(k, distances)) + + # Most commonly occurring class is the one into which the point is classified + result = Counter(votes).most_common(1)[0][0] + return self.labels[result] if __name__ == "__main__": - print(classifier(X_train, y_train, classes, [4.4, 3.1, 1.3, 1.4])) + import doctest + + doctest.testmod() + + iris = datasets.load_iris() + + X = np.array(iris["data"]) + y = np.array(iris["target"]) + iris_classes = iris["target_names"] + + X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0) + iris_point = np.array([4.4, 3.1, 1.3, 1.4]) + classifier = KNN(X_train, y_train, iris_classes) + print(classifier.classify(iris_point, k=3)) diff --git a/machine_learning/knn_sklearn.py b/machine_learning/knn_sklearn.py deleted file mode 100644 index 4a621a4244b6..000000000000 --- a/machine_learning/knn_sklearn.py +++ /dev/null @@ -1,31 +0,0 @@ -from sklearn.datasets import load_iris -from sklearn.model_selection import train_test_split -from sklearn.neighbors import KNeighborsClassifier - -# Load iris file -iris = load_iris() -iris.keys() - - -print(f"Target names: \n {iris.target_names} ") -print(f"\n Features: \n {iris.feature_names}") - -# Train set e Test set -X_train, X_test, y_train, y_test = train_test_split( - iris["data"], iris["target"], random_state=4 -) - -# KNN - -knn = KNeighborsClassifier(n_neighbors=1) -knn.fit(X_train, y_train) - -# new array to test -X_new = [[1, 2, 1, 4], [2, 3, 4, 5]] - -prediction = knn.predict(X_new) - -print( - f"\nNew array: \n {X_new}\n\nTarget Names Prediction: \n" - f" {iris['target_names'][prediction]}" -) From f9b8759ba82cd7ca4e4a99b9bc9b661ace5a93cc Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Wed, 27 Sep 2023 09:54:40 -0400 Subject: [PATCH 207/757] Move bitwise add (#9097) * updating DIRECTORY.md * updating DIRECTORY.md * updating DIRECTORY.md * Move and rename maths/sum_of_two_positive_numbers_bitwise.py * updating DIRECTORY.md --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 3 +++ .../bitwise_addition_recursive.py | 0 2 files changed, 3 insertions(+) rename maths/sum_of_two_positive_numbers_bitwise.py => bit_manipulation/bitwise_addition_recursive.py (100%) diff --git a/DIRECTORY.md b/DIRECTORY.md index 902999460fe5..e596d96e5e83 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -43,6 +43,7 @@ * [Binary Shifts](bit_manipulation/binary_shifts.py) * [Binary Twos Complement](bit_manipulation/binary_twos_complement.py) * [Binary Xor Operator](bit_manipulation/binary_xor_operator.py) + * [Bitwise Addition Recursive](bit_manipulation/bitwise_addition_recursive.py) * [Count 1S Brian Kernighan Method](bit_manipulation/count_1s_brian_kernighan_method.py) * [Count Number Of One Bits](bit_manipulation/count_number_of_one_bits.py) * [Gray Code Sequence](bit_manipulation/gray_code_sequence.py) @@ -514,6 +515,7 @@ * [Logistic Regression](machine_learning/logistic_regression.py) * Lstm * [Lstm Prediction](machine_learning/lstm/lstm_prediction.py) + * [Mfcc](machine_learning/mfcc.py) * [Multilayer Perceptron Classifier](machine_learning/multilayer_perceptron_classifier.py) * [Polynomial Regression](machine_learning/polynomial_regression.py) * [Scoring Functions](machine_learning/scoring_functions.py) @@ -752,6 +754,7 @@ * [Basic Orbital Capture](physics/basic_orbital_capture.py) * [Casimir Effect](physics/casimir_effect.py) * [Centripetal Force](physics/centripetal_force.py) + * [Coulombs Law](physics/coulombs_law.py) * [Grahams Law](physics/grahams_law.py) * [Horizontal Projectile Motion](physics/horizontal_projectile_motion.py) * [Hubble Parameter](physics/hubble_parameter.py) diff --git a/maths/sum_of_two_positive_numbers_bitwise.py b/bit_manipulation/bitwise_addition_recursive.py similarity index 100% rename from maths/sum_of_two_positive_numbers_bitwise.py rename to bit_manipulation/bitwise_addition_recursive.py From 38c2b839819549d1ab8566675fab09db449875cc Mon Sep 17 00:00:00 2001 From: aryan1165 <111041731+aryan1165@users.noreply.github.com> Date: Wed, 27 Sep 2023 19:26:01 +0530 Subject: [PATCH 208/757] Deleted euclidean_gcd.py. Fixes#8063 (#9108) --- maths/euclidean_gcd.py | 47 ------------------------------------------ 1 file changed, 47 deletions(-) delete mode 100644 maths/euclidean_gcd.py diff --git a/maths/euclidean_gcd.py b/maths/euclidean_gcd.py deleted file mode 100644 index de4b250243db..000000000000 --- a/maths/euclidean_gcd.py +++ /dev/null @@ -1,47 +0,0 @@ -""" https://en.wikipedia.org/wiki/Euclidean_algorithm """ - - -def euclidean_gcd(a: int, b: int) -> int: - """ - Examples: - >>> euclidean_gcd(3, 5) - 1 - - >>> euclidean_gcd(6, 3) - 3 - """ - while b: - a, b = b, a % b - return a - - -def euclidean_gcd_recursive(a: int, b: int) -> int: - """ - Recursive method for euclicedan gcd algorithm - - Examples: - >>> euclidean_gcd_recursive(3, 5) - 1 - - >>> euclidean_gcd_recursive(6, 3) - 3 - """ - return a if b == 0 else euclidean_gcd_recursive(b, a % b) - - -def main(): - print(f"euclidean_gcd(3, 5) = {euclidean_gcd(3, 5)}") - print(f"euclidean_gcd(5, 3) = {euclidean_gcd(5, 3)}") - print(f"euclidean_gcd(1, 3) = {euclidean_gcd(1, 3)}") - print(f"euclidean_gcd(3, 6) = {euclidean_gcd(3, 6)}") - print(f"euclidean_gcd(6, 3) = {euclidean_gcd(6, 3)}") - - print(f"euclidean_gcd_recursive(3, 5) = {euclidean_gcd_recursive(3, 5)}") - print(f"euclidean_gcd_recursive(5, 3) = {euclidean_gcd_recursive(5, 3)}") - print(f"euclidean_gcd_recursive(1, 3) = {euclidean_gcd_recursive(1, 3)}") - print(f"euclidean_gcd_recursive(3, 6) = {euclidean_gcd_recursive(3, 6)}") - print(f"euclidean_gcd_recursive(6, 3) = {euclidean_gcd_recursive(6, 3)}") - - -if __name__ == "__main__": - main() From 35dd529c85fc433e0780cdaff586c684208aa1b7 Mon Sep 17 00:00:00 2001 From: Hetarth Jain Date: Thu, 28 Sep 2023 23:54:46 +0530 Subject: [PATCH 209/757] Returning Index instead of boolean in knuth_morris_pratt (kmp) function, making it compatible with str.find(). (#9083) * Update knuth_morris_pratt.py - changed Boolean to Index * Update knuth_morris_pratt.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update knuth_morris_pratt.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update knuth_morris_pratt.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update back_propagation_neural_network.py * Update back_propagation_neural_network.py * Update strings/knuth_morris_pratt.py * Update knuth_morris_pratt.py * Update knuth_morris_pratt.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- strings/knuth_morris_pratt.py | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/strings/knuth_morris_pratt.py b/strings/knuth_morris_pratt.py index a488c171a93b..8a04eb2532c0 100644 --- a/strings/knuth_morris_pratt.py +++ b/strings/knuth_morris_pratt.py @@ -1,7 +1,7 @@ from __future__ import annotations -def kmp(pattern: str, text: str) -> bool: +def knuth_morris_pratt(text: str, pattern: str) -> int: """ The Knuth-Morris-Pratt Algorithm for finding a pattern within a piece of text with complexity O(n + m) @@ -14,6 +14,12 @@ def kmp(pattern: str, text: str) -> bool: 2) Step through the text one character at a time and compare it to a character in the pattern updating our location within the pattern if necessary + >>> kmp = "knuth_morris_pratt" + >>> all( + ... knuth_morris_pratt(kmp, s) == kmp.find(s) + ... for s in ("kn", "h_m", "rr", "tt", "not there") + ... ) + True """ # 1) Construct the failure array @@ -24,7 +30,7 @@ def kmp(pattern: str, text: str) -> bool: while i < len(text): if pattern[j] == text[i]: if j == (len(pattern) - 1): - return True + return i - j j += 1 # if this is a prefix in our pattern @@ -33,7 +39,7 @@ def kmp(pattern: str, text: str) -> bool: j = failure[j - 1] continue i += 1 - return False + return -1 def get_failure_array(pattern: str) -> list[int]: @@ -57,27 +63,38 @@ def get_failure_array(pattern: str) -> list[int]: if __name__ == "__main__": + import doctest + + doctest.testmod() + # Test 1) pattern = "abc1abc12" text1 = "alskfjaldsabc1abc1abc12k23adsfabcabc" text2 = "alskfjaldsk23adsfabcabc" - assert kmp(pattern, text1) and not kmp(pattern, text2) + assert knuth_morris_pratt(text1, pattern) and knuth_morris_pratt(text2, pattern) # Test 2) pattern = "ABABX" text = "ABABZABABYABABX" - assert kmp(pattern, text) + assert knuth_morris_pratt(text, pattern) # Test 3) pattern = "AAAB" text = "ABAAAAAB" - assert kmp(pattern, text) + assert knuth_morris_pratt(text, pattern) # Test 4) pattern = "abcdabcy" text = "abcxabcdabxabcdabcdabcy" - assert kmp(pattern, text) + assert knuth_morris_pratt(text, pattern) + + # Test 5) -> Doctests + kmp = "knuth_morris_pratt" + assert all( + knuth_morris_pratt(kmp, s) == kmp.find(s) + for s in ("kn", "h_m", "rr", "tt", "not there") + ) - # Test 5) + # Test 6) pattern = "aabaabaaa" assert get_failure_array(pattern) == [0, 1, 0, 1, 2, 3, 4, 5, 2] From 467903aa33ad746262bd46d803231d0930131197 Mon Sep 17 00:00:00 2001 From: Belhadj Ahmed Walid <80895522+BAW2501@users.noreply.github.com> Date: Sat, 30 Sep 2023 05:33:13 +0100 Subject: [PATCH 210/757] added smith waterman algorithm (#9001) * added smith waterman algorithm * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * descriptive names for the parameters a and b * doctesting lowercase upcase empty string cases * updated block quot,fixed traceback and doctests * shorter block quote Co-authored-by: Tianyi Zheng * global vars to func params,more doctests * updated doctests * user access to SW params * formating --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Tianyi Zheng --- dynamic_programming/smith_waterman.py | 193 ++++++++++++++++++++++++++ 1 file changed, 193 insertions(+) create mode 100644 dynamic_programming/smith_waterman.py diff --git a/dynamic_programming/smith_waterman.py b/dynamic_programming/smith_waterman.py new file mode 100644 index 000000000000..4c5d58379f07 --- /dev/null +++ b/dynamic_programming/smith_waterman.py @@ -0,0 +1,193 @@ +""" +https://en.wikipedia.org/wiki/Smith%E2%80%93Waterman_algorithm +The Smith-Waterman algorithm is a dynamic programming algorithm used for sequence +alignment. It is particularly useful for finding similarities between two sequences, +such as DNA or protein sequences. In this implementation, gaps are penalized +linearly, meaning that the score is reduced by a fixed amount for each gap introduced +in the alignment. However, it's important to note that the Smith-Waterman algorithm +supports other gap penalty methods as well. +""" + + +def score_function( + source_char: str, + target_char: str, + match: int = 1, + mismatch: int = -1, + gap: int = -2, +) -> int: + """ + Calculate the score for a character pair based on whether they match or mismatch. + Returns 1 if the characters match, -1 if they mismatch, and -2 if either of the + characters is a gap. + >>> score_function('A', 'A') + 1 + >>> score_function('A', 'C') + -1 + >>> score_function('-', 'A') + -2 + >>> score_function('A', '-') + -2 + >>> score_function('-', '-') + -2 + """ + if "-" in (source_char, target_char): + return gap + return match if source_char == target_char else mismatch + + +def smith_waterman( + query: str, + subject: str, + match: int = 1, + mismatch: int = -1, + gap: int = -2, +) -> list[list[int]]: + """ + Perform the Smith-Waterman local sequence alignment algorithm. + Returns a 2D list representing the score matrix. Each value in the matrix + corresponds to the score of the best local alignment ending at that point. + >>> smith_waterman('ACAC', 'CA') + [[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 0, 2], [0, 1, 0]] + >>> smith_waterman('acac', 'ca') + [[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 0, 2], [0, 1, 0]] + >>> smith_waterman('ACAC', 'ca') + [[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 0, 2], [0, 1, 0]] + >>> smith_waterman('acac', 'CA') + [[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 0, 2], [0, 1, 0]] + >>> smith_waterman('ACAC', '') + [[0], [0], [0], [0], [0]] + >>> smith_waterman('', 'CA') + [[0, 0, 0]] + >>> smith_waterman('ACAC', 'CA') + [[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 0, 2], [0, 1, 0]] + + >>> smith_waterman('acac', 'ca') + [[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 0, 2], [0, 1, 0]] + + >>> smith_waterman('ACAC', 'ca') + [[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 0, 2], [0, 1, 0]] + + >>> smith_waterman('acac', 'CA') + [[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 0, 2], [0, 1, 0]] + + >>> smith_waterman('ACAC', '') + [[0], [0], [0], [0], [0]] + + >>> smith_waterman('', 'CA') + [[0, 0, 0]] + + >>> smith_waterman('AGT', 'AGT') + [[0, 0, 0, 0], [0, 1, 0, 0], [0, 0, 2, 0], [0, 0, 0, 3]] + + >>> smith_waterman('AGT', 'GTA') + [[0, 0, 0, 0], [0, 0, 0, 1], [0, 1, 0, 0], [0, 0, 2, 0]] + + >>> smith_waterman('AGT', 'GTC') + [[0, 0, 0, 0], [0, 0, 0, 0], [0, 1, 0, 0], [0, 0, 2, 0]] + + >>> smith_waterman('AGT', 'G') + [[0, 0], [0, 0], [0, 1], [0, 0]] + + >>> smith_waterman('G', 'AGT') + [[0, 0, 0, 0], [0, 0, 1, 0]] + + >>> smith_waterman('AGT', 'AGTCT') + [[0, 0, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0], [0, 0, 2, 0, 0, 0], [0, 0, 0, 3, 1, 1]] + + >>> smith_waterman('AGTCT', 'AGT') + [[0, 0, 0, 0], [0, 1, 0, 0], [0, 0, 2, 0], [0, 0, 0, 3], [0, 0, 0, 1], [0, 0, 0, 1]] + + >>> smith_waterman('AGTCT', 'GTC') + [[0, 0, 0, 0], [0, 0, 0, 0], [0, 1, 0, 0], [0, 0, 2, 0], [0, 0, 0, 3], [0, 0, 1, 1]] + """ + # make both query and subject uppercase + query = query.upper() + subject = subject.upper() + + # Initialize score matrix + m = len(query) + n = len(subject) + score = [[0] * (n + 1) for _ in range(m + 1)] + kwargs = {"match": match, "mismatch": mismatch, "gap": gap} + + for i in range(1, m + 1): + for j in range(1, n + 1): + # Calculate scores for each cell + match = score[i - 1][j - 1] + score_function( + query[i - 1], subject[j - 1], **kwargs + ) + delete = score[i - 1][j] + gap + insert = score[i][j - 1] + gap + + # Take maximum score + score[i][j] = max(0, match, delete, insert) + + return score + + +def traceback(score: list[list[int]], query: str, subject: str) -> str: + r""" + Perform traceback to find the optimal local alignment. + Starts from the highest scoring cell in the matrix and traces back recursively + until a 0 score is found. Returns the alignment strings. + >>> traceback([[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 0, 2], [0, 1, 0]], 'ACAC', 'CA') + 'CA\nCA' + >>> traceback([[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 0, 2], [0, 1, 0]], 'acac', 'ca') + 'CA\nCA' + >>> traceback([[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 0, 2], [0, 1, 0]], 'ACAC', 'ca') + 'CA\nCA' + >>> traceback([[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 0, 2], [0, 1, 0]], 'acac', 'CA') + 'CA\nCA' + >>> traceback([[0, 0, 0]], 'ACAC', '') + '' + """ + # make both query and subject uppercase + query = query.upper() + subject = subject.upper() + # find the indices of the maximum value in the score matrix + max_value = float("-inf") + i_max = j_max = 0 + for i, row in enumerate(score): + for j, value in enumerate(row): + if value > max_value: + max_value = value + i_max, j_max = i, j + # Traceback logic to find optimal alignment + i = i_max + j = j_max + align1 = "" + align2 = "" + gap = score_function("-", "-") + # guard against empty query or subject + if i == 0 or j == 0: + return "" + while i > 0 and j > 0: + if score[i][j] == score[i - 1][j - 1] + score_function( + query[i - 1], subject[j - 1] + ): + # optimal path is a diagonal take both letters + align1 = query[i - 1] + align1 + align2 = subject[j - 1] + align2 + i -= 1 + j -= 1 + elif score[i][j] == score[i - 1][j] + gap: + # optimal path is a vertical + align1 = query[i - 1] + align1 + align2 = f"-{align2}" + i -= 1 + else: + # optimal path is a horizontal + align1 = f"-{align1}" + align2 = subject[j - 1] + align2 + j -= 1 + + return f"{align1}\n{align2}" + + +if __name__ == "__main__": + query = "HEAGAWGHEE" + subject = "PAWHEAE" + + score = smith_waterman(query, subject, match=1, mismatch=-1, gap=-2) + print(traceback(score, query, subject)) From dec96438be1a165eaa300a4d6df33e338b4e44c6 Mon Sep 17 00:00:00 2001 From: Caeden Perelli-Harris Date: Sat, 30 Sep 2023 05:57:56 +0100 Subject: [PATCH 211/757] Create word search algorithm (#8906) * feat(other): Create word_search algorithm * updating DIRECTORY.md * doc(word_search): Link to wikipedia article * Apply suggestions from code review Co-authored-by: Tianyi Zheng * Update word_search.py --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: Tianyi Zheng --- DIRECTORY.md | 1 + other/word_search.py | 396 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 397 insertions(+) create mode 100644 other/word_search.py diff --git a/DIRECTORY.md b/DIRECTORY.md index e596d96e5e83..aabbf27512ce 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -747,6 +747,7 @@ * [Scoring Algorithm](other/scoring_algorithm.py) * [Sdes](other/sdes.py) * [Tower Of Hanoi](other/tower_of_hanoi.py) + * [Word Search](other/word_search.py) ## Physics * [Altitude Pressure](physics/altitude_pressure.py) diff --git a/other/word_search.py b/other/word_search.py new file mode 100644 index 000000000000..a4796e220c7c --- /dev/null +++ b/other/word_search.py @@ -0,0 +1,396 @@ +""" +Creates a random wordsearch with eight different directions +that are best described as compass locations. + +@ https://en.wikipedia.org/wiki/Word_search +""" + + +from random import choice, randint, shuffle + +# The words to display on the word search - +# can be made dynamic by randonly selecting a certain number of +# words from a predefined word file, while ensuring the character +# count fits within the matrix size (n x m) +WORDS = ["cat", "dog", "snake", "fish"] + +WIDTH = 10 +HEIGHT = 10 + + +class WordSearch: + """ + >>> ws = WordSearch(WORDS, WIDTH, HEIGHT) + >>> ws.board # doctest: +ELLIPSIS + [[None, ..., None], ..., [None, ..., None]] + >>> ws.generate_board() + """ + + def __init__(self, words: list[str], width: int, height: int) -> None: + self.words = words + self.width = width + self.height = height + + # Board matrix holding each letter + self.board: list[list[str | None]] = [[None] * width for _ in range(height)] + + def insert_north(self, word: str, rows: list[int], cols: list[int]) -> None: + """ + >>> ws = WordSearch(WORDS, 3, 3) + >>> ws.insert_north("cat", [2], [2]) + >>> ws.board # doctest: +NORMALIZE_WHITESPACE + [[None, None, 't'], + [None, None, 'a'], + [None, None, 'c']] + >>> ws.insert_north("at", [0, 1, 2], [2, 1]) + >>> ws.board # doctest: +NORMALIZE_WHITESPACE + [[None, 't', 't'], + [None, 'a', 'a'], + [None, None, 'c']] + """ + word_length = len(word) + # Attempt to insert the word into each row and when successful, exit + for row in rows: + # Check if there is space above the row to fit in the word + if word_length > row + 1: + continue + + # Attempt to insert the word into each column + for col in cols: + # Only check to be made here is if there are existing letters + # above the column that will be overwritten + letters_above = [self.board[row - i][col] for i in range(word_length)] + if all(letter is None for letter in letters_above): + # Successful, insert the word north + for i in range(word_length): + self.board[row - i][col] = word[i] + return + + def insert_northeast(self, word: str, rows: list[int], cols: list[int]) -> None: + """ + >>> ws = WordSearch(WORDS, 3, 3) + >>> ws.insert_northeast("cat", [2], [0]) + >>> ws.board # doctest: +NORMALIZE_WHITESPACE + [[None, None, 't'], + [None, 'a', None], + ['c', None, None]] + >>> ws.insert_northeast("at", [0, 1], [2, 1, 0]) + >>> ws.board # doctest: +NORMALIZE_WHITESPACE + [[None, 't', 't'], + ['a', 'a', None], + ['c', None, None]] + """ + word_length = len(word) + # Attempt to insert the word into each row and when successful, exit + for row in rows: + # Check if there is space for the word above the row + if word_length > row + 1: + continue + + # Attempt to insert the word into each column + for col in cols: + # Check if there is space to the right of the word as well as above + if word_length + col > self.width: + continue + + # Check if there are existing letters + # to the right of the column that will be overwritten + letters_diagonal_left = [ + self.board[row - i][col + i] for i in range(word_length) + ] + if all(letter is None for letter in letters_diagonal_left): + # Successful, insert the word northeast + for i in range(word_length): + self.board[row - i][col + i] = word[i] + return + + def insert_east(self, word: str, rows: list[int], cols: list[int]) -> None: + """ + >>> ws = WordSearch(WORDS, 3, 3) + >>> ws.insert_east("cat", [1], [0]) + >>> ws.board # doctest: +NORMALIZE_WHITESPACE + [[None, None, None], + ['c', 'a', 't'], + [None, None, None]] + >>> ws.insert_east("at", [1, 0], [2, 1, 0]) + >>> ws.board # doctest: +NORMALIZE_WHITESPACE + [[None, 'a', 't'], + ['c', 'a', 't'], + [None, None, None]] + """ + word_length = len(word) + # Attempt to insert the word into each row and when successful, exit + for row in rows: + # Attempt to insert the word into each column + for col in cols: + # Check if there is space to the right of the word + if word_length + col > self.width: + continue + + # Check if there are existing letters + # to the right of the column that will be overwritten + letters_left = [self.board[row][col + i] for i in range(word_length)] + if all(letter is None for letter in letters_left): + # Successful, insert the word east + for i in range(word_length): + self.board[row][col + i] = word[i] + return + + def insert_southeast(self, word: str, rows: list[int], cols: list[int]) -> None: + """ + >>> ws = WordSearch(WORDS, 3, 3) + >>> ws.insert_southeast("cat", [0], [0]) + >>> ws.board # doctest: +NORMALIZE_WHITESPACE + [['c', None, None], + [None, 'a', None], + [None, None, 't']] + >>> ws.insert_southeast("at", [1, 0], [2, 1, 0]) + >>> ws.board # doctest: +NORMALIZE_WHITESPACE + [['c', None, None], + ['a', 'a', None], + [None, 't', 't']] + """ + word_length = len(word) + # Attempt to insert the word into each row and when successful, exit + for row in rows: + # Check if there is space for the word below the row + if word_length + row > self.height: + continue + + # Attempt to insert the word into each column + for col in cols: + # Check if there is space to the right of the word as well as below + if word_length + col > self.width: + continue + + # Check if there are existing letters + # to the right of the column that will be overwritten + letters_diagonal_left = [ + self.board[row + i][col + i] for i in range(word_length) + ] + if all(letter is None for letter in letters_diagonal_left): + # Successful, insert the word southeast + for i in range(word_length): + self.board[row + i][col + i] = word[i] + return + + def insert_south(self, word: str, rows: list[int], cols: list[int]) -> None: + """ + >>> ws = WordSearch(WORDS, 3, 3) + >>> ws.insert_south("cat", [0], [0]) + >>> ws.board # doctest: +NORMALIZE_WHITESPACE + [['c', None, None], + ['a', None, None], + ['t', None, None]] + >>> ws.insert_south("at", [2, 1, 0], [0, 1, 2]) + >>> ws.board # doctest: +NORMALIZE_WHITESPACE + [['c', None, None], + ['a', 'a', None], + ['t', 't', None]] + """ + word_length = len(word) + # Attempt to insert the word into each row and when successful, exit + for row in rows: + # Check if there is space below the row to fit in the word + if word_length + row > self.height: + continue + + # Attempt to insert the word into each column + for col in cols: + # Only check to be made here is if there are existing letters + # below the column that will be overwritten + letters_below = [self.board[row + i][col] for i in range(word_length)] + if all(letter is None for letter in letters_below): + # Successful, insert the word south + for i in range(word_length): + self.board[row + i][col] = word[i] + return + + def insert_southwest(self, word: str, rows: list[int], cols: list[int]) -> None: + """ + >>> ws = WordSearch(WORDS, 3, 3) + >>> ws.insert_southwest("cat", [0], [2]) + >>> ws.board # doctest: +NORMALIZE_WHITESPACE + [[None, None, 'c'], + [None, 'a', None], + ['t', None, None]] + >>> ws.insert_southwest("at", [1, 2], [2, 1, 0]) + >>> ws.board # doctest: +NORMALIZE_WHITESPACE + [[None, None, 'c'], + [None, 'a', 'a'], + ['t', 't', None]] + """ + word_length = len(word) + # Attempt to insert the word into each row and when successful, exit + for row in rows: + # Check if there is space for the word below the row + if word_length + row > self.height: + continue + + # Attempt to insert the word into each column + for col in cols: + # Check if there is space to the left of the word as well as below + if word_length > col + 1: + continue + + # Check if there are existing letters + # to the right of the column that will be overwritten + letters_diagonal_left = [ + self.board[row + i][col - i] for i in range(word_length) + ] + if all(letter is None for letter in letters_diagonal_left): + # Successful, insert the word southwest + for i in range(word_length): + self.board[row + i][col - i] = word[i] + return + + def insert_west(self, word: str, rows: list[int], cols: list[int]) -> None: + """ + >>> ws = WordSearch(WORDS, 3, 3) + >>> ws.insert_west("cat", [1], [2]) + >>> ws.board # doctest: +NORMALIZE_WHITESPACE + [[None, None, None], + ['t', 'a', 'c'], + [None, None, None]] + >>> ws.insert_west("at", [1, 0], [1, 2, 0]) + >>> ws.board # doctest: +NORMALIZE_WHITESPACE + [['t', 'a', None], + ['t', 'a', 'c'], + [None, None, None]] + """ + word_length = len(word) + # Attempt to insert the word into each row and when successful, exit + for row in rows: + # Attempt to insert the word into each column + for col in cols: + # Check if there is space to the left of the word + if word_length > col + 1: + continue + + # Check if there are existing letters + # to the left of the column that will be overwritten + letters_left = [self.board[row][col - i] for i in range(word_length)] + if all(letter is None for letter in letters_left): + # Successful, insert the word west + for i in range(word_length): + self.board[row][col - i] = word[i] + return + + def insert_northwest(self, word: str, rows: list[int], cols: list[int]) -> None: + """ + >>> ws = WordSearch(WORDS, 3, 3) + >>> ws.insert_northwest("cat", [2], [2]) + >>> ws.board # doctest: +NORMALIZE_WHITESPACE + [['t', None, None], + [None, 'a', None], + [None, None, 'c']] + >>> ws.insert_northwest("at", [1, 2], [0, 1]) + >>> ws.board # doctest: +NORMALIZE_WHITESPACE + [['t', None, None], + ['t', 'a', None], + [None, 'a', 'c']] + """ + word_length = len(word) + # Attempt to insert the word into each row and when successful, exit + for row in rows: + # Check if there is space for the word above the row + if word_length > row + 1: + continue + + # Attempt to insert the word into each column + for col in cols: + # Check if there is space to the left of the word as well as above + if word_length > col + 1: + continue + + # Check if there are existing letters + # to the right of the column that will be overwritten + letters_diagonal_left = [ + self.board[row - i][col - i] for i in range(word_length) + ] + if all(letter is None for letter in letters_diagonal_left): + # Successful, insert the word northwest + for i in range(word_length): + self.board[row - i][col - i] = word[i] + return + + def generate_board(self) -> None: + """ + Generates a board with a random direction for each word. + + >>> wt = WordSearch(WORDS, WIDTH, HEIGHT) + >>> wt.generate_board() + >>> len(list(filter(lambda word: word is not None, sum(wt.board, start=[]))) + ... ) == sum(map(lambda word: len(word), WORDS)) + True + """ + directions = ( + self.insert_north, + self.insert_northeast, + self.insert_east, + self.insert_southeast, + self.insert_south, + self.insert_southwest, + self.insert_west, + self.insert_northwest, + ) + for word in self.words: + # Shuffle the row order and column order that is used when brute forcing + # the insertion of the word + rows, cols = list(range(self.height)), list(range(self.width)) + shuffle(rows) + shuffle(cols) + + # Insert the word via the direction + choice(directions)(word, rows, cols) + + +def visualise_word_search( + board: list[list[str | None]] | None = None, *, add_fake_chars: bool = True +) -> None: + """ + Graphically displays the word search in the terminal. + + >>> ws = WordSearch(WORDS, 5, 5) + >>> ws.insert_north("cat", [4], [4]) + >>> visualise_word_search( + ... ws.board, add_fake_chars=False) # doctest: +NORMALIZE_WHITESPACE + # # # # # + # # # # # + # # # # t + # # # # a + # # # # c + >>> ws.insert_northeast("snake", [4], [4, 3, 2, 1, 0]) + >>> visualise_word_search( + ... ws.board, add_fake_chars=False) # doctest: +NORMALIZE_WHITESPACE + # # # # e + # # # k # + # # a # t + # n # # a + s # # # c + """ + if board is None: + word_search = WordSearch(WORDS, WIDTH, HEIGHT) + word_search.generate_board() + board = word_search.board + + result = "" + for row in range(len(board)): + for col in range(len(board[0])): + character = "#" + if (letter := board[row][col]) is not None: + character = letter + # Empty char, so add a fake char + elif add_fake_chars: + character = chr(randint(97, 122)) + result += f"{character} " + result += "\n" + print(result, end="") + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + + visualise_word_search() From aaf7195465ddfe743cda707cac0feacf70287ecd Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Sat, 30 Sep 2023 23:10:33 -0400 Subject: [PATCH 212/757] Fix mypy error in web_programming/reddit.py (#9162) * updating DIRECTORY.md * updating DIRECTORY.md * Fix mypy error in web_programming/reddit.py web_programming/reddit.py:36: error: Missing named argument "response" for "HTTPError" [call-arg] --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 2 +- web_programming/reddit.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index aabbf27512ce..001da2c15b99 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -341,6 +341,7 @@ * [Palindrome Partitioning](dynamic_programming/palindrome_partitioning.py) * [Regex Match](dynamic_programming/regex_match.py) * [Rod Cutting](dynamic_programming/rod_cutting.py) + * [Smith Waterman](dynamic_programming/smith_waterman.py) * [Subset Generation](dynamic_programming/subset_generation.py) * [Sum Of Subset](dynamic_programming/sum_of_subset.py) * [Tribonacci](dynamic_programming/tribonacci.py) @@ -567,7 +568,6 @@ * [Dual Number Automatic Differentiation](maths/dual_number_automatic_differentiation.py) * [Entropy](maths/entropy.py) * [Euclidean Distance](maths/euclidean_distance.py) - * [Euclidean Gcd](maths/euclidean_gcd.py) * [Euler Method](maths/euler_method.py) * [Euler Modified](maths/euler_modified.py) * [Eulers Totient](maths/eulers_totient.py) diff --git a/web_programming/reddit.py b/web_programming/reddit.py index 5ca5f828c0fb..1c165ecc49ec 100644 --- a/web_programming/reddit.py +++ b/web_programming/reddit.py @@ -33,7 +33,7 @@ def get_subreddit_data( headers={"User-agent": "A random string"}, ) if response.status_code == 429: - raise requests.HTTPError + raise requests.HTTPError(response=response) data = response.json() if not wanted_data: From 5f8d1cb5c99cccf6e5ce62fbca9c3dcd60a75292 Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Sat, 30 Sep 2023 23:31:35 -0400 Subject: [PATCH 213/757] Fix DeprecationWarning in local_weighted_learning.py (#9165) Fix DeprecationWarning that occurs during build due to converting an np.ndarray to a scalar implicitly --- .../local_weighted_learning/local_weighted_learning.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/machine_learning/local_weighted_learning/local_weighted_learning.py b/machine_learning/local_weighted_learning/local_weighted_learning.py index 8dd0e55d41df..ada6f7cd2520 100644 --- a/machine_learning/local_weighted_learning/local_weighted_learning.py +++ b/machine_learning/local_weighted_learning/local_weighted_learning.py @@ -122,7 +122,7 @@ def local_weight_regression( """ y_pred = np.zeros(len(x_train)) # Initialize array of predictions for i, item in enumerate(x_train): - y_pred[i] = item @ local_weight(item, x_train, y_train, tau) + y_pred[i] = np.dot(item, local_weight(item, x_train, y_train, tau)) return y_pred From 320d895b86133b4b5c489df39ab245fa6be4bce8 Mon Sep 17 00:00:00 2001 From: aryan1165 <111041731+aryan1165@users.noreply.github.com> Date: Sun, 1 Oct 2023 09:36:15 +0530 Subject: [PATCH 214/757] Fixed permute_recursive() by passing nums.copy(). Fixes #9014 (#9161) * Fixes #9014 * Fixed permute_recursive() by passing nums.copy() --- data_structures/arrays/permutations.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/data_structures/arrays/permutations.py b/data_structures/arrays/permutations.py index 0f029187b92b..4906dd5c2ae1 100644 --- a/data_structures/arrays/permutations.py +++ b/data_structures/arrays/permutations.py @@ -10,7 +10,7 @@ def permute_recursive(nums: list[int]) -> list[list[int]]: return [[]] for _ in range(len(nums)): n = nums.pop(0) - permutations = permute_recursive(nums) + permutations = permute_recursive(nums.copy()) for perm in permutations: perm.append(n) result.extend(permutations) @@ -43,6 +43,6 @@ def backtrack(start: int) -> None: if __name__ == "__main__": import doctest - res = permute_backtrack([1, 2, 3]) - print(res) + result = permute_backtrack([1, 2, 3]) + print(result) doctest.testmod() From 280dfc1a22adb08aa71984ee4b22e4df220a8e68 Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Sun, 1 Oct 2023 00:07:25 -0400 Subject: [PATCH 215/757] Fix DeprecationWarning in local_weighted_learning.py (Attempt 2) (#9170) * Fix DeprecationWarning in local_weighted_learning.py Fix DeprecationWarning that occurs during build due to converting an np.ndarray to a scalar implicitly * DeprecationWarning fix attempt 2 --- .../local_weighted_learning/local_weighted_learning.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/machine_learning/local_weighted_learning/local_weighted_learning.py b/machine_learning/local_weighted_learning/local_weighted_learning.py index ada6f7cd2520..f3056da40e24 100644 --- a/machine_learning/local_weighted_learning/local_weighted_learning.py +++ b/machine_learning/local_weighted_learning/local_weighted_learning.py @@ -122,7 +122,7 @@ def local_weight_regression( """ y_pred = np.zeros(len(x_train)) # Initialize array of predictions for i, item in enumerate(x_train): - y_pred[i] = np.dot(item, local_weight(item, x_train, y_train, tau)) + y_pred[i] = np.dot(item, local_weight(item, x_train, y_train, tau)).item() return y_pred From 832610ab1d05c8cea2814adcc8db5597e7e5ede7 Mon Sep 17 00:00:00 2001 From: aryan1165 <111041731+aryan1165@users.noreply.github.com> Date: Sun, 1 Oct 2023 10:10:53 +0530 Subject: [PATCH 216/757] Deleted sorts/random_pivot_quick_sort.py (#9178) --- sorts/random_pivot_quick_sort.py | 44 -------------------------------- 1 file changed, 44 deletions(-) delete mode 100644 sorts/random_pivot_quick_sort.py diff --git a/sorts/random_pivot_quick_sort.py b/sorts/random_pivot_quick_sort.py deleted file mode 100644 index 748b6741047e..000000000000 --- a/sorts/random_pivot_quick_sort.py +++ /dev/null @@ -1,44 +0,0 @@ -""" -Picks the random index as the pivot -""" -import random - - -def partition(a, left_index, right_index): - pivot = a[left_index] - i = left_index + 1 - for j in range(left_index + 1, right_index): - if a[j] < pivot: - a[j], a[i] = a[i], a[j] - i += 1 - a[left_index], a[i - 1] = a[i - 1], a[left_index] - return i - 1 - - -def quick_sort_random(a, left, right): - if left < right: - pivot = random.randint(left, right - 1) - a[pivot], a[left] = ( - a[left], - a[pivot], - ) # switches the pivot with the left most bound - pivot_index = partition(a, left, right) - quick_sort_random( - a, left, pivot_index - ) # recursive quicksort to the left of the pivot point - quick_sort_random( - a, pivot_index + 1, right - ) # recursive quicksort to the right of the pivot point - - -def main(): - user_input = input("Enter numbers separated by a comma:\n").strip() - arr = [int(item) for item in user_input.split(",")] - - quick_sort_random(arr, 0, len(arr)) - - print(arr) - - -if __name__ == "__main__": - main() From 3dbafd3f0db55e040a7fd277134d86ec3accfb57 Mon Sep 17 00:00:00 2001 From: aryan1165 <111041731+aryan1165@users.noreply.github.com> Date: Sun, 1 Oct 2023 10:51:46 +0530 Subject: [PATCH 217/757] Deleted random_normal_distribution_quicksort.py. Fixes #9124 (#9182) --- sorts/random_normal_distribution_quicksort.py | 62 ------------------- 1 file changed, 62 deletions(-) delete mode 100644 sorts/random_normal_distribution_quicksort.py diff --git a/sorts/random_normal_distribution_quicksort.py b/sorts/random_normal_distribution_quicksort.py deleted file mode 100644 index f7f60903c546..000000000000 --- a/sorts/random_normal_distribution_quicksort.py +++ /dev/null @@ -1,62 +0,0 @@ -from random import randint -from tempfile import TemporaryFile - -import numpy as np - - -def _in_place_quick_sort(a, start, end): - count = 0 - if start < end: - pivot = randint(start, end) - temp = a[end] - a[end] = a[pivot] - a[pivot] = temp - - p, count = _in_place_partition(a, start, end) - count += _in_place_quick_sort(a, start, p - 1) - count += _in_place_quick_sort(a, p + 1, end) - return count - - -def _in_place_partition(a, start, end): - count = 0 - pivot = randint(start, end) - temp = a[end] - a[end] = a[pivot] - a[pivot] = temp - new_pivot_index = start - 1 - for index in range(start, end): - count += 1 - if a[index] < a[end]: # check if current val is less than pivot value - new_pivot_index = new_pivot_index + 1 - temp = a[new_pivot_index] - a[new_pivot_index] = a[index] - a[index] = temp - - temp = a[new_pivot_index + 1] - a[new_pivot_index + 1] = a[end] - a[end] = temp - return new_pivot_index + 1, count - - -outfile = TemporaryFile() -p = 100 # 1000 elements are to be sorted - - -mu, sigma = 0, 1 # mean and standard deviation -X = np.random.normal(mu, sigma, p) -np.save(outfile, X) -print("The array is") -print(X) - - -outfile.seek(0) # using the same array -M = np.load(outfile) -r = len(M) - 1 -z = _in_place_quick_sort(M, 0, r) - -print( - "No of Comparisons for 100 elements selected from a standard normal distribution" - "is :" -) -print(z) From fbbbd5db05987e735ec35fc658136001d3e9e663 Mon Sep 17 00:00:00 2001 From: aryan1165 <111041731+aryan1165@users.noreply.github.com> Date: Sun, 1 Oct 2023 11:04:03 +0530 Subject: [PATCH 218/757] Deleted add.py. As stated in #6216 (#9180) --- maths/add.py | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 maths/add.py diff --git a/maths/add.py b/maths/add.py deleted file mode 100644 index c89252c645ea..000000000000 --- a/maths/add.py +++ /dev/null @@ -1,19 +0,0 @@ -""" -Just to check -""" - - -def add(a: float, b: float) -> float: - """ - >>> add(2, 2) - 4 - >>> add(2, -2) - 0 - """ - return a + b - - -if __name__ == "__main__": - a = 5 - b = 6 - print(f"The sum of {a} + {b} is {add(a, b)}") From eaa87bd791cdc18d210d775f3258767751f9d3fe Mon Sep 17 00:00:00 2001 From: aryan1165 <111041731+aryan1165@users.noreply.github.com> Date: Sun, 1 Oct 2023 14:13:48 +0530 Subject: [PATCH 219/757] Made binary tree memory-friendly using generators based travels. Fixes (#9208) #8725 --- .../binary_tree/binary_tree_traversals.py | 56 +++++++++++-------- 1 file changed, 34 insertions(+), 22 deletions(-) diff --git a/data_structures/binary_tree/binary_tree_traversals.py b/data_structures/binary_tree/binary_tree_traversals.py index 2afb7604f9c6..5dbbbe623906 100644 --- a/data_structures/binary_tree/binary_tree_traversals.py +++ b/data_structures/binary_tree/binary_tree_traversals.py @@ -1,12 +1,12 @@ -# https://en.wikipedia.org/wiki/Tree_traversal from __future__ import annotations from collections import deque -from collections.abc import Sequence +from collections.abc import Generator, Sequence from dataclasses import dataclass from typing import Any +# https://en.wikipedia.org/wiki/Tree_traversal @dataclass class Node: data: int @@ -31,44 +31,56 @@ def make_tree() -> Node | None: return tree -def preorder(root: Node | None) -> list[int]: +def preorder(root: Node | None) -> Generator[int, None, None]: """ Pre-order traversal visits root node, left subtree, right subtree. - >>> preorder(make_tree()) + >>> list(preorder(make_tree())) [1, 2, 4, 5, 3] """ - return [root.data, *preorder(root.left), *preorder(root.right)] if root else [] + if not root: + return + yield root.data + yield from preorder(root.left) + yield from preorder(root.right) -def postorder(root: Node | None) -> list[int]: +def postorder(root: Node | None) -> Generator[int, None, None]: """ Post-order traversal visits left subtree, right subtree, root node. - >>> postorder(make_tree()) + >>> list(postorder(make_tree())) [4, 5, 2, 3, 1] """ - return postorder(root.left) + postorder(root.right) + [root.data] if root else [] + if not root: + return + yield from postorder(root.left) + yield from postorder(root.right) + yield root.data -def inorder(root: Node | None) -> list[int]: +def inorder(root: Node | None) -> Generator[int, None, None]: """ In-order traversal visits left subtree, root node, right subtree. - >>> inorder(make_tree()) + >>> list(inorder(make_tree())) [4, 2, 5, 1, 3] """ - return [*inorder(root.left), root.data, *inorder(root.right)] if root else [] + if not root: + return + yield from inorder(root.left) + yield root.data + yield from inorder(root.right) -def reverse_inorder(root: Node | None) -> list[int]: +def reverse_inorder(root: Node | None) -> Generator[int, None, None]: """ Reverse in-order traversal visits right subtree, root node, left subtree. - >>> reverse_inorder(make_tree()) + >>> list(reverse_inorder(make_tree())) [3, 1, 5, 2, 4] """ - return ( - [*reverse_inorder(root.right), root.data, *reverse_inorder(root.left)] - if root - else [] - ) + if not root: + return + yield from reverse_inorder(root.right) + yield root.data + yield from reverse_inorder(root.left) def height(root: Node | None) -> int: @@ -178,10 +190,10 @@ def main() -> None: # Main function for testing. root = make_tree() # All Traversals of the binary are as follows: - print(f"In-order Traversal: {inorder(root)}") - print(f"Reverse In-order Traversal: {reverse_inorder(root)}") - print(f"Pre-order Traversal: {preorder(root)}") - print(f"Post-order Traversal: {postorder(root)}", "\n") + print(f"In-order Traversal: {list(inorder(root))}") + print(f"Reverse In-order Traversal: {list(reverse_inorder(root))}") + print(f"Pre-order Traversal: {list(preorder(root))}") + print(f"Post-order Traversal: {list(postorder(root))}", "\n") print(f"Height of Tree: {height(root)}", "\n") From cfabd91a8ba83bbe23d2790494e2450118044fcc Mon Sep 17 00:00:00 2001 From: Shreya Bhalgat <85868386+shreyabhalgat@users.noreply.github.com> Date: Sun, 1 Oct 2023 16:58:20 +0530 Subject: [PATCH 220/757] Add missing number algorithm (#9203) * Added missing_number algorithm using bit manipulation * Update bit_manipulation/missing_number.py --------- Co-authored-by: Christian Clauss --- bit_manipulation/missing_number.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 bit_manipulation/missing_number.py diff --git a/bit_manipulation/missing_number.py b/bit_manipulation/missing_number.py new file mode 100644 index 000000000000..92502a778ace --- /dev/null +++ b/bit_manipulation/missing_number.py @@ -0,0 +1,21 @@ +def find_missing_number(nums: list[int]) -> int: + """ + Finds the missing number in a list of consecutive integers. + + Args: + nums: A list of integers. + + Returns: + The missing number. + + Example: + >>> find_missing_number([0, 1, 3, 4]) + 2 + """ + n = len(nums) + missing_number = n + + for i in range(n): + missing_number ^= i ^ nums[i] + + return missing_number From 596d93423862da8c8e419e9b74c1321b7d26b7a1 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Sun, 1 Oct 2023 13:58:30 +0200 Subject: [PATCH 221/757] Fix ruff warning (#9272) --- .github/workflows/ruff.yml | 2 +- DIRECTORY.md | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/ruff.yml b/.github/workflows/ruff.yml index ca2d5be47327..e71ac8a4e933 100644 --- a/.github/workflows/ruff.yml +++ b/.github/workflows/ruff.yml @@ -13,4 +13,4 @@ jobs: steps: - uses: actions/checkout@v3 - run: pip install --user ruff - - run: ruff --format=github . + - run: ruff --output-format=github . diff --git a/DIRECTORY.md b/DIRECTORY.md index 001da2c15b99..4ae1c69f7099 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -530,7 +530,6 @@ ## Maths * [Abs](maths/abs.py) - * [Add](maths/add.py) * [Addition Without Arithmetic](maths/addition_without_arithmetic.py) * [Aliquot Sum](maths/aliquot_sum.py) * [Allocation Number](maths/allocation_number.py) @@ -1141,8 +1140,6 @@ * [Quick Sort](sorts/quick_sort.py) * [Quick Sort 3 Partition](sorts/quick_sort_3_partition.py) * [Radix Sort](sorts/radix_sort.py) - * [Random Normal Distribution Quicksort](sorts/random_normal_distribution_quicksort.py) - * [Random Pivot Quick Sort](sorts/random_pivot_quick_sort.py) * [Recursive Bubble Sort](sorts/recursive_bubble_sort.py) * [Recursive Insertion Sort](sorts/recursive_insertion_sort.py) * [Recursive Mergesort Array](sorts/recursive_mergesort_array.py) From 43c3f4ea4070bfbe1f41f4b861c7ff3f89953715 Mon Sep 17 00:00:00 2001 From: Bama Charan Chhandogi Date: Sun, 1 Oct 2023 20:16:12 +0530 Subject: [PATCH 222/757] add Three sum (#9177) * add Three sum * add Three sum * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * update * update * update * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * update * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * add documention --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- maths/three_sum.py | 47 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 maths/three_sum.py diff --git a/maths/three_sum.py b/maths/three_sum.py new file mode 100644 index 000000000000..09956f8415a0 --- /dev/null +++ b/maths/three_sum.py @@ -0,0 +1,47 @@ +""" +https://en.wikipedia.org/wiki/3SUM +""" + + +def three_sum(nums: list[int]) -> list[list[int]]: + """ + Find all unique triplets in a sorted array of integers that sum up to zero. + + Args: + nums: A sorted list of integers. + + Returns: + A list of lists containing unique triplets that sum up to zero. + + >>> three_sum([-1, 0, 1, 2, -1, -4]) + [[-1, -1, 2], [-1, 0, 1]] + >>> three_sum([1, 2, 3, 4]) + [] + """ + nums.sort() + ans = [] + for i in range(len(nums) - 2): + if i == 0 or (nums[i] != nums[i - 1]): + low, high, c = i + 1, len(nums) - 1, 0 - nums[i] + while low < high: + if nums[low] + nums[high] == c: + ans.append([nums[i], nums[low], nums[high]]) + + while low < high and nums[low] == nums[low + 1]: + low += 1 + while low < high and nums[high] == nums[high - 1]: + high -= 1 + + low += 1 + high -= 1 + elif nums[low] + nums[high] < c: + low += 1 + else: + high -= 1 + return ans + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From bacad12a1f64d92a793ccc2ec88535c9a4092fb6 Mon Sep 17 00:00:00 2001 From: Muhammad Umer Farooq <115654418+Muhammadummerr@users.noreply.github.com> Date: Sun, 1 Oct 2023 21:11:16 +0500 Subject: [PATCH 223/757] [NEW ALGORITHM] Rotate linked list by K. (#9278) * Rotate linked list by k. * Rotate linked list by k. * updated variable name. * Update data_structures/linked_list/rotate_linked_list_by_k.py Co-authored-by: Christian Clauss * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update data_structures/linked_list/rotate_linked_list_by_k.py Co-authored-by: Christian Clauss * Update data_structures/linked_list/rotate_linked_list_by_k.py * Make Node a dataclass --------- Co-authored-by: Christian Clauss Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .../linked_list/rotate_to_the_right.py | 156 ++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 data_structures/linked_list/rotate_to_the_right.py diff --git a/data_structures/linked_list/rotate_to_the_right.py b/data_structures/linked_list/rotate_to_the_right.py new file mode 100644 index 000000000000..51b10481c0ce --- /dev/null +++ b/data_structures/linked_list/rotate_to_the_right.py @@ -0,0 +1,156 @@ +from __future__ import annotations + +from dataclasses import dataclass + + +@dataclass +class Node: + data: int + next_node: Node | None = None + + +def print_linked_list(head: Node | None) -> None: + """ + Print the entire linked list iteratively. + + This function prints the elements of a linked list separated by '->'. + + Parameters: + head (Node | None): The head of the linked list to be printed, + or None if the linked list is empty. + + >>> head = insert_node(None, 0) + >>> head = insert_node(head, 2) + >>> head = insert_node(head, 1) + >>> print_linked_list(head) + 0->2->1 + >>> head = insert_node(head, 4) + >>> head = insert_node(head, 5) + >>> print_linked_list(head) + 0->2->1->4->5 + """ + if head is None: + return + while head.next_node is not None: + print(head.data, end="->") + head = head.next_node + print(head.data) + + +def insert_node(head: Node | None, data: int) -> Node: + """ + Insert a new node at the end of a linked list and return the new head. + + Parameters: + head (Node | None): The head of the linked list. + data (int): The data to be inserted into the new node. + + Returns: + Node: The new head of the linked list. + + >>> head = insert_node(None, 10) + >>> head = insert_node(head, 9) + >>> head = insert_node(head, 8) + >>> print_linked_list(head) + 10->9->8 + """ + new_node = Node(data) + # If the linked list is empty, the new_node becomes the head + if head is None: + return new_node + + temp_node = head + while temp_node.next_node: + temp_node = temp_node.next_node + + temp_node.next_node = new_node # type: ignore + return head + + +def rotate_to_the_right(head: Node, places: int) -> Node: + """ + Rotate a linked list to the right by places times. + + Parameters: + head: The head of the linked list. + places: The number of places to rotate. + + Returns: + Node: The head of the rotated linked list. + + >>> rotate_to_the_right(None, places=1) + Traceback (most recent call last): + ... + ValueError: The linked list is empty. + >>> head = insert_node(None, 1) + >>> rotate_to_the_right(head, places=1) == head + True + >>> head = insert_node(None, 1) + >>> head = insert_node(head, 2) + >>> head = insert_node(head, 3) + >>> head = insert_node(head, 4) + >>> head = insert_node(head, 5) + >>> new_head = rotate_to_the_right(head, places=2) + >>> print_linked_list(new_head) + 4->5->1->2->3 + """ + # Check if the list is empty or has only one element + if not head: + raise ValueError("The linked list is empty.") + + if head.next_node is None: + return head + + # Calculate the length of the linked list + length = 1 + temp_node = head + while temp_node.next_node is not None: + length += 1 + temp_node = temp_node.next_node + + # Adjust the value of places to avoid places longer than the list. + places %= length + + if places == 0: + return head # As no rotation is needed. + + # Find the new head position after rotation. + new_head_index = length - places + + # Traverse to the new head position + temp_node = head + for _ in range(new_head_index - 1): + assert temp_node.next_node + temp_node = temp_node.next_node + + # Update pointers to perform rotation + assert temp_node.next_node + new_head = temp_node.next_node + temp_node.next_node = None + temp_node = new_head + while temp_node.next_node: + temp_node = temp_node.next_node + temp_node.next_node = head + + assert new_head + return new_head + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + head = insert_node(None, 5) + head = insert_node(head, 1) + head = insert_node(head, 2) + head = insert_node(head, 4) + head = insert_node(head, 3) + + print("Original list: ", end="") + print_linked_list(head) + + places = 3 + new_head = rotate_to_the_right(head, places) + + print(f"After {places} iterations: ", end="") + print_linked_list(new_head) From 18cdbc416504391bc9246f1874bd752ea730c710 Mon Sep 17 00:00:00 2001 From: aryan1165 <111041731+aryan1165@users.noreply.github.com> Date: Sun, 1 Oct 2023 22:24:05 +0530 Subject: [PATCH 224/757] binary_search_traversals.py made memory-friendly using generators. Fixes #8725 completely. (#9237) * Made binary tree memory-friendly using generators based travels. Fixes #8725 * Made binary tree memory-friendly using generators based travels. Fixes #8725 * Fixed pre-commit errors --- .../binary_tree/binary_tree_traversals.py | 57 ++++++++----------- 1 file changed, 23 insertions(+), 34 deletions(-) diff --git a/data_structures/binary_tree/binary_tree_traversals.py b/data_structures/binary_tree/binary_tree_traversals.py index 5dbbbe623906..2b33cdca4fed 100644 --- a/data_structures/binary_tree/binary_tree_traversals.py +++ b/data_structures/binary_tree/binary_tree_traversals.py @@ -1,9 +1,8 @@ from __future__ import annotations from collections import deque -from collections.abc import Generator, Sequence +from collections.abc import Generator from dataclasses import dataclass -from typing import Any # https://en.wikipedia.org/wiki/Tree_traversal @@ -94,96 +93,86 @@ def height(root: Node | None) -> int: return (max(height(root.left), height(root.right)) + 1) if root else 0 -def level_order(root: Node | None) -> Sequence[Node | None]: +def level_order(root: Node | None) -> Generator[int, None, None]: """ Returns a list of nodes value from a whole binary tree in Level Order Traverse. Level Order traverse: Visit nodes of the tree level-by-level. """ - output: list[Any] = [] if root is None: - return output + return process_queue = deque([root]) while process_queue: node = process_queue.popleft() - output.append(node.data) + yield node.data if node.left: process_queue.append(node.left) if node.right: process_queue.append(node.right) - return output def get_nodes_from_left_to_right( root: Node | None, level: int -) -> Sequence[Node | None]: +) -> Generator[int, None, None]: """ Returns a list of nodes value from a particular level: Left to right direction of the binary tree. """ - output: list[Any] = [] - def populate_output(root: Node | None, level: int) -> None: + def populate_output(root: Node | None, level: int) -> Generator[int, None, None]: if not root: return if level == 1: - output.append(root.data) + yield root.data elif level > 1: - populate_output(root.left, level - 1) - populate_output(root.right, level - 1) + yield from populate_output(root.left, level - 1) + yield from populate_output(root.right, level - 1) - populate_output(root, level) - return output + yield from populate_output(root, level) def get_nodes_from_right_to_left( root: Node | None, level: int -) -> Sequence[Node | None]: +) -> Generator[int, None, None]: """ Returns a list of nodes value from a particular level: Right to left direction of the binary tree. """ - output: list[Any] = [] - def populate_output(root: Node | None, level: int) -> None: + def populate_output(root: Node | None, level: int) -> Generator[int, None, None]: if root is None: return if level == 1: - output.append(root.data) + yield root.data elif level > 1: - populate_output(root.right, level - 1) - populate_output(root.left, level - 1) + yield from populate_output(root.right, level - 1) + yield from populate_output(root.left, level - 1) - populate_output(root, level) - return output + yield from populate_output(root, level) -def zigzag(root: Node | None) -> Sequence[Node | None] | list[Any]: +def zigzag(root: Node | None) -> Generator[int, None, None]: """ ZigZag traverse: Returns a list of nodes value from left to right and right to left, alternatively. """ if root is None: - return [] - - output: list[Sequence[Node | None]] = [] + return flag = 0 height_tree = height(root) for h in range(1, height_tree + 1): if not flag: - output.append(get_nodes_from_left_to_right(root, h)) + yield from get_nodes_from_left_to_right(root, h) flag = 1 else: - output.append(get_nodes_from_right_to_left(root, h)) + yield from get_nodes_from_right_to_left(root, h) flag = 0 - return output - def main() -> None: # Main function for testing. # Create binary tree. @@ -198,15 +187,15 @@ def main() -> None: # Main function for testing. print(f"Height of Tree: {height(root)}", "\n") print("Complete Level Order Traversal: ") - print(level_order(root), "\n") + print(f"{list(level_order(root))} \n") print("Level-wise order Traversal: ") for level in range(1, height(root) + 1): - print(f"Level {level}:", get_nodes_from_left_to_right(root, level=level)) + print(f"Level {level}:", list(get_nodes_from_left_to_right(root, level=level))) print("\nZigZag order Traversal: ") - print(zigzag(root)) + print(f"{list(zigzag(root))}") if __name__ == "__main__": From 8d94f7745f81c8f7c33bdd3d0c0740861b9c98e7 Mon Sep 17 00:00:00 2001 From: Kamil <32775019+quant12345@users.noreply.github.com> Date: Sun, 1 Oct 2023 23:14:58 +0500 Subject: [PATCH 225/757] Euler072 - application of vector operations to reduce calculation time and refactoring numpy (#9229) * Replacing the generator with numpy vector operations from lu_decomposition. * Revert "Replacing the generator with numpy vector operations from lu_decomposition." This reverts commit ad217c66165898d62b76cc89ba09c2d7049b6448. * Application of vector operations to reduce calculation time and refactoring numpy. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- project_euler/problem_072/sol1.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/project_euler/problem_072/sol1.py b/project_euler/problem_072/sol1.py index a2a0eeeb31c5..5a28be564556 100644 --- a/project_euler/problem_072/sol1.py +++ b/project_euler/problem_072/sol1.py @@ -21,6 +21,8 @@ Time: 1 sec """ +import numpy as np + def solution(limit: int = 1_000_000) -> int: """ @@ -33,14 +35,15 @@ def solution(limit: int = 1_000_000) -> int: 304191 """ - phi = [i - 1 for i in range(limit + 1)] + # generating an array from -1 to limit + phi = np.arange(-1, limit) for i in range(2, limit + 1): if phi[i] == i - 1: - for j in range(2 * i, limit + 1, i): - phi[j] -= phi[j] // i + ind = np.arange(2 * i, limit + 1, i) # indexes for selection + phi[ind] -= phi[ind] // i - return sum(phi[2 : limit + 1]) + return np.sum(phi[2 : limit + 1]) if __name__ == "__main__": From 24e7edbe5bc771023335544a7a9cf7895140c1fe Mon Sep 17 00:00:00 2001 From: Dhruv Manilawala Date: Mon, 2 Oct 2023 02:48:16 +0530 Subject: [PATCH 226/757] Remove myself from CODEOWNERS (#9325) --- .github/CODEOWNERS | 2 +- DIRECTORY.md | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index abf99ab227be..05cd709a8f62 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -69,7 +69,7 @@ # /other/ @cclauss # TODO: Uncomment this line after Hacktoberfest -/project_euler/ @dhruvmanila +# /project_euler/ # /quantum/ diff --git a/DIRECTORY.md b/DIRECTORY.md index 4ae1c69f7099..7d3ceee144be 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -51,6 +51,7 @@ * [Index Of Rightmost Set Bit](bit_manipulation/index_of_rightmost_set_bit.py) * [Is Even](bit_manipulation/is_even.py) * [Is Power Of Two](bit_manipulation/is_power_of_two.py) + * [Missing Number](bit_manipulation/missing_number.py) * [Numbers Different Signs](bit_manipulation/numbers_different_signs.py) * [Reverse Bits](bit_manipulation/reverse_bits.py) * [Single Bit Manipulation Operations](bit_manipulation/single_bit_manipulation_operations.py) @@ -232,6 +233,7 @@ * [Merge Two Lists](data_structures/linked_list/merge_two_lists.py) * [Middle Element Of Linked List](data_structures/linked_list/middle_element_of_linked_list.py) * [Print Reverse](data_structures/linked_list/print_reverse.py) + * [Rotate To The Right](data_structures/linked_list/rotate_to_the_right.py) * [Singly Linked List](data_structures/linked_list/singly_linked_list.py) * [Skip List](data_structures/linked_list/skip_list.py) * [Swap Nodes](data_structures/linked_list/swap_nodes.py) @@ -676,6 +678,7 @@ * [Sylvester Sequence](maths/sylvester_sequence.py) * [Tanh](maths/tanh.py) * [Test Prime Check](maths/test_prime_check.py) + * [Three Sum](maths/three_sum.py) * [Trapezoidal Rule](maths/trapezoidal_rule.py) * [Triplet Sum](maths/triplet_sum.py) * [Twin Prime](maths/twin_prime.py) From e798e5acdee69416d61c8ab65cea4da8a5c16355 Mon Sep 17 00:00:00 2001 From: Bama Charan Chhandogi Date: Mon, 2 Oct 2023 05:49:39 +0530 Subject: [PATCH 227/757] add reverse k group linkedlist (#9323) * add reverse k group linkedlist * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * update * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * update * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * update * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * update * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * update * Update reverse_k_group.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update reverse_k_group.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update reverse_k_group.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- .../linked_list/reverse_k_group.py | 118 ++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 data_structures/linked_list/reverse_k_group.py diff --git a/data_structures/linked_list/reverse_k_group.py b/data_structures/linked_list/reverse_k_group.py new file mode 100644 index 000000000000..5fc45491a540 --- /dev/null +++ b/data_structures/linked_list/reverse_k_group.py @@ -0,0 +1,118 @@ +from __future__ import annotations + +from collections.abc import Iterable, Iterator +from dataclasses import dataclass + + +@dataclass +class Node: + data: int + next_node: Node | None = None + + +class LinkedList: + def __init__(self, ints: Iterable[int]) -> None: + self.head: Node | None = None + for i in ints: + self.append(i) + + def __iter__(self) -> Iterator[int]: + """ + >>> ints = [] + >>> list(LinkedList(ints)) == ints + True + >>> ints = tuple(range(5)) + >>> tuple(LinkedList(ints)) == ints + True + """ + node = self.head + while node: + yield node.data + node = node.next_node + + def __len__(self) -> int: + """ + >>> for i in range(3): + ... len(LinkedList(range(i))) == i + True + True + True + >>> len(LinkedList("abcdefgh")) + 8 + """ + return sum(1 for _ in self) + + def __str__(self) -> str: + """ + >>> str(LinkedList([])) + '' + >>> str(LinkedList(range(5))) + '0 -> 1 -> 2 -> 3 -> 4' + """ + return " -> ".join([str(node) for node in self]) + + def append(self, data: int) -> None: + """ + >>> ll = LinkedList([1, 2]) + >>> tuple(ll) + (1, 2) + >>> ll.append(3) + >>> tuple(ll) + (1, 2, 3) + >>> ll.append(4) + >>> tuple(ll) + (1, 2, 3, 4) + >>> len(ll) + 4 + """ + if not self.head: + self.head = Node(data) + return + node = self.head + while node.next_node: + node = node.next_node + node.next_node = Node(data) + + def reverse_k_nodes(self, group_size: int) -> None: + """ + reverse nodes within groups of size k + >>> ll = LinkedList([1, 2, 3, 4, 5]) + >>> ll.reverse_k_nodes(2) + >>> tuple(ll) + (2, 1, 4, 3, 5) + >>> str(ll) + '2 -> 1 -> 4 -> 3 -> 5' + """ + if self.head is None or self.head.next_node is None: + return + + length = len(self) + dummy_head = Node(0) + dummy_head.next_node = self.head + previous_node = dummy_head + + while length >= group_size: + current_node = previous_node.next_node + assert current_node + next_node = current_node.next_node + for _ in range(1, group_size): + assert next_node, current_node + current_node.next_node = next_node.next_node + assert previous_node + next_node.next_node = previous_node.next_node + previous_node.next_node = next_node + next_node = current_node.next_node + previous_node = current_node + length -= group_size + self.head = dummy_head.next_node + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + ll = LinkedList([1, 2, 3, 4, 5]) + print(f"Original Linked List: {ll}") + k = 2 + ll.reverse_k_nodes(k) + print(f"After reversing groups of size {k}: {ll}") From 9640a4041a7b331e506daab1b31dd30fb47b228d Mon Sep 17 00:00:00 2001 From: Saksham Chawla <51916697+saksham-chawla@users.noreply.github.com> Date: Mon, 2 Oct 2023 19:58:36 +0530 Subject: [PATCH 228/757] Add typing to binary_exponentiation_2.py (#9475) --- maths/binary_exponentiation_2.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/maths/binary_exponentiation_2.py b/maths/binary_exponentiation_2.py index 51ec4baf2598..af8f776dd266 100644 --- a/maths/binary_exponentiation_2.py +++ b/maths/binary_exponentiation_2.py @@ -11,7 +11,7 @@ """ -def b_expo(a, b): +def b_expo(a: int, b: int) -> int: res = 0 while b > 0: if b & 1: @@ -23,7 +23,7 @@ def b_expo(a, b): return res -def b_expo_mod(a, b, c): +def b_expo_mod(a: int, b: int, c: int) -> int: res = 0 while b > 0: if b & 1: From 89a65a861724d2eb8c6a60a9e1655d7af9cdc836 Mon Sep 17 00:00:00 2001 From: Saksham Chawla <51916697+saksham-chawla@users.noreply.github.com> Date: Mon, 2 Oct 2023 19:59:06 +0530 Subject: [PATCH 229/757] Add typing to binary_exponentiation.py (#9471) * Add typing to binary_exponentiation.py * Update binary_exponentiation.py * float to int division change as per review --- maths/binary_exponentiation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/maths/binary_exponentiation.py b/maths/binary_exponentiation.py index 147b4285ffa1..05de939d1bde 100644 --- a/maths/binary_exponentiation.py +++ b/maths/binary_exponentiation.py @@ -4,7 +4,7 @@ # Time Complexity : O(logn) -def binary_exponentiation(a, n): +def binary_exponentiation(a: int, n: int) -> int: if n == 0: return 1 @@ -12,7 +12,7 @@ def binary_exponentiation(a, n): return binary_exponentiation(a, n - 1) * a else: - b = binary_exponentiation(a, n / 2) + b = binary_exponentiation(a, n // 2) return b * b From 97154cfa351e35ddf0727691a92998cfd7be4e5b Mon Sep 17 00:00:00 2001 From: Saksham Chawla <51916697+saksham-chawla@users.noreply.github.com> Date: Mon, 2 Oct 2023 20:00:34 +0530 Subject: [PATCH 230/757] Add typing to binary_exp_mod.py (#9469) * Add typing to binary_exp_mod.py * Update binary_exp_mod.py * review changes --- maths/binary_exp_mod.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/maths/binary_exp_mod.py b/maths/binary_exp_mod.py index df688892d690..8893182a3496 100644 --- a/maths/binary_exp_mod.py +++ b/maths/binary_exp_mod.py @@ -1,4 +1,4 @@ -def bin_exp_mod(a, n, b): +def bin_exp_mod(a: int, n: int, b: int) -> int: """ >>> bin_exp_mod(3, 4, 5) 1 @@ -13,7 +13,7 @@ def bin_exp_mod(a, n, b): if n % 2 == 1: return (bin_exp_mod(a, n - 1, b) * a) % b - r = bin_exp_mod(a, n / 2, b) + r = bin_exp_mod(a, n // 2, b) return (r * r) % b From 73118b9f67f49fae14eb9a39e47ec9127ef1f155 Mon Sep 17 00:00:00 2001 From: Saksham Chawla <51916697+saksham-chawla@users.noreply.github.com> Date: Mon, 2 Oct 2023 20:11:34 +0530 Subject: [PATCH 231/757] Add typing to binary_exponentiation_3.py (#9477) --- maths/binary_exponentiation_3.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/maths/binary_exponentiation_3.py b/maths/binary_exponentiation_3.py index dd4e70e74129..9cd143e09207 100644 --- a/maths/binary_exponentiation_3.py +++ b/maths/binary_exponentiation_3.py @@ -11,7 +11,7 @@ """ -def b_expo(a, b): +def b_expo(a: int, b: int) -> int: res = 1 while b > 0: if b & 1: @@ -23,7 +23,7 @@ def b_expo(a, b): return res -def b_expo_mod(a, b, c): +def b_expo_mod(a: int, b: int, c: int) -> int: res = 1 while b > 0: if b & 1: From 95345f6f5b0e6ae10f54a33850298634e05766ee Mon Sep 17 00:00:00 2001 From: Saksham Chawla <51916697+saksham-chawla@users.noreply.github.com> Date: Mon, 2 Oct 2023 20:51:45 +0530 Subject: [PATCH 232/757] Add typng to binomial_coefficient.py (#9480) --- maths/binomial_coefficient.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maths/binomial_coefficient.py b/maths/binomial_coefficient.py index 0d4b3d1a8d9a..6d5b46cb5861 100644 --- a/maths/binomial_coefficient.py +++ b/maths/binomial_coefficient.py @@ -1,4 +1,4 @@ -def binomial_coefficient(n, r): +def binomial_coefficient(n: int, r: int) -> int: """ Find binomial coefficient using pascals triangle. From 8c7bd1c48d1e4029aa115d50fb3034e199bef7f9 Mon Sep 17 00:00:00 2001 From: Varshaa Shetty Date: Tue, 3 Oct 2023 03:17:10 +0530 Subject: [PATCH 233/757] Deleted minmax.py (#9482) --- backtracking/minmax.py | 69 ------------------------------------------ 1 file changed, 69 deletions(-) delete mode 100644 backtracking/minmax.py diff --git a/backtracking/minmax.py b/backtracking/minmax.py deleted file mode 100644 index 9b87183cfdb7..000000000000 --- a/backtracking/minmax.py +++ /dev/null @@ -1,69 +0,0 @@ -""" -Minimax helps to achieve maximum score in a game by checking all possible moves. - -""" -from __future__ import annotations - -import math - - -def minimax( - depth: int, node_index: int, is_max: bool, scores: list[int], height: float -) -> int: - """ - depth is current depth in game tree. - node_index is index of current node in scores[]. - scores[] contains the leaves of game tree. - height is maximum height of game tree. - - >>> 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 - """ - - if depth < 0: - raise ValueError("Depth cannot be less than 0") - - if not scores: - raise ValueError("Scores cannot be empty") - - if depth == height: - return scores[node_index] - - return ( - max( - minimax(depth + 1, node_index * 2, False, scores, height), - minimax(depth + 1, node_index * 2 + 1, False, scores, height), - ) - if is_max - else min( - minimax(depth + 1, node_index * 2, True, scores, height), - minimax(depth + 1, node_index * 2 + 1, True, scores, height), - ) - ) - - -def main() -> None: - scores = [90, 23, 6, 33, 21, 65, 123, 34423] - height = math.log(len(scores), 2) - print(f"Optimal value : {minimax(0, 0, True, scores, height)}") - - -if __name__ == "__main__": - import doctest - - doctest.testmod() - main() From f8fe8fe41f74c8ecc5c8555ca43d65bd12b4f073 Mon Sep 17 00:00:00 2001 From: aryan1165 <111041731+aryan1165@users.noreply.github.com> Date: Tue, 3 Oct 2023 03:27:00 +0530 Subject: [PATCH 234/757] Removed maths/miller_rabin.py , Double implementation. #8098 (#9228) * Removed ciphers/rabin_miller.py as it is already there maths/miller_rabin.py * Renamed miller_rabin.py to rabain_miller.py * Restore ciphers/rabin_miller.py and removed maths/rabin_miller.py --- maths/miller_rabin.py | 51 ------------------------------------------- 1 file changed, 51 deletions(-) delete mode 100644 maths/miller_rabin.py diff --git a/maths/miller_rabin.py b/maths/miller_rabin.py deleted file mode 100644 index 9f2668dbab14..000000000000 --- a/maths/miller_rabin.py +++ /dev/null @@ -1,51 +0,0 @@ -import random - -from .binary_exp_mod import bin_exp_mod - - -# This is a probabilistic check to test primality, useful for big numbers! -# if it's a prime, it will return true -# if it's not a prime, the chance of it returning true is at most 1/4**prec -def is_prime_big(n, prec=1000): - """ - >>> from maths.prime_check import is_prime - >>> # all(is_prime_big(i) == is_prime(i) for i in range(1000)) # 3.45s - >>> all(is_prime_big(i) == is_prime(i) for i in range(256)) - True - """ - if n < 2: - return False - - if n % 2 == 0: - return n == 2 - - # this means n is odd - d = n - 1 - exp = 0 - while d % 2 == 0: - d /= 2 - exp += 1 - - # n - 1=d*(2**exp) - count = 0 - while count < prec: - a = random.randint(2, n - 1) - b = bin_exp_mod(a, d, n) - if b != 1: - flag = True - for _ in range(exp): - if b == n - 1: - flag = False - break - b = b * b - b %= n - if flag: - return False - count += 1 - return True - - -if __name__ == "__main__": - n = abs(int(input("Enter bound : ").strip())) - print("Here's the list of primes:") - print(", ".join(str(i) for i in range(n + 1) if is_prime_big(i))) From f964dcbf2ff7c70e4aca20532a38dfb02ce8a4c0 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Tue, 3 Oct 2023 05:05:43 +0200 Subject: [PATCH 235/757] pre-commit autoupdate && pre-commit run --all-files (#9516) * pre-commit autoupdate && pre-commit run --all-files * updating DIRECTORY.md --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- .pre-commit-config.yaml | 4 ++-- DIRECTORY.md | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 809b841d0ea3..dbf7ff341243 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.0.291 + rev: v0.0.292 hooks: - id: ruff @@ -33,7 +33,7 @@ repos: - tomli - repo: https://github.com/tox-dev/pyproject-fmt - rev: "1.1.0" + rev: "1.2.0" hooks: - id: pyproject-fmt diff --git a/DIRECTORY.md b/DIRECTORY.md index 7d3ceee144be..24c68171c9bc 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -233,6 +233,7 @@ * [Merge Two Lists](data_structures/linked_list/merge_two_lists.py) * [Middle Element Of Linked List](data_structures/linked_list/middle_element_of_linked_list.py) * [Print Reverse](data_structures/linked_list/print_reverse.py) + * [Reverse K Group](data_structures/linked_list/reverse_k_group.py) * [Rotate To The Right](data_structures/linked_list/rotate_to_the_right.py) * [Singly Linked List](data_structures/linked_list/singly_linked_list.py) * [Skip List](data_structures/linked_list/skip_list.py) From 0f4e51245f33175b4fb311f633d3821210741bdd Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Tue, 3 Oct 2023 11:17:10 +0200 Subject: [PATCH 236/757] Upgrade to Python 3.12 (#9576) * DRAFT: GitHub Actions: Test on Python 3.12 Repeats #8777 * #8777 Some of our dependencies will not be ready yet. * Python 3.12: Disable qiskit and tensorflow algorithms * updating DIRECTORY.md --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- .github/workflows/build.yml | 5 +++-- .github/workflows/ruff.yml | 2 +- CONTRIBUTING.md | 2 +- DIRECTORY.md | 19 ------------------- backtracking/combination_sum.py | 2 +- ....py => cnn_classification.py.DISABLED.txt} | 0 ...ans_clustering_tensorflow.py.DISABLED.txt} | 0 ...ns.py => fuzzy_operations.py.DISABLED.txt} | 0 ...ion.py => lstm_prediction.py.DISABLED.txt} | 0 maths/maclaurin_series.py | 8 ++++---- quantum/{bb84.py => bb84.py.DISABLED.txt} | 0 ...jozsa.py => deutsch_jozsa.py.DISABLED.txt} | 1 + ...lf_adder.py => half_adder.py.DISABLED.txt} | 1 + .../{not_gate.py => not_gate.py.DISABLED.txt} | 0 ..._adder.py => q_full_adder.py.DISABLED.txt} | 0 ...y => quantum_entanglement.py.DISABLED.txt} | 0 ... => quantum_teleportation.py.DISABLED.txt} | 0 ...y => ripple_adder_classic.py.DISABLED.txt} | 0 ...y => single_qubit_measure.py.DISABLED.txt} | 0 ...g.py => superdense_coding.py.DISABLED.txt} | 0 requirements.txt | 6 +++--- 21 files changed, 15 insertions(+), 31 deletions(-) rename computer_vision/{cnn_classification.py => cnn_classification.py.DISABLED.txt} (100%) rename dynamic_programming/{k_means_clustering_tensorflow.py => k_means_clustering_tensorflow.py.DISABLED.txt} (100%) rename fuzzy_logic/{fuzzy_operations.py => fuzzy_operations.py.DISABLED.txt} (100%) rename machine_learning/lstm/{lstm_prediction.py => lstm_prediction.py.DISABLED.txt} (100%) rename quantum/{bb84.py => bb84.py.DISABLED.txt} (100%) rename quantum/{deutsch_jozsa.py => deutsch_jozsa.py.DISABLED.txt} (99%) mode change 100755 => 100644 rename quantum/{half_adder.py => half_adder.py.DISABLED.txt} (99%) mode change 100755 => 100644 rename quantum/{not_gate.py => not_gate.py.DISABLED.txt} (100%) rename quantum/{q_full_adder.py => q_full_adder.py.DISABLED.txt} (100%) rename quantum/{quantum_entanglement.py => quantum_entanglement.py.DISABLED.txt} (100%) rename quantum/{quantum_teleportation.py => quantum_teleportation.py.DISABLED.txt} (100%) rename quantum/{ripple_adder_classic.py => ripple_adder_classic.py.DISABLED.txt} (100%) rename quantum/{single_qubit_measure.py => single_qubit_measure.py.DISABLED.txt} (100%) rename quantum/{superdense_coding.py => superdense_coding.py.DISABLED.txt} (100%) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fc8cb636979e..60c1d6d119d0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -9,10 +9,11 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-python@v4 with: - python-version: 3.11 + python-version: 3.12 + allow-prereleases: true - uses: actions/cache@v3 with: path: ~/.cache/pip diff --git a/.github/workflows/ruff.yml b/.github/workflows/ruff.yml index e71ac8a4e933..496f1460e074 100644 --- a/.github/workflows/ruff.yml +++ b/.github/workflows/ruff.yml @@ -11,6 +11,6 @@ jobs: ruff: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: pip install --user ruff - run: ruff --output-format=github . diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4a1bb652738f..7a67ce33cd62 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -73,7 +73,7 @@ pre-commit run --all-files --show-diff-on-failure We want your work to be readable by others; therefore, we encourage you to note the following: -- Please write in Python 3.11+. For instance: `print()` is a function in Python 3 so `print "Hello"` will *not* work but `print("Hello")` will. +- Please write in Python 3.12+. For instance: `print()` is a function in Python 3 so `print "Hello"` will *not* work but `print("Hello")` will. - Please focus hard on the naming of functions, classes, and variables. Help your reader by using __descriptive names__ that can help you to remove redundant comments. - Single letter variable names are *old school* so please avoid them unless their life only spans a few lines. - Expand acronyms because `gcd()` is hard to understand but `greatest_common_divisor()` is not. diff --git a/DIRECTORY.md b/DIRECTORY.md index 24c68171c9bc..9a913aa786e1 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -26,7 +26,6 @@ * [Hamiltonian Cycle](backtracking/hamiltonian_cycle.py) * [Knight Tour](backtracking/knight_tour.py) * [Minimax](backtracking/minimax.py) - * [Minmax](backtracking/minmax.py) * [N Queens](backtracking/n_queens.py) * [N Queens Math](backtracking/n_queens_math.py) * [Power Sum](backtracking/power_sum.py) @@ -133,7 +132,6 @@ * [Run Length Encoding](compression/run_length_encoding.py) ## Computer Vision - * [Cnn Classification](computer_vision/cnn_classification.py) * [Flip Augmentation](computer_vision/flip_augmentation.py) * [Haralick Descriptors](computer_vision/haralick_descriptors.py) * [Harris Corner](computer_vision/harris_corner.py) @@ -321,7 +319,6 @@ * [Floyd Warshall](dynamic_programming/floyd_warshall.py) * [Integer Partition](dynamic_programming/integer_partition.py) * [Iterating Through Submasks](dynamic_programming/iterating_through_submasks.py) - * [K Means Clustering Tensorflow](dynamic_programming/k_means_clustering_tensorflow.py) * [Knapsack](dynamic_programming/knapsack.py) * [Longest Common Subsequence](dynamic_programming/longest_common_subsequence.py) * [Longest Common Substring](dynamic_programming/longest_common_substring.py) @@ -384,9 +381,6 @@ * [Mandelbrot](fractals/mandelbrot.py) * [Sierpinski Triangle](fractals/sierpinski_triangle.py) -## Fuzzy Logic - * [Fuzzy Operations](fuzzy_logic/fuzzy_operations.py) - ## Genetic Algorithm * [Basic String](genetic_algorithm/basic_string.py) @@ -517,8 +511,6 @@ * Local Weighted Learning * [Local Weighted Learning](machine_learning/local_weighted_learning/local_weighted_learning.py) * [Logistic Regression](machine_learning/logistic_regression.py) - * Lstm - * [Lstm Prediction](machine_learning/lstm/lstm_prediction.py) * [Mfcc](machine_learning/mfcc.py) * [Multilayer Perceptron Classifier](machine_learning/multilayer_perceptron_classifier.py) * [Polynomial Regression](machine_learning/polynomial_regression.py) @@ -613,7 +605,6 @@ * [Matrix Exponentiation](maths/matrix_exponentiation.py) * [Max Sum Sliding Window](maths/max_sum_sliding_window.py) * [Median Of Two Arrays](maths/median_of_two_arrays.py) - * [Miller Rabin](maths/miller_rabin.py) * [Mobius Function](maths/mobius_function.py) * [Modular Exponential](maths/modular_exponential.py) * [Monte Carlo](maths/monte_carlo.py) @@ -1071,17 +1062,7 @@ * [Sol1](project_euler/problem_800/sol1.py) ## Quantum - * [Bb84](quantum/bb84.py) - * [Deutsch Jozsa](quantum/deutsch_jozsa.py) - * [Half Adder](quantum/half_adder.py) - * [Not Gate](quantum/not_gate.py) * [Q Fourier Transform](quantum/q_fourier_transform.py) - * [Q Full Adder](quantum/q_full_adder.py) - * [Quantum Entanglement](quantum/quantum_entanglement.py) - * [Quantum Teleportation](quantum/quantum_teleportation.py) - * [Ripple Adder Classic](quantum/ripple_adder_classic.py) - * [Single Qubit Measure](quantum/single_qubit_measure.py) - * [Superdense Coding](quantum/superdense_coding.py) ## Scheduling * [First Come First Served](scheduling/first_come_first_served.py) diff --git a/backtracking/combination_sum.py b/backtracking/combination_sum.py index f555adb751d0..3c6ed81f44f0 100644 --- a/backtracking/combination_sum.py +++ b/backtracking/combination_sum.py @@ -47,7 +47,7 @@ def combination_sum(candidates: list, target: int) -> list: >>> combination_sum([-8, 2.3, 0], 1) Traceback (most recent call last): ... - RecursionError: maximum recursion depth exceeded in comparison + RecursionError: maximum recursion depth exceeded """ path = [] # type: list[int] answer = [] # type: list[int] diff --git a/computer_vision/cnn_classification.py b/computer_vision/cnn_classification.py.DISABLED.txt similarity index 100% rename from computer_vision/cnn_classification.py rename to computer_vision/cnn_classification.py.DISABLED.txt diff --git a/dynamic_programming/k_means_clustering_tensorflow.py b/dynamic_programming/k_means_clustering_tensorflow.py.DISABLED.txt similarity index 100% rename from dynamic_programming/k_means_clustering_tensorflow.py rename to dynamic_programming/k_means_clustering_tensorflow.py.DISABLED.txt diff --git a/fuzzy_logic/fuzzy_operations.py b/fuzzy_logic/fuzzy_operations.py.DISABLED.txt similarity index 100% rename from fuzzy_logic/fuzzy_operations.py rename to fuzzy_logic/fuzzy_operations.py.DISABLED.txt diff --git a/machine_learning/lstm/lstm_prediction.py b/machine_learning/lstm/lstm_prediction.py.DISABLED.txt similarity index 100% rename from machine_learning/lstm/lstm_prediction.py rename to machine_learning/lstm/lstm_prediction.py.DISABLED.txt diff --git a/maths/maclaurin_series.py b/maths/maclaurin_series.py index e55839bc15ba..806e5f9b0788 100644 --- a/maths/maclaurin_series.py +++ b/maths/maclaurin_series.py @@ -17,9 +17,9 @@ def maclaurin_sin(theta: float, accuracy: int = 30) -> float: >>> all(isclose(maclaurin_sin(x, 50), sin(x)) for x in range(-25, 25)) True >>> maclaurin_sin(10) - -0.544021110889369 + -0.5440211108893691 >>> maclaurin_sin(-10) - 0.5440211108893703 + 0.5440211108893704 >>> maclaurin_sin(10, 15) -0.5440211108893689 >>> maclaurin_sin(-10, 15) @@ -69,9 +69,9 @@ def maclaurin_cos(theta: float, accuracy: int = 30) -> float: >>> all(isclose(maclaurin_cos(x, 50), cos(x)) for x in range(-25, 25)) True >>> maclaurin_cos(5) - 0.28366218546322675 + 0.2836621854632268 >>> maclaurin_cos(-5) - 0.2836621854632266 + 0.2836621854632265 >>> maclaurin_cos(10, 15) -0.8390715290764525 >>> maclaurin_cos(-10, 15) diff --git a/quantum/bb84.py b/quantum/bb84.py.DISABLED.txt similarity index 100% rename from quantum/bb84.py rename to quantum/bb84.py.DISABLED.txt diff --git a/quantum/deutsch_jozsa.py b/quantum/deutsch_jozsa.py.DISABLED.txt old mode 100755 new mode 100644 similarity index 99% rename from quantum/deutsch_jozsa.py rename to quantum/deutsch_jozsa.py.DISABLED.txt index 95c3e65b5edf..5c8a379debfc --- a/quantum/deutsch_jozsa.py +++ b/quantum/deutsch_jozsa.py.DISABLED.txt @@ -1,3 +1,4 @@ +# DISABLED!! #!/usr/bin/env python3 """ Deutsch-Jozsa Algorithm is one of the first examples of a quantum diff --git a/quantum/half_adder.py b/quantum/half_adder.py.DISABLED.txt old mode 100755 new mode 100644 similarity index 99% rename from quantum/half_adder.py rename to quantum/half_adder.py.DISABLED.txt index 21a57ddcf2dd..800d563ec76f --- a/quantum/half_adder.py +++ b/quantum/half_adder.py.DISABLED.txt @@ -1,3 +1,4 @@ +# DISABLED!! #!/usr/bin/env python3 """ Build a half-adder quantum circuit that takes two bits as input, diff --git a/quantum/not_gate.py b/quantum/not_gate.py.DISABLED.txt similarity index 100% rename from quantum/not_gate.py rename to quantum/not_gate.py.DISABLED.txt diff --git a/quantum/q_full_adder.py b/quantum/q_full_adder.py.DISABLED.txt similarity index 100% rename from quantum/q_full_adder.py rename to quantum/q_full_adder.py.DISABLED.txt diff --git a/quantum/quantum_entanglement.py b/quantum/quantum_entanglement.py.DISABLED.txt similarity index 100% rename from quantum/quantum_entanglement.py rename to quantum/quantum_entanglement.py.DISABLED.txt diff --git a/quantum/quantum_teleportation.py b/quantum/quantum_teleportation.py.DISABLED.txt similarity index 100% rename from quantum/quantum_teleportation.py rename to quantum/quantum_teleportation.py.DISABLED.txt diff --git a/quantum/ripple_adder_classic.py b/quantum/ripple_adder_classic.py.DISABLED.txt similarity index 100% rename from quantum/ripple_adder_classic.py rename to quantum/ripple_adder_classic.py.DISABLED.txt diff --git a/quantum/single_qubit_measure.py b/quantum/single_qubit_measure.py.DISABLED.txt similarity index 100% rename from quantum/single_qubit_measure.py rename to quantum/single_qubit_measure.py.DISABLED.txt diff --git a/quantum/superdense_coding.py b/quantum/superdense_coding.py.DISABLED.txt similarity index 100% rename from quantum/superdense_coding.py rename to quantum/superdense_coding.py.DISABLED.txt diff --git a/requirements.txt b/requirements.txt index 1128e9d66820..25dba6f5a250 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,15 +9,15 @@ opencv-python pandas pillow projectq -qiskit -qiskit-aer +qiskit ; python_version < '3.12' +qiskit-aer ; python_version < '3.12' requests rich scikit-fuzzy scikit-learn statsmodels sympy -tensorflow +tensorflow ; python_version < '3.12' texttable tweepy xgboost From da03c14d39ec8c6a3c253951541b902172bb92fc Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Tue, 3 Oct 2023 11:48:58 +0200 Subject: [PATCH 237/757] Fix accuracy in maclaurin_series on Python 3.12 (#9581) --- maths/maclaurin_series.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/maths/maclaurin_series.py b/maths/maclaurin_series.py index 806e5f9b0788..d5c3c3ab958b 100644 --- a/maths/maclaurin_series.py +++ b/maths/maclaurin_series.py @@ -21,9 +21,9 @@ def maclaurin_sin(theta: float, accuracy: int = 30) -> float: >>> maclaurin_sin(-10) 0.5440211108893704 >>> maclaurin_sin(10, 15) - -0.5440211108893689 + -0.544021110889369 >>> maclaurin_sin(-10, 15) - 0.5440211108893703 + 0.5440211108893704 >>> maclaurin_sin("10") Traceback (most recent call last): ... @@ -73,7 +73,7 @@ def maclaurin_cos(theta: float, accuracy: int = 30) -> float: >>> maclaurin_cos(-5) 0.2836621854632265 >>> maclaurin_cos(10, 15) - -0.8390715290764525 + -0.8390715290764524 >>> maclaurin_cos(-10, 15) -0.8390715290764521 >>> maclaurin_cos("10") From e60779c202880275e786f0f857f4261b90a41d51 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Tue, 3 Oct 2023 12:04:59 +0200 Subject: [PATCH 238/757] Upgrade our Devcontainer to Python 3.12 on Debian bookworm (#9580) --- .devcontainer/Dockerfile | 2 +- .devcontainer/README.md | 1 + .devcontainer/devcontainer.json | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 .devcontainer/README.md diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index b5a5347c66b0..6aa0073bf95b 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,5 +1,5 @@ # https://github.com/microsoft/vscode-dev-containers/blob/main/containers/python-3/README.md -ARG VARIANT=3.11-bookworm +ARG VARIANT=3.12-bookworm FROM mcr.microsoft.com/vscode/devcontainers/python:${VARIANT} COPY requirements.txt /tmp/pip-tmp/ RUN python3 -m pip install --upgrade pip \ diff --git a/.devcontainer/README.md b/.devcontainer/README.md new file mode 100644 index 000000000000..ec3cdb61de7a --- /dev/null +++ b/.devcontainer/README.md @@ -0,0 +1 @@ +https://code.visualstudio.com/docs/devcontainers/tutorial diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index c5a855b2550c..ae1d4fb7494d 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -4,10 +4,10 @@ "dockerfile": "Dockerfile", "context": "..", "args": { - // Update 'VARIANT' to pick a Python version: 3, 3.10, 3.9, 3.8, 3.7, 3.6 + // Update 'VARIANT' to pick a Python version: 3, 3.11, 3.10, 3.9, 3.8 // Append -bullseye or -buster to pin to an OS version. // Use -bullseye variants on local on arm64/Apple Silicon. - "VARIANT": "3.11-bookworm", + "VARIANT": "3.12-bookworm", } }, From b60a94b5b305487ca5f5755ab6de2bf0adeb3d78 Mon Sep 17 00:00:00 2001 From: dekomori_sanae09 Date: Tue, 3 Oct 2023 19:23:27 +0530 Subject: [PATCH 239/757] merge double_factorial (#9431) * merge double_factorial * fix ruff error * fix merge issues * change test case * fix import error --- maths/double_factorial.py | 60 +++++++++++++++++++++++++++++ maths/double_factorial_iterative.py | 33 ---------------- maths/double_factorial_recursive.py | 31 --------------- 3 files changed, 60 insertions(+), 64 deletions(-) create mode 100644 maths/double_factorial.py delete mode 100644 maths/double_factorial_iterative.py delete mode 100644 maths/double_factorial_recursive.py diff --git a/maths/double_factorial.py b/maths/double_factorial.py new file mode 100644 index 000000000000..3c3a28304e95 --- /dev/null +++ b/maths/double_factorial.py @@ -0,0 +1,60 @@ +def double_factorial_recursive(n: int) -> int: + """ + Compute double factorial using recursive method. + Recursion can be costly for large numbers. + + To learn about the theory behind this algorithm: + https://en.wikipedia.org/wiki/Double_factorial + + >>> from math import prod + >>> all(double_factorial_recursive(i) == prod(range(i, 0, -2)) for i in range(20)) + True + >>> double_factorial_recursive(0.1) + Traceback (most recent call last): + ... + ValueError: double_factorial_recursive() only accepts integral values + >>> double_factorial_recursive(-1) + Traceback (most recent call last): + ... + ValueError: double_factorial_recursive() not defined for negative values + """ + if not isinstance(n, int): + raise ValueError("double_factorial_recursive() only accepts integral values") + if n < 0: + raise ValueError("double_factorial_recursive() not defined for negative values") + return 1 if n <= 1 else n * double_factorial_recursive(n - 2) + + +def double_factorial_iterative(num: int) -> int: + """ + Compute double factorial using iterative method. + + To learn about the theory behind this algorithm: + https://en.wikipedia.org/wiki/Double_factorial + + >>> from math import prod + >>> all(double_factorial_iterative(i) == prod(range(i, 0, -2)) for i in range(20)) + True + >>> double_factorial_iterative(0.1) + Traceback (most recent call last): + ... + ValueError: double_factorial_iterative() only accepts integral values + >>> double_factorial_iterative(-1) + Traceback (most recent call last): + ... + ValueError: double_factorial_iterative() not defined for negative values + """ + if not isinstance(num, int): + raise ValueError("double_factorial_iterative() only accepts integral values") + if num < 0: + raise ValueError("double_factorial_iterative() not defined for negative values") + value = 1 + for i in range(num, 0, -2): + value *= i + return value + + +if __name__ == "__main__": + import doctest + + doctest.testmod() diff --git a/maths/double_factorial_iterative.py b/maths/double_factorial_iterative.py deleted file mode 100644 index b2b58aa04c28..000000000000 --- a/maths/double_factorial_iterative.py +++ /dev/null @@ -1,33 +0,0 @@ -def double_factorial(num: int) -> int: - """ - Compute double factorial using iterative method. - - To learn about the theory behind this algorithm: - https://en.wikipedia.org/wiki/Double_factorial - - >>> import math - >>> all(double_factorial(i) == math.prod(range(i, 0, -2)) for i in range(20)) - True - >>> double_factorial(0.1) - Traceback (most recent call last): - ... - ValueError: double_factorial() only accepts integral values - >>> double_factorial(-1) - Traceback (most recent call last): - ... - ValueError: double_factorial() not defined for negative values - """ - if not isinstance(num, int): - raise ValueError("double_factorial() only accepts integral values") - if num < 0: - raise ValueError("double_factorial() not defined for negative values") - value = 1 - for i in range(num, 0, -2): - value *= i - return value - - -if __name__ == "__main__": - import doctest - - doctest.testmod() diff --git a/maths/double_factorial_recursive.py b/maths/double_factorial_recursive.py deleted file mode 100644 index 05c9b29680a7..000000000000 --- a/maths/double_factorial_recursive.py +++ /dev/null @@ -1,31 +0,0 @@ -def double_factorial(n: int) -> int: - """ - Compute double factorial using recursive method. - Recursion can be costly for large numbers. - - To learn about the theory behind this algorithm: - https://en.wikipedia.org/wiki/Double_factorial - - >>> import math - >>> all(double_factorial(i) == math.prod(range(i, 0, -2)) for i in range(20)) - True - >>> double_factorial(0.1) - Traceback (most recent call last): - ... - ValueError: double_factorial() only accepts integral values - >>> double_factorial(-1) - Traceback (most recent call last): - ... - ValueError: double_factorial() not defined for negative values - """ - if not isinstance(n, int): - raise ValueError("double_factorial() only accepts integral values") - if n < 0: - raise ValueError("double_factorial() not defined for negative values") - return 1 if n <= 1 else n * double_factorial(n - 2) - - -if __name__ == "__main__": - import doctest - - doctest.testmod() From 0a84b8f842c4c72f400d96313d992b608d621d07 Mon Sep 17 00:00:00 2001 From: Aasheesh <126905285+AasheeshLikePanner@users.noreply.github.com> Date: Tue, 3 Oct 2023 21:10:11 +0530 Subject: [PATCH 240/757] Changing Name of file and adding doctests in file. (#9513) * Adding doctests and changing file name * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update binary_multiplication.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update binary_multiplication.py * Changing comment and changing name function * Changing comment and changing name function * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update binary_multiplication.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update binary_multiplication.py * Update binary_multiplication.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Tianyi Zheng --- maths/binary_exponentiation_2.py | 50 --------------- maths/binary_multiplication.py | 101 +++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+), 50 deletions(-) delete mode 100644 maths/binary_exponentiation_2.py create mode 100644 maths/binary_multiplication.py diff --git a/maths/binary_exponentiation_2.py b/maths/binary_exponentiation_2.py deleted file mode 100644 index af8f776dd266..000000000000 --- a/maths/binary_exponentiation_2.py +++ /dev/null @@ -1,50 +0,0 @@ -""" -* Binary Exponentiation with Multiplication -* This is a method to find a*b in a time complexity of O(log b) -* This is one of the most commonly used methods of finding result of multiplication. -* Also useful in cases where solution to (a*b)%c is required, -* where a,b,c can be numbers over the computers calculation limits. -* Done using iteration, can also be done using recursion - -* @author chinmoy159 -* @version 1.0 dated 10/08/2017 -""" - - -def b_expo(a: int, b: int) -> int: - res = 0 - while b > 0: - if b & 1: - res += a - - a += a - b >>= 1 - - return res - - -def b_expo_mod(a: int, b: int, c: int) -> int: - res = 0 - while b > 0: - if b & 1: - res = ((res % c) + (a % c)) % c - - a += a - b >>= 1 - - return res - - -""" -* Wondering how this method works ! -* It's pretty simple. -* Let's say you need to calculate a ^ b -* RULE 1 : a * b = (a+a) * (b/2) ---- example : 4 * 4 = (4+4) * (4/2) = 8 * 2 -* RULE 2 : IF b is ODD, then ---- a * b = a + (a * (b - 1)) :: where (b - 1) is even. -* Once b is even, repeat the process to get a * b -* Repeat the process till b = 1 OR b = 0, because a*1 = a AND a*0 = 0 -* -* As far as the modulo is concerned, -* the fact : (a+b) % c = ((a%c) + (b%c)) % c -* Now apply RULE 1 OR 2, whichever is required. -""" diff --git a/maths/binary_multiplication.py b/maths/binary_multiplication.py new file mode 100644 index 000000000000..0cc5a575f445 --- /dev/null +++ b/maths/binary_multiplication.py @@ -0,0 +1,101 @@ +""" +Binary Multiplication +This is a method to find a*b in a time complexity of O(log b) +This is one of the most commonly used methods of finding result of multiplication. +Also useful in cases where solution to (a*b)%c is required, +where a,b,c can be numbers over the computers calculation limits. +Done using iteration, can also be done using recursion + +Let's say you need to calculate a * b +RULE 1 : a * b = (a+a) * (b/2) ---- example : 4 * 4 = (4+4) * (4/2) = 8 * 2 +RULE 2 : IF b is odd, then ---- a * b = a + (a * (b - 1)), where (b - 1) is even. +Once b is even, repeat the process to get a * b +Repeat the process until b = 1 or b = 0, because a*1 = a and a*0 = 0 + +As far as the modulo is concerned, +the fact : (a+b) % c = ((a%c) + (b%c)) % c +Now apply RULE 1 or 2, whichever is required. + +@author chinmoy159 +""" + + +def binary_multiply(a: int, b: int) -> int: + """ + Multiply 'a' and 'b' using bitwise multiplication. + + Parameters: + a (int): The first number. + b (int): The second number. + + Returns: + int: a * b + + Examples: + >>> binary_multiply(2, 3) + 6 + >>> binary_multiply(5, 0) + 0 + >>> binary_multiply(3, 4) + 12 + >>> binary_multiply(10, 5) + 50 + >>> binary_multiply(0, 5) + 0 + >>> binary_multiply(2, 1) + 2 + >>> binary_multiply(1, 10) + 10 + """ + res = 0 + while b > 0: + if b & 1: + res += a + + a += a + b >>= 1 + + return res + + +def binary_mod_multiply(a: int, b: int, modulus: int) -> int: + """ + Calculate (a * b) % c using binary multiplication and modular arithmetic. + + Parameters: + a (int): The first number. + b (int): The second number. + modulus (int): The modulus. + + Returns: + int: (a * b) % modulus. + + Examples: + >>> binary_mod_multiply(2, 3, 5) + 1 + >>> binary_mod_multiply(5, 0, 7) + 0 + >>> binary_mod_multiply(3, 4, 6) + 0 + >>> binary_mod_multiply(10, 5, 13) + 11 + >>> binary_mod_multiply(2, 1, 5) + 2 + >>> binary_mod_multiply(1, 10, 3) + 1 + """ + res = 0 + while b > 0: + if b & 1: + res = ((res % modulus) + (a % modulus)) % modulus + + a += a + b >>= 1 + + return res + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 81661bd2d0c34363de7d3e1e802fe2f75b9a1fa4 Mon Sep 17 00:00:00 2001 From: Ayush Yadav <115359450+ayush-yadavv@users.noreply.github.com> Date: Wed, 4 Oct 2023 05:17:26 +0530 Subject: [PATCH 241/757] Update newtons_law_of_gravitation.py : Typo(Space Removed) (#9351) --- physics/newtons_law_of_gravitation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/physics/newtons_law_of_gravitation.py b/physics/newtons_law_of_gravitation.py index 4bbeddd61d5b..ae9da2f1e949 100644 --- a/physics/newtons_law_of_gravitation.py +++ b/physics/newtons_law_of_gravitation.py @@ -3,7 +3,7 @@ provided that the other three parameters are given. Description : Newton's Law of Universal Gravitation explains the presence of force of -attraction between bodies having a definite mass situated at a distance. It is usually +attraction between bodies having a definite mass situated at a distance. It is usually stated as that, every particle attracts every other particle in the universe with a force that is directly proportional to the product of their masses and inversely proportional to the square of the distance between their centers. The publication of the From 12431389e32c290aae8c046ce9d8504d698d5f41 Mon Sep 17 00:00:00 2001 From: "Tan Kai Qun, Jeremy" Date: Wed, 4 Oct 2023 10:47:03 +0900 Subject: [PATCH 242/757] Add typing to topological_sort.py (#9650) * Add typing * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: Jeremy Tan Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- sorts/topological_sort.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/sorts/topological_sort.py b/sorts/topological_sort.py index 59a0c8571b53..efce8165fcac 100644 --- a/sorts/topological_sort.py +++ b/sorts/topological_sort.py @@ -5,11 +5,17 @@ # b c # / \ # d e -edges = {"a": ["c", "b"], "b": ["d", "e"], "c": [], "d": [], "e": []} -vertices = ["a", "b", "c", "d", "e"] +edges: dict[str, list[str]] = { + "a": ["c", "b"], + "b": ["d", "e"], + "c": [], + "d": [], + "e": [], +} +vertices: list[str] = ["a", "b", "c", "d", "e"] -def topological_sort(start, visited, sort): +def topological_sort(start: str, visited: list[str], sort: list[str]) -> list[str]: """Perform topological sort on a directed acyclic graph.""" current = start # add current to visited From 28f1e68f005f99eb628efd1af899bdfe1c1bc99e Mon Sep 17 00:00:00 2001 From: "Tan Kai Qun, Jeremy" Date: Wed, 4 Oct 2023 11:05:47 +0900 Subject: [PATCH 243/757] Add typing (#9651) Co-authored-by: Jeremy Tan --- sorts/stooge_sort.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sorts/stooge_sort.py b/sorts/stooge_sort.py index 9a5bedeae21b..767c6a05924f 100644 --- a/sorts/stooge_sort.py +++ b/sorts/stooge_sort.py @@ -1,4 +1,4 @@ -def stooge_sort(arr): +def stooge_sort(arr: list[int]) -> list[int]: """ Examples: >>> stooge_sort([18.1, 0, -7.1, -1, 2, 2]) @@ -11,7 +11,7 @@ def stooge_sort(arr): return arr -def stooge(arr, i, h): +def stooge(arr: list[int], i: int, h: int) -> None: if i >= h: return From a7133eca13d312fa729e2872048c7d9a662f6c8c Mon Sep 17 00:00:00 2001 From: "Tan Kai Qun, Jeremy" Date: Wed, 4 Oct 2023 11:06:52 +0900 Subject: [PATCH 244/757] Add typing (#9652) Co-authored-by: Jeremy Tan --- sorts/shell_sort.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sorts/shell_sort.py b/sorts/shell_sort.py index 10ae9ba407ec..b65609c974b7 100644 --- a/sorts/shell_sort.py +++ b/sorts/shell_sort.py @@ -3,7 +3,7 @@ """ -def shell_sort(collection): +def shell_sort(collection: list[int]) -> list[int]: """Pure implementation of shell sort algorithm in Python :param collection: Some mutable ordered collection with heterogeneous comparable items inside From 8c23cc5117b338ea907045260274ac40301a4e0e Mon Sep 17 00:00:00 2001 From: "Tan Kai Qun, Jeremy" Date: Wed, 4 Oct 2023 11:07:25 +0900 Subject: [PATCH 245/757] Add typing (#9654) Co-authored-by: Jeremy Tan --- sorts/selection_sort.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sorts/selection_sort.py b/sorts/selection_sort.py index f3beb31b7070..28971a5e1aad 100644 --- a/sorts/selection_sort.py +++ b/sorts/selection_sort.py @@ -11,7 +11,7 @@ """ -def selection_sort(collection): +def selection_sort(collection: list[int]) -> list[int]: """Pure implementation of the selection sort algorithm in Python :param collection: some mutable ordered collection with heterogeneous comparable items inside From 700df39ad446da895d413c0383632871459f0e9f Mon Sep 17 00:00:00 2001 From: aryan1165 <111041731+aryan1165@users.noreply.github.com> Date: Wed, 4 Oct 2023 09:04:55 +0530 Subject: [PATCH 246/757] Fixed file name in transposition_cipher_encrypt_decrypt_file.py. Fixing bug file not found. (#9426) * Fixed file name in trnasposition_cipher_encrypt_decrypt_file.py * Removed Output.txt * Removed Output.txt * Fixed build errors --- ciphers/prehistoric_men.txt | 1196 ++++++++--------- ...ansposition_cipher_encrypt_decrypt_file.py | 4 +- 2 files changed, 600 insertions(+), 600 deletions(-) diff --git a/ciphers/prehistoric_men.txt b/ciphers/prehistoric_men.txt index a58e533a8405..8d1b2bd8c8d1 100644 --- a/ciphers/prehistoric_men.txt +++ b/ciphers/prehistoric_men.txt @@ -40,8 +40,8 @@ Transcriber's note: version referred to above. One example of this might occur in the second paragraph under "Choppers and Adze-like Tools", page 46, which contains the phrase - �an adze cutting edge is ? shaped�. The symbol before - �shaped� looks like a sharply-italicized sans-serif �L�. + �an adze cutting edge is ? shaped�. The symbol before + �shaped� looks like a sharply-italicized sans-serif �L�. Devices that cannot display that symbol may substitute a question mark, a square, or other symbol. @@ -98,7 +98,7 @@ forced or pedantic; at least I have done my very best to tell the story simply and clearly. Many friends have aided in the preparation of the book. The whimsical -charm of Miss Susan Richert�s illustrations add enormously to the +charm of Miss Susan Richert�s illustrations add enormously to the spirit I wanted. She gave freely of her own time on the drawings and in planning the book with me. My colleagues at the University of Chicago, especially Professor Wilton M. Krogman (now of the University @@ -108,7 +108,7 @@ the Department of Anthropology, gave me counsel in matters bearing on their special fields, and the Department of Anthropology bore some of the expense of the illustrations. From Mrs. Irma Hunter and Mr. Arnold Maremont, who are not archeologists at all and have only an intelligent -layman�s notion of archeology, I had sound advice on how best to tell +layman�s notion of archeology, I had sound advice on how best to tell the story. I am deeply indebted to all these friends. While I was preparing the second edition, I had the great fortune @@ -117,13 +117,13 @@ Washburn, now of the Department of Anthropology of the University of California, and the fourth, fifth, and sixth chapters with Professor Hallum L. Movius, Jr., of the Peabody Museum, Harvard University. The book has gained greatly in accuracy thereby. In matters of dating, -Professor Movius and the indications of Professor W. F. Libby�s Carbon +Professor Movius and the indications of Professor W. F. Libby�s Carbon 14 chronology project have both encouraged me to choose the lowest dates now current for the events of the Pleistocene Ice Age. There is still no certain way of fixing a direct chronology for most of the -Pleistocene, but Professor Libby�s method appears very promising for +Pleistocene, but Professor Libby�s method appears very promising for its end range and for proto-historic dates. In any case, this book -names �periods,� and new dates may be written in against mine, if new +names �periods,� and new dates may be written in against mine, if new and better dating systems appear. I wish to thank Dr. Clifford C. Gregg, Director of Chicago Natural @@ -150,7 +150,7 @@ Clark Howell of the Department of Anthropology of the University of Chicago in reworking the earlier chapters, and he was very patient in the matter, which I sincerely appreciate. -All of Mrs. Susan Richert Allen�s original drawings appear, but a few +All of Mrs. Susan Richert Allen�s original drawings appear, but a few necessary corrections have been made in some of the charts and some new drawings have been added by Mr. John Pfiffner, Staff Artist, Chicago Natural History Museum. @@ -200,7 +200,7 @@ HOW WE LEARN about Prehistoric Men Prehistory means the time before written history began. Actually, more -than 99 per cent of man�s story is prehistory. Man is at least half a +than 99 per cent of man�s story is prehistory. Man is at least half a million years old, but he did not begin to write history (or to write anything) until about 5,000 years ago. @@ -216,7 +216,7 @@ The scientists who study the bones and teeth and any other parts they find of the bodies of prehistoric men, are called _physical anthropologists_. Physical anthropologists are trained, much like doctors, to know all about the human body. They study living people, -too; they know more about the biological facts of human �races� than +too; they know more about the biological facts of human �races� than anybody else. If the police find a badly decayed body in a trunk, they ask a physical anthropologist to tell them what the person originally looked like. The physical anthropologists who specialize in @@ -228,14 +228,14 @@ ARCHEOLOGISTS There is a kind of scientist who studies the things that prehistoric men made and did. Such a scientist is called an _archeologist_. It is -the archeologist�s business to look for the stone and metal tools, the +the archeologist�s business to look for the stone and metal tools, the pottery, the graves, and the caves or huts of the men who lived before history began. But there is more to archeology than just looking for things. In -Professor V. Gordon Childe�s words, archeology �furnishes a sort of +Professor V. Gordon Childe�s words, archeology �furnishes a sort of history of human activity, provided always that the actions have -produced concrete results and left recognizable material traces.� You +produced concrete results and left recognizable material traces.� You will see that there are at least three points in what Childe says: 1. The archeologists have to find the traces of things left behind by @@ -245,7 +245,7 @@ will see that there are at least three points in what Childe says: too soft or too breakable to last through the years. However, 3. The archeologist must use whatever he can find to tell a story--to - make a �sort of history�--from the objects and living-places and + make a �sort of history�--from the objects and living-places and graves that have escaped destruction. What I mean is this: Let us say you are walking through a dump yard, @@ -253,8 +253,8 @@ and you find a rusty old spark plug. If you want to think about what the spark plug means, you quickly remember that it is a part of an automobile motor. This tells you something about the man who threw the spark plug on the dump. He either had an automobile, or he knew -or lived near someone who did. He can�t have lived so very long ago, -you�ll remember, because spark plugs and automobiles are only about +or lived near someone who did. He can�t have lived so very long ago, +you�ll remember, because spark plugs and automobiles are only about sixty years old. When you think about the old spark plug in this way you have @@ -264,8 +264,8 @@ It is the same way with the man-made things we archeologists find and put in museums. Usually, only a few of these objects are pretty to look at; but each of them has some sort of story to tell. Making the interpretation of his finds is the most important part of the -archeologist�s job. It is the way he gets at the �sort of history of -human activity� which is expected of archeology. +archeologist�s job. It is the way he gets at the �sort of history of +human activity� which is expected of archeology. SOME OTHER SCIENTISTS @@ -274,7 +274,7 @@ There are many other scientists who help the archeologist and the physical anthropologist find out about prehistoric men. The geologists help us tell the age of the rocks or caves or gravel beds in which human bones or man-made objects are found. There are other scientists -with names which all begin with �paleo� (the Greek word for �old�). The +with names which all begin with �paleo� (the Greek word for �old�). The _paleontologists_ study fossil animals. There are also, for example, such scientists as _paleobotanists_ and _paleoclimatologists_, who study ancient plants and climates. These scientists help us to know @@ -306,20 +306,20 @@ systems. The rate of disappearance of radioactivity as time passes.[1]] [1] It is important that the limitations of the radioactive carbon - �dating� system be held in mind. As the statistics involved in + �dating� system be held in mind. As the statistics involved in the system are used, there are two chances in three that the - �date� of the sample falls within the range given as plus or - minus an added number of years. For example, the �date� for the - Jarmo village (see chart), given as 6750 � 200 B.C., really + �date� of the sample falls within the range given as plus or + minus an added number of years. For example, the �date� for the + Jarmo village (see chart), given as 6750 � 200 B.C., really means that there are only two chances in three that the real date of the charcoal sampled fell between 6950 and 6550 B.C. We have also begun to suspect that there are ways in which the - samples themselves may have become �contaminated,� either on + samples themselves may have become �contaminated,� either on the early or on the late side. We now tend to be suspicious of single radioactive carbon determinations, or of determinations from one site alone. But as a fabric of consistent determinations for several or more sites of one archeological - period, we gain confidence in the �dates.� + period, we gain confidence in the dates. HOW THE SCIENTISTS FIND OUT @@ -330,9 +330,9 @@ about prehistoric men. We also need a word about _how_ they find out. All our finds came by accident until about a hundred years ago. Men digging wells, or digging in caves for fertilizer, often turned up ancient swords or pots or stone arrowheads. People also found some odd -pieces of stone that didn�t look like natural forms, but they also -didn�t look like any known tool. As a result, the people who found them -gave them queer names; for example, �thunderbolts.� The people thought +pieces of stone that didn�t look like natural forms, but they also +didn�t look like any known tool. As a result, the people who found them +gave them queer names; for example, �thunderbolts.� The people thought the strange stones came to earth as bolts of lightning. We know now that these strange stones were prehistoric stone tools. @@ -349,7 +349,7 @@ story of cave men on Mount Carmel, in Palestine, began to be known. Planned archeological digging is only about a century old. Even before this, however, a few men realized the significance of objects they dug from the ground; one of these early archeologists was our own Thomas -Jefferson. The first real mound-digger was a German grocer�s clerk, +Jefferson. The first real mound-digger was a German grocer�s clerk, Heinrich Schliemann. Schliemann made a fortune as a merchant, first in Europe and then in the California gold-rush of 1849. He became an American citizen. Then he retired and had both money and time to test @@ -389,16 +389,16 @@ used had been a soft, unbaked mud-brick, and most of the debris consisted of fallen or rain-melted mud from these mud-bricks. This idea of _stratification_, like the cake layers, was already a -familiar one to the geologists by Schliemann�s time. They could show +familiar one to the geologists by Schliemann�s time. They could show that their lowest layer of rock was oldest or earliest, and that the -overlying layers became more recent as one moved upward. Schliemann�s +overlying layers became more recent as one moved upward. Schliemann�s digging proved the same thing at Troy. His first (lowest and earliest) city had at least nine layers above it; he thought that the second -layer contained the remains of Homer�s Troy. We now know that Homeric +layer contained the remains of Homer�s Troy. We now know that Homeric Troy was layer VIIa from the bottom; also, we count eleven layers or sub-layers in total. -Schliemann�s work marks the beginnings of modern archeology. Scholars +Schliemann�s work marks the beginnings of modern archeology. Scholars soon set out to dig on ancient sites, from Egypt to Central America. @@ -410,21 +410,21 @@ Archeologists began to get ideas as to the kinds of objects that belonged together. If you compared a mail-order catalogue of 1890 with one of today, you would see a lot of differences. If you really studied the two catalogues hard, you would also begin to see that certain -objects �go together.� Horseshoes and metal buggy tires and pieces of +objects �go together.� Horseshoes and metal buggy tires and pieces of harness would begin to fit into a picture with certain kinds of coal stoves and furniture and china dishes and kerosene lamps. Our friend the spark plug, and radios and electric refrigerators and light bulbs would fit into a picture with different kinds of furniture and dishes -and tools. You won�t be old enough to remember the kind of hats that -women wore in 1890, but you�ve probably seen pictures of them, and you -know very well they couldn�t be worn with the fashions of today. +and tools. You won�t be old enough to remember the kind of hats that +women wore in 1890, but you�ve probably seen pictures of them, and you +know very well they couldn�t be worn with the fashions of today. This is one of the ways that archeologists study their materials. The various tools and weapons and jewelry, the pottery, the kinds of houses, and even the ways of burying the dead tend to fit into pictures. Some archeologists call all of the things that go together to make such a picture an _assemblage_. The assemblage of the first layer -of Schliemann�s Troy was as different from that of the seventh layer as +of Schliemann�s Troy was as different from that of the seventh layer as our 1900 mail-order catalogue is from the one of today. The archeologists who came after Schliemann began to notice other @@ -433,23 +433,23 @@ idea that people will buy better mousetraps goes back into very ancient times. Today, if we make good automobiles or radios, we can sell some of them in Turkey or even in Timbuktu. This means that a few present-day types of American automobiles and radios form part -of present-day �assemblages� in both Turkey and Timbuktu. The total -present-day �assemblage� of Turkey is quite different from that of +of present-day �assemblages� in both Turkey and Timbuktu. The total +present-day �assemblage� of Turkey is quite different from that of Timbuktu or that of America, but they have at least some automobiles and some radios in common. Now these automobiles and radios will eventually wear out. Let us suppose we could go to some remote part of Turkey or to Timbuktu in a -dream. We don�t know what the date is, in our dream, but we see all +dream. We don�t know what the date is, in our dream, but we see all sorts of strange things and ways of living in both places. Nobody tells us what the date is. But suddenly we see a 1936 Ford; so we know that in our dream it has to be at least the year 1936, and only as many years after that as we could reasonably expect a Ford to keep -in running order. The Ford would probably break down in twenty years� -time, so the Turkish or Timbuktu �assemblage� we�re seeing in our dream +in running order. The Ford would probably break down in twenty years� +time, so the Turkish or Timbuktu �assemblage� we�re seeing in our dream has to date at about A.D. 1936-56. -Archeologists not only �date� their ancient materials in this way; they +Archeologists not only �date� their ancient materials in this way; they also see over what distances and between which peoples trading was done. It turns out that there was a good deal of trading in ancient times, probably all on a barter and exchange basis. @@ -480,13 +480,13 @@ site. They find the remains of everything that would last through time, in several different layers. They know that the assemblage in the bottom layer was laid down earlier than the assemblage in the next layer above, and so on up to the topmost layer, which is the latest. -They look at the results of other �digs� and find that some other +They look at the results of other �digs� and find that some other archeologist 900 miles away has found ax-heads in his lowest layer, exactly like the ax-heads of their fifth layer. This means that their fifth layer must have been lived in at about the same time as was the first layer in the site 200 miles away. It also may mean that the people who lived in the two layers knew and traded with each other. Or -it could mean that they didn�t necessarily know each other, but simply +it could mean that they didn�t necessarily know each other, but simply that both traded with a third group at about the same time. You can see that the more we dig and find, the more clearly the main @@ -501,8 +501,8 @@ those of domesticated animals, for instance, sheep or cattle, and therefore the people must have kept herds. More important than anything else--as our structure grows more -complicated and our materials increase--is the fact that �a sort -of history of human activity� does begin to appear. The habits or +complicated and our materials increase--is the fact that �a sort +of history of human activity� does begin to appear. The habits or traditions that men formed in the making of their tools and in the ways they did things, begin to stand out for us. How characteristic were these habits and traditions? What areas did they spread over? @@ -519,7 +519,7 @@ method--chemical tests of the bones--that will enable them to discover what the blood-type may have been. One thing is sure. We have never found a group of skeletons so absolutely similar among themselves--so cast from a single mould, so to speak--that we could claim to have a -�pure� race. I am sure we never shall. +�pure� race. I am sure we never shall. We become particularly interested in any signs of change--when new materials and tool types and ways of doing things replace old ones. We @@ -527,7 +527,7 @@ watch for signs of social change and progress in one way or another. We must do all this without one word of written history to aid us. Everything we are concerned with goes back to the time _before_ men -learned to write. That is the prehistorian�s job--to find out what +learned to write. That is the prehistorian�s job--to find out what happened before history began. @@ -538,9 +538,9 @@ THE CHANGING WORLD in which Prehistoric Men Lived [Illustration] -Mankind, we�ll say, is at least a half million years old. It is very +Mankind, we�ll say, is at least a half million years old. It is very hard to understand how long a time half a million years really is. -If we were to compare this whole length of time to one day, we�d get +If we were to compare this whole length of time to one day, we�d get something like this: The present time is midnight, and Jesus was born just five minutes and thirty-six seconds ago. Earliest history began less than fifteen minutes ago. Everything before 11:45 was in @@ -569,7 +569,7 @@ book; it would mainly affect the dates earlier than 25,000 years ago. CHANGES IN ENVIRONMENT -The earth probably hasn�t changed much in the last 5,000 years (250 +The earth probably hasn�t changed much in the last 5,000 years (250 generations). Men have built things on its surface and dug into it and drawn boundaries on maps of it, but the places where rivers, lakes, seas, and mountains now stand have changed very little. @@ -605,7 +605,7 @@ the glaciers covered most of Canada and the northern United States and reached down to southern England and France in Europe. Smaller ice sheets sat like caps on the Rockies, the Alps, and the Himalayas. The continental glaciation only happened north of the equator, however, so -remember that �Ice Age� is only half true. +remember that �Ice Age� is only half true. As you know, the amount of water on and about the earth does not vary. These large glaciers contained millions of tons of water frozen into @@ -677,9 +677,9 @@ their dead. At about the time when the last great glacier was finally melting away, men in the Near East made the first basic change in human economy. They began to plant grain, and they learned to raise and herd certain -animals. This meant that they could store food in granaries and �on the -hoof� against the bad times of the year. This first really basic change -in man�s way of living has been called the �food-producing revolution.� +animals. This meant that they could store food in granaries and �on the +hoof� against the bad times of the year. This first really basic change +in man�s way of living has been called the �food-producing revolution.� By the time it happened, a modern kind of climate was beginning. Men had already grown to look as they do now. Know-how in ways of living had developed and progressed, slowly but surely, up to a point. It was @@ -698,25 +698,25 @@ Prehistoric Men THEMSELVES DO WE KNOW WHERE MAN ORIGINATED? -For a long time some scientists thought the �cradle of mankind� was in +For a long time some scientists thought the �cradle of mankind� was in central Asia. Other scientists insisted it was in Africa, and still -others said it might have been in Europe. Actually, we don�t know -where it was. We don�t even know that there was only _one_ �cradle.� -If we had to choose a �cradle� at this moment, we would probably say +others said it might have been in Europe. Actually, we don�t know +where it was. We don�t even know that there was only _one_ �cradle.� +If we had to choose a �cradle� at this moment, we would probably say Africa. But the southern portions of Asia and Europe may also have been included in the general area. The scene of the early development of -mankind was certainly the Old World. It is pretty certain men didn�t +mankind was certainly the Old World. It is pretty certain men didn�t reach North or South America until almost the end of the Ice Age--had they done so earlier we would certainly have found some trace of them by now. The earliest tools we have yet found come from central and south -Africa. By the dating system I�m using, these tools must be over +Africa. By the dating system I�m using, these tools must be over 500,000 years old. There are now reports that a few such early tools have been found--at the Sterkfontein cave in South Africa--along with -the bones of small fossil men called �australopithecines.� +the bones of small fossil men called �australopithecines.� -Not all scientists would agree that the australopithecines were �men,� +Not all scientists would agree that the australopithecines were �men,� or would agree that the tools were made by the australopithecines themselves. For these sticklers, the earliest bones of men come from the island of Java. The date would be about 450,000 years ago. So far, @@ -727,12 +727,12 @@ Let me say it another way. How old are the earliest traces of men we now have? Over half a million years. This was a time when the first alpine glaciation was happening in the north. What has been found so far? The tools which the men of those times made, in different parts -of Africa. It is now fairly generally agreed that the �men� who made -the tools were the australopithecines. There is also a more �man-like� +of Africa. It is now fairly generally agreed that the �men� who made +the tools were the australopithecines. There is also a more �man-like� jawbone at Kanam in Kenya, but its find-spot has been questioned. The next earliest bones we have were found in Java, and they may be almost a hundred thousand years younger than the earliest African finds. We -haven�t yet found the tools of these early Javanese. Our knowledge of +haven�t yet found the tools of these early Javanese. Our knowledge of tool-using in Africa spreads quickly as time goes on: soon after the appearance of tools in the south we shall have them from as far north as Algeria. @@ -758,30 +758,30 @@ prove it. MEN AND APES Many people used to get extremely upset at the ill-formed notion -that �man descended from the apes.� Such words were much more likely -to start fights or �monkey trials� than the correct notion that all +that �man descended from the apes.� Such words were much more likely +to start fights or �monkey trials� than the correct notion that all living animals, including man, ascended or evolved from a single-celled organism which lived in the primeval seas hundreds of millions of years -ago. Men are mammals, of the order called Primates, and man�s living -relatives are the great apes. Men didn�t �descend� from the apes or +ago. Men are mammals, of the order called Primates, and man�s living +relatives are the great apes. Men didn�t �descend� from the apes or apes from men, and mankind must have had much closer relatives who have since become extinct. Men stand erect. They also walk and run on their two feet. Apes are happiest in trees, swinging with their arms from branch to branch. Few branches of trees will hold the mighty gorilla, although he still -manages to sleep in trees. Apes can�t stand really erect in our sense, +manages to sleep in trees. Apes can�t stand really erect in our sense, and when they have to run on the ground, they use the knuckles of their hands as well as their feet. A key group of fossil bones here are the south African australopithecines. These are called the _Australopithecinae_ or -�man-apes� or sometimes even �ape-men.� We do not _know_ that they were +�man-apes� or sometimes even �ape-men.� We do not _know_ that they were directly ancestral to men but they can hardly have been so to apes. -Presently I�ll describe them a bit more. The reason I mention them +Presently I�ll describe them a bit more. The reason I mention them here is that while they had brains no larger than those of apes, their hipbones were enough like ours so that they must have stood erect. -There is no good reason to think they couldn�t have walked as we do. +There is no good reason to think they couldn�t have walked as we do. BRAINS, HANDS, AND TOOLS @@ -801,12 +801,12 @@ Nobody knows which of these three is most important, or which came first. Most probably the growth of all three things was very much blended together. If you think about each of the things, you will see what I mean. Unless your hand is more flexible than a paw, and your -thumb will work against (or oppose) your fingers, you can�t hold a tool -very well. But you wouldn�t get the idea of using a tool unless you had +thumb will work against (or oppose) your fingers, you can�t hold a tool +very well. But you wouldn�t get the idea of using a tool unless you had enough brain to help you see cause and effect. And it is rather hard to see how your hand and brain would develop unless they had something to -practice on--like using tools. In Professor Krogman�s words, �the hand -must become the obedient servant of the eye and the brain.� It is the +practice on--like using tools. In Professor Krogman�s words, �the hand +must become the obedient servant of the eye and the brain.� It is the _co-ordination_ of these things that counts. Many other things must have been happening to the bodies of the @@ -820,17 +820,17 @@ little by little, all together. Men became men very slowly. WHEN SHALL WE CALL MEN MEN? -What do I mean when I say �men�? People who looked pretty much as we +What do I mean when I say �men�? People who looked pretty much as we do, and who used different tools to do different things, are men to me. -We�ll probably never know whether the earliest ones talked or not. They +We�ll probably never know whether the earliest ones talked or not. They probably had vocal cords, so they could make sounds, but did they know how to make sounds work as symbols to carry meanings? But if the fossil -bones look like our skeletons, and if we find tools which we�ll agree -couldn�t have been made by nature or by animals, then I�d say we had +bones look like our skeletons, and if we find tools which we�ll agree +couldn�t have been made by nature or by animals, then I�d say we had traces of _men_. The australopithecine finds of the Transvaal and Bechuanaland, in -south Africa, are bound to come into the discussion here. I�ve already +south Africa, are bound to come into the discussion here. I�ve already told you that the australopithecines could have stood upright and walked on their two hind legs. They come from the very base of the Pleistocene or Ice Age, and a few coarse stone tools have been found @@ -848,17 +848,17 @@ bones. The doubt as to whether the australopithecines used the tools themselves goes like this--just suppose some man-like creature (whose bones we have not yet found) made the tools and used them to kill and butcher australopithecines. Hence a few experts tend to let -australopithecines still hang in limbo as �man-apes.� +australopithecines still hang in limbo as �man-apes.� THE EARLIEST MEN WE KNOW -I�ll postpone talking about the tools of early men until the next +I�ll postpone talking about the tools of early men until the next chapter. The men whose bones were the earliest of the Java lot have been given the name _Meganthropus_. The bones are very fragmentary. We would not understand them very well unless we had the somewhat later -Javanese lot--the more commonly known _Pithecanthropus_ or �Java -man�--against which to refer them for study. One of the less well-known +Javanese lot--the more commonly known _Pithecanthropus_ or �Java +man�--against which to refer them for study. One of the less well-known and earliest fragments, a piece of lower jaw and some teeth, rather strongly resembles the lower jaws and teeth of the australopithecine type. Was _Meganthropus_ a sort of half-way point between the @@ -872,7 +872,7 @@ finds of Java man were made in 1891-92 by Dr. Eugene Dubois, a Dutch doctor in the colonial service. Finds have continued to be made. There are now bones enough to account for four skulls. There are also four jaws and some odd teeth and thigh bones. Java man, generally speaking, -was about five feet six inches tall, and didn�t hold his head very +was about five feet six inches tall, and didn�t hold his head very erect. His skull was very thick and heavy and had room for little more than two-thirds as large a brain as we have. He had big teeth and a big jaw and enormous eyebrow ridges. @@ -885,22 +885,22 @@ belonged to his near descendants. Remember that there are several varieties of men in the whole early Java lot, at least two of which are earlier than the _Pithecanthropus_, -�Java man.� Some of the earlier ones seem to have gone in for +�Java man.� Some of the earlier ones seem to have gone in for bigness, in tooth-size at least. _Meganthropus_ is one of these earlier varieties. As we said, he _may_ turn out to be a link to the australopithecines, who _may_ or _may not_ be ancestral to men. _Meganthropus_ is best understandable in terms of _Pithecanthropus_, who appeared later in the same general area. _Pithecanthropus_ is pretty well understandable from the bones he left us, and also because -of his strong resemblance to the fully tool-using cave-dwelling �Peking -man,� _Sinanthropus_, about whom we shall talk next. But you can see +of his strong resemblance to the fully tool-using cave-dwelling �Peking +man,� _Sinanthropus_, about whom we shall talk next. But you can see that the physical anthropologists and prehistoric archeologists still have a lot of work to do on the problem of earliest men. PEKING MEN AND SOME EARLY WESTERNERS -The earliest known Chinese are called _Sinanthropus_, or �Peking man,� +The earliest known Chinese are called _Sinanthropus_, or �Peking man,� because the finds were made near that city. In World War II, the United States Marine guard at our Embassy in Peking tried to help get the bones out of the city before the Japanese attack. Nobody knows where @@ -913,9 +913,9 @@ casts of the bones. Peking man lived in a cave in a limestone hill, made tools, cracked animal bones to get the marrow out, and used fire. Incidentally, the bones of Peking man were found because Chinese dig for what they call -�dragon bones� and �dragon teeth.� Uneducated Chinese buy these things +�dragon bones� and �dragon teeth.� Uneducated Chinese buy these things in their drug stores and grind them into powder for medicine. The -�dragon teeth� and �bones� are really fossils of ancient animals, and +�dragon teeth� and �bones� are really fossils of ancient animals, and sometimes of men. The people who supply the drug stores have learned where to dig for strange bones and teeth. Paleontologists who get to China go to the drug stores to buy fossils. In a roundabout way, this @@ -924,7 +924,7 @@ is how the fallen-in cave of Peking man at Choukoutien was discovered. Peking man was not quite as tall as Java man but he probably stood straighter. His skull looked very much like that of the Java skull except that it had room for a slightly larger brain. His face was less -brutish than was Java man�s face, but this isn�t saying much. +brutish than was Java man�s face, but this isn�t saying much. Peking man dates from early in the interglacial period following the second alpine glaciation. He probably lived close to 350,000 years @@ -946,9 +946,9 @@ big ridges over the eyes. The more fragmentary skull from Swanscombe in England (p. 11) has been much more carefully studied. Only the top and back of that skull have been found. Since the skull rounds up nicely, it has been assumed that the face and forehead must have been quite -�modern.� Careful comparison with Steinheim shows that this was not +�modern.� Careful comparison with Steinheim shows that this was not necessarily so. This is important because it bears on the question of -how early truly �modern� man appeared. +how early truly �modern� man appeared. Recently two fragmentary jaws were found at Ternafine in Algeria, northwest Africa. They look like the jaws of Peking man. Tools were @@ -971,22 +971,22 @@ modern Australian natives. During parts of the Ice Age there was a land bridge all the way from Java to Australia. -TWO ENGLISHMEN WHO WEREN�T OLD +TWO ENGLISHMEN WHO WEREN�T OLD The older textbooks contain descriptions of two English finds which were thought to be very old. These were called Piltdown (_Eoanthropus dawsoni_) and Galley Hill. The skulls were very modern in appearance. In 1948-49, British scientists began making chemical tests which proved that neither of these finds is very old. It is now known that both -�Piltdown man� and the tools which were said to have been found with +�Piltdown man� and the tools which were said to have been found with him were part of an elaborate fake! -TYPICAL �CAVE MEN� +TYPICAL �CAVE MEN� The next men we have to talk about are all members of a related group. -These are the Neanderthal group. �Neanderthal man� himself was found in -the Neander Valley, near D�sseldorf, Germany, in 1856. He was the first +These are the Neanderthal group. �Neanderthal man� himself was found in +the Neander Valley, near D�sseldorf, Germany, in 1856. He was the first human fossil to be recognized as such. [Illustration: PRINCIPAL KNOWN TYPES OF FOSSIL MEN @@ -999,7 +999,7 @@ human fossil to be recognized as such. PITHECANTHROPUS] Some of us think that the neanderthaloids proper are only those people -of western Europe who didn�t get out before the beginning of the last +of western Europe who didn�t get out before the beginning of the last great glaciation, and who found themselves hemmed in by the glaciers in the Alps and northern Europe. Being hemmed in, they intermarried a bit too much and developed into a special type. Professor F. Clark @@ -1010,7 +1010,7 @@ pre-neanderthaloids. There are traces of these pre-neanderthaloids pretty much throughout Europe during the third interglacial period--say 100,000 years ago. The pre-neanderthaloids are represented by such finds as the ones at Ehringsdorf in Germany and Saccopastore in Italy. -I won�t describe them for you, since they are simply less extreme than +I won�t describe them for you, since they are simply less extreme than the neanderthaloids proper--about half way between Steinheim and the classic Neanderthal people. @@ -1019,24 +1019,24 @@ get caught in the pocket of the southwest corner of Europe at the onset of the last great glaciation became the classic Neanderthalers. Out in the Near East, Howell thinks, it is possible to see traces of people evolving from the pre-neanderthaloid type toward that of fully modern -man. Certainly, we don�t see such extreme cases of �neanderthaloidism� +man. Certainly, we don�t see such extreme cases of �neanderthaloidism� outside of western Europe. There are at least a dozen good examples in the main or classic Neanderthal group in Europe. They date to just before and in the earlier part of the last great glaciation (85,000 to 40,000 years ago). -Many of the finds have been made in caves. The �cave men� the movies +Many of the finds have been made in caves. The �cave men� the movies and the cartoonists show you are probably meant to be Neanderthalers. -I�m not at all sure they dragged their women by the hair; the women +I�m not at all sure they dragged their women by the hair; the women were probably pretty tough, too! Neanderthal men had large bony heads, but plenty of room for brains. Some had brain cases even larger than the average for modern man. Their faces were heavy, and they had eyebrow ridges of bone, but the ridges were not as big as those of Java man. Their foreheads were very low, -and they didn�t have much chin. They were about five feet three inches -tall, but were heavy and barrel-chested. But the Neanderthalers didn�t -slouch as much as they�ve been blamed for, either. +and they didn�t have much chin. They were about five feet three inches +tall, but were heavy and barrel-chested. But the Neanderthalers didn�t +slouch as much as they�ve been blamed for, either. One important thing about the Neanderthal group is that there is a fair number of them to study. Just as important is the fact that we know @@ -1059,10 +1059,10 @@ different-looking people. EARLY MODERN MEN -How early is modern man (_Homo sapiens_), the �wise man�? Some people +How early is modern man (_Homo sapiens_), the �wise man�? Some people have thought that he was very early, a few still think so. Piltdown and Galley Hill, which were quite modern in anatomical appearance and -_supposedly_ very early in date, were the best �evidence� for very +_supposedly_ very early in date, were the best �evidence� for very early modern men. Now that Piltdown has been liquidated and Galley Hill is known to be very late, what is left of the idea? @@ -1073,13 +1073,13 @@ the Ternafine jaws, you might come to the conclusion that the crown of the Swanscombe head was that of a modern-like man. Two more skulls, again without faces, are available from a French -cave site, Font�chevade. They come from the time of the last great +cave site, Font�chevade. They come from the time of the last great interglacial, as did the pre-neanderthaloids. The crowns of the -Font�chevade skulls also look quite modern. There is a bit of the +Font�chevade skulls also look quite modern. There is a bit of the forehead preserved on one of these skulls and the brow-ridge is not heavy. Nevertheless, there is a suggestion that the bones belonged to an immature individual. In this case, his (or even more so, if _her_) -brow-ridges would have been weak anyway. The case for the Font�chevade +brow-ridges would have been weak anyway. The case for the Font�chevade fossils, as modern type men, is little stronger than that for Swanscombe, although Professor Vallois believes it a good case. @@ -1101,8 +1101,8 @@ of the onset of colder weather, when the last glaciation was beginning in the north--say 75,000 years ago. The 70 per cent modern group came from only one cave, Mugharet es-Skhul -(�cave of the kids�). The other group, from several caves, had bones of -men of the type we�ve been calling pre-neanderthaloid which we noted +(�cave of the kids�). The other group, from several caves, had bones of +men of the type we�ve been calling pre-neanderthaloid which we noted were widespread in Europe and beyond. The tools which came with each of these finds were generally similar, and McCown and Keith, and other scholars since their study, have tended to assume that both the Skhul @@ -1131,26 +1131,26 @@ important fossil men of later Europe are shown in the chart on page DIFFERENCES IN THE EARLY MODERNS The main early European moderns have been divided into two groups, the -Cro-Magnon group and the Combe Capelle-Br�nn group. Cro-Magnon people +Cro-Magnon group and the Combe Capelle-Br�nn group. Cro-Magnon people were tall and big-boned, with large, long, and rugged heads. They must have been built like many present-day Scandinavians. The Combe -Capelle-Br�nn people were shorter; they had narrow heads and faces, and -big eyebrow-ridges. Of course we don�t find the skin or hair of these -people. But there is little doubt they were Caucasoids (�Whites�). +Capelle-Br�nn people were shorter; they had narrow heads and faces, and +big eyebrow-ridges. Of course we don�t find the skin or hair of these +people. But there is little doubt they were Caucasoids (�Whites�). Another important find came in the Italian Riviera, near Monte Carlo. Here, in a cave near Grimaldi, there was a grave containing a woman and a young boy, buried together. The two skeletons were first called -�Negroid� because some features of their bones were thought to resemble +�Negroid� because some features of their bones were thought to resemble certain features of modern African Negro bones. But more recently, Professor E. A. Hooton and other experts questioned the use of the word -�Negroid� in describing the Grimaldi skeletons. It is true that nothing +�Negroid� in describing the Grimaldi skeletons. It is true that nothing is known of the skin color, hair form, or any other fleshy feature of -the Grimaldi people, so that the word �Negroid� in its usual meaning is +the Grimaldi people, so that the word �Negroid� in its usual meaning is not proper here. It is also not clear whether the features of the bones -claimed to be �Negroid� are really so at all. +claimed to be �Negroid� are really so at all. -From a place called Wadjak, in Java, we have �proto-Australoid� skulls +From a place called Wadjak, in Java, we have �proto-Australoid� skulls which closely resemble those of modern Australian natives. Some of the skulls found in South Africa, especially the Boskop skull, look like those of modern Bushmen, but are much bigger. The ancestors of @@ -1159,12 +1159,12 @@ Desert. True African Negroes were forest people who apparently expanded out of the west central African area only in the last several thousand years. Although dark in skin color, neither the Australians nor the Bushmen are Negroes; neither the Wadjak nor the Boskop skulls are -�Negroid.� +�Negroid.� -As we�ve already mentioned, Professor Weidenreich believed that Peking +As we�ve already mentioned, Professor Weidenreich believed that Peking man was already on the way to becoming a Mongoloid. Anyway, the -Mongoloids would seem to have been present by the time of the �Upper -Cave� at Choukoutien, the _Sinanthropus_ find-spot. +Mongoloids would seem to have been present by the time of the �Upper +Cave� at Choukoutien, the _Sinanthropus_ find-spot. WHAT THE DIFFERENCES MEAN @@ -1175,14 +1175,14 @@ From area to area, men tended to look somewhat different, just as they do today. This is all quite natural. People _tended_ to mate near home; in the anthropological jargon, they made up geographically localized breeding populations. The simple continental division of -�stocks�--black = Africa, yellow = Asia, white = Europe--is too simple +�stocks�--black = Africa, yellow = Asia, white = Europe--is too simple a picture to fit the facts. People became accustomed to life in some -particular area within a continent (we might call it a �natural area�). +particular area within a continent (we might call it a �natural area�). As they went on living there, they evolved towards some particular physical variety. It would, of course, have been difficult to draw a clear boundary between two adjacent areas. There must always have been some mating across the boundaries in every case. One thing human -beings don�t do, and never have done, is to mate for �purity.� It is +beings don�t do, and never have done, is to mate for �purity.� It is self-righteous nonsense when we try to kid ourselves into thinking that they do. @@ -1195,28 +1195,28 @@ and they must do the writing about races. I shall, however, give two modern definitions of race, and then make one comment. Dr. William G. Boyd, professor of Immunochemistry, School of - Medicine, Boston University: �We may define a human race as a + Medicine, Boston University: �We may define a human race as a population which differs significantly from other human populations in regard to the frequency of one or more of the genes it - possesses.� + possesses.� Professor Sherwood L. Washburn, professor of Physical Anthropology, - Department of Anthropology, the University of California: �A �race� + Department of Anthropology, the University of California: �A �race� is a group of genetically similar populations, and races intergrade - because there are always intermediate populations.� + because there are always intermediate populations.� My comment is that the ideas involved here are all biological: they concern groups, _not_ individuals. Boyd and Washburn may differ a bit -on what they want to consider a �population,� but a population is a +on what they want to consider a �population,� but a population is a group nevertheless, and genetics is biology to the hilt. Now a lot of people still think of race in terms of how people dress or fix their food or of other habits or customs they have. The next step is to talk -about racial �purity.� None of this has anything whatever to do with +about racial �purity.� None of this has anything whatever to do with race proper, which is a matter of the biology of groups. -Incidentally, I�m told that if man very carefully _controls_ +Incidentally, I�m told that if man very carefully _controls_ the breeding of certain animals over generations--dogs, cattle, -chickens--he might achieve a �pure� race of animals. But he doesn�t do +chickens--he might achieve a �pure� race of animals. But he doesn�t do it. Some unfortunate genetic trait soon turns up, so this has just as carefully to be bred out again, and so on. @@ -1240,20 +1240,20 @@ date to the second great interglacial period, about 350,000 years ago. Piltdown and Galley Hill are out, and with them, much of the starch in the old idea that there were two distinct lines of development -in human evolution: (1) a line of �paleoanthropic� development from +in human evolution: (1) a line of �paleoanthropic� development from Heidelberg to the Neanderthalers where it became extinct, and (2) a -very early �modern� line, through Piltdown, Galley Hill, Swanscombe, to +very early �modern� line, through Piltdown, Galley Hill, Swanscombe, to us. Swanscombe, Steinheim, and Ternafine are just as easily cases of very early pre-neanderthaloids. The pre-neanderthaloids were very widespread during the third interglacial: Ehringsdorf, Saccopastore, some of the Mount Carmel -people, and probably Font�chevade are cases in point. A variety of +people, and probably Font�chevade are cases in point. A variety of their descendants can be seen, from Java (Solo), Africa (Rhodesian man), and about the Mediterranean and in western Europe. As the acute cold of the last glaciation set in, the western Europeans found themselves surrounded by water, ice, or bitter cold tundra. To vastly -over-simplify it, they �bred in� and became classic neanderthaloids. +over-simplify it, they �bred in� and became classic neanderthaloids. But on Mount Carmel, the Skhul cave-find with its 70 per cent modern features shows what could happen elsewhere at the same time. @@ -1263,12 +1263,12 @@ modern skeletons of men. The modern skeletons differ from place to place, just as different groups of men living in different places still look different. -What became of the Neanderthalers? Nobody can tell me for sure. I�ve a -hunch they were simply �bred out� again when the cold weather was over. +What became of the Neanderthalers? Nobody can tell me for sure. I�ve a +hunch they were simply �bred out� again when the cold weather was over. Many Americans, as the years go by, are no longer ashamed to claim they -have �Indian blood in their veins.� Give us a few more generations +have �Indian blood in their veins.� Give us a few more generations and there will not be very many other Americans left to whom we can -brag about it. It certainly isn�t inconceivable to me to imagine a +brag about it. It certainly isn�t inconceivable to me to imagine a little Cro-Magnon boy bragging to his friends about his tough, strong, Neanderthaler great-great-great-great-grandfather! @@ -1281,15 +1281,15 @@ Cultural BEGINNINGS Men, unlike the lower animals, are made up of much more than flesh and -blood and bones; for men have �culture.� +blood and bones; for men have �culture.� WHAT IS CULTURE? -�Culture� is a word with many meanings. The doctors speak of making a -�culture� of a certain kind of bacteria, and ants are said to have a -�culture.� Then there is the Emily Post kind of �culture�--you say a -person is �cultured,� or that he isn�t, depending on such things as +�Culture� is a word with many meanings. The doctors speak of making a +�culture� of a certain kind of bacteria, and ants are said to have a +�culture.� Then there is the Emily Post kind of �culture�--you say a +person is �cultured,� or that he isn�t, depending on such things as whether or not he eats peas with his knife. The anthropologists use the word too, and argue heatedly over its finer @@ -1300,7 +1300,7 @@ men from another. In this sense, a CULTURE means the way the members of a group of people think and believe and live, the tools they make, and the way they do things. Professor Robert Redfield says a culture is an organized or formalized body of conventional understandings. -�Conventional understandings� means the whole set of rules, beliefs, +�Conventional understandings� means the whole set of rules, beliefs, and standards which a group of people lives by. These understandings show themselves in art, and in the other things a people may make and do. The understandings continue to last, through tradition, from one @@ -1325,12 +1325,12 @@ Egyptians. I mean their beliefs as to why grain grew, as well as their ability to make tools with which to reap the grain. I mean their beliefs about life after death. What I am thinking about as culture is a thing which lasted in time. If any one Egyptian, even the Pharaoh, -died, it didn�t affect the Egyptian culture of that particular moment. +died, it didn�t affect the Egyptian culture of that particular moment. PREHISTORIC CULTURES -For that long period of man�s history that is all prehistory, we have +For that long period of man�s history that is all prehistory, we have no written descriptions of cultures. We find only the tools men made, the places where they lived, the graves in which they buried their dead. Fortunately for us, these tools and living places and graves all @@ -1345,15 +1345,15 @@ of the classic European Neanderthal group of men, we have found few cave-dwelling places of very early prehistoric men. First, there is the fallen-in cave where Peking man was found, near Peking. Then there are two or three other _early_, but not _very early_, possibilities. The -finds at the base of the French cave of Font�chevade, those in one of +finds at the base of the French cave of Font�chevade, those in one of the Makapan caves in South Africa, and several open sites such as Dr. -L. S. B. Leakey�s Olorgesailie in Kenya doubtless all lie earlier than +L. S. B. Leakey�s Olorgesailie in Kenya doubtless all lie earlier than the time of the main European Neanderthal group, but none are so early as the Peking finds. You can see that we know very little about the home life of earlier prehistoric men. We find different kinds of early stone tools, but we -can�t even be really sure which tools may have been used together. +can�t even be really sure which tools may have been used together. WHY LITTLE HAS LASTED FROM EARLY TIMES @@ -1380,11 +1380,11 @@ there first! The front of this enormous sheet of ice moved down over the country, crushing and breaking and plowing up everything, like a gigantic bulldozer. You can see what happened to our camp site. -Everything the glacier couldn�t break, it pushed along in front of it +Everything the glacier couldn�t break, it pushed along in front of it or plowed beneath it. Rocks were ground to gravel, and soil was caught into the ice, which afterwards melted and ran off as muddy water. Hard -tools of flint sometimes remained whole. Human bones weren�t so hard; -it�s a wonder _any_ of them lasted. Gushing streams of melt water +tools of flint sometimes remained whole. Human bones weren�t so hard; +it�s a wonder _any_ of them lasted. Gushing streams of melt water flushed out the debris from underneath the glacier, and water flowed off the surface and through great crevasses. The hard materials these waters carried were even more rolled and ground up. Finally, such @@ -1407,26 +1407,26 @@ all up, and so we cannot say which particular sets of tools belonged together in the first place. -�EOLITHS� +�EOLITHS� But what sort of tools do we find earliest? For almost a century, people have been picking up odd bits of flint and other stone in the oldest Ice Age gravels in England and France. It is now thought these -odd bits of stone weren�t actually worked by prehistoric men. The -stones were given a name, _eoliths_, or �dawn stones.� You can see them +odd bits of stone weren�t actually worked by prehistoric men. The +stones were given a name, _eoliths_, or �dawn stones.� You can see them in many museums; but you can be pretty sure that very few of them were actually fashioned by men. -It is impossible to pick out �eoliths� that seem to be made in any -one _tradition_. By �tradition� I mean a set of habits for making one -kind of tool for some particular job. No two �eoliths� look very much +It is impossible to pick out �eoliths� that seem to be made in any +one _tradition_. By �tradition� I mean a set of habits for making one +kind of tool for some particular job. No two �eoliths� look very much alike: tools made as part of some one tradition all look much alike. -Now it�s easy to suppose that the very earliest prehistoric men picked -up and used almost any sort of stone. This wouldn�t be surprising; you -and I do it when we go camping. In other words, some of these �eoliths� +Now it�s easy to suppose that the very earliest prehistoric men picked +up and used almost any sort of stone. This wouldn�t be surprising; you +and I do it when we go camping. In other words, some of these �eoliths� may actually have been used by prehistoric men. They must have used anything that might be handy when they needed it. We could have figured -that out without the �eoliths.� +that out without the �eoliths.� THE ROAD TO STANDARDIZATION @@ -1434,7 +1434,7 @@ THE ROAD TO STANDARDIZATION Reasoning from what we know or can easily imagine, there should have been three major steps in the prehistory of tool-making. The first step would have been simple _utilization_ of what was at hand. This is the -step into which the �eoliths� would fall. The second step would have +step into which the �eoliths� would fall. The second step would have been _fashioning_--the haphazard preparation of a tool when there was a need for it. Probably many of the earlier pebble tools, which I shall describe next, fall into this group. The third step would have been @@ -1447,7 +1447,7 @@ tradition appears. PEBBLE TOOLS -At the beginning of the last chapter, you�ll remember that I said there +At the beginning of the last chapter, you�ll remember that I said there were tools from very early geological beds. The earliest bones of men have not yet been found in such early beds although the Sterkfontein australopithecine cave approaches this early date. The earliest tools @@ -1467,7 +1467,7 @@ Old World besides Africa; in fact, some prehistorians already claim to have identified a few. Since the forms and the distinct ways of making the earlier pebble tools had not yet sufficiently jelled into a set tradition, they are difficult for us to recognize. It is not -so difficult, however, if there are great numbers of �possibles� +so difficult, however, if there are great numbers of �possibles� available. A little later in time the tradition becomes more clearly set, and pebble tools are easier to recognize. So far, really large collections of pebble tools have only been found and examined in Africa. @@ -1475,9 +1475,9 @@ collections of pebble tools have only been found and examined in Africa. CORE-BIFACE TOOLS -The next tradition we�ll look at is the _core_ or biface one. The tools +The next tradition we�ll look at is the _core_ or biface one. The tools are large pear-shaped pieces of stone trimmed flat on the two opposite -sides or �faces.� Hence �biface� has been used to describe these tools. +sides or �faces.� Hence �biface� has been used to describe these tools. The front view is like that of a pear with a rather pointed top, and the back view looks almost exactly the same. Look at them side on, and you can see that the front and back faces are the same and have been @@ -1488,7 +1488,7 @@ illustration. [Illustration: ABBEVILLIAN BIFACE] We have very little idea of the way in which these core-bifaces were -used. They have been called �hand axes,� but this probably gives the +used. They have been called �hand axes,� but this probably gives the wrong idea, for an ax, to us, is not a pointed tool. All of these early tools must have been used for a number of jobs--chopping, scraping, cutting, hitting, picking, and prying. Since the core-bifaces tend to @@ -1505,7 +1505,7 @@ a big block of stone. You had to break off the flake in such a way that it was broad and thin, and also had a good sharp cutting edge. Once you really got on to the trick of doing it, this was probably a simpler way to make a good cutting tool than preparing a biface. You have to know -how, though; I�ve tried it and have mashed my fingers more than once. +how, though; I�ve tried it and have mashed my fingers more than once. The flake tools look as if they were meant mainly for chopping, scraping, and cutting jobs. When one made a flake tool, the idea seems @@ -1535,9 +1535,9 @@ tradition. It probably has its earliest roots in the pebble tool tradition of African type. There are several kinds of tools in this tradition, but all differ from the western core-bifaces and flakes. There are broad, heavy scrapers or cleavers, and tools with an -adze-like cutting edge. These last-named tools are called �hand adzes,� -just as the core-bifaces of the west have often been called �hand -axes.� The section of an adze cutting edge is ? shaped; the section of +adze-like cutting edge. These last-named tools are called �hand adzes,� +just as the core-bifaces of the west have often been called �hand +axes.� The section of an adze cutting edge is ? shaped; the section of an ax is < shaped. [Illustration: ANYATHIAN ADZE-LIKE TOOL] @@ -1581,17 +1581,17 @@ stratification.[3] Soan (India) Flake: - �Typical Mousterian� + �Typical Mousterian� Levalloiso-Mousterian Levalloisian Tayacian Clactonian (localized in England) Core-biface: - Some blended elements in �Mousterian� + Some blended elements in �Mousterian� Micoquian (= Acheulean 6 and 7) Acheulean - Abbevillian (once called �Chellean�) + Abbevillian (once called �Chellean�) Pebble tool: Oldowan @@ -1608,8 +1608,8 @@ out of glacial gravels the easiest thing to do first is to isolate individual types of tools into groups. First you put a bushel-basketful of tools on a table and begin matching up types. Then you give names to the groups of each type. The groups and the types are really matters of -the archeologists� choice; in real life, they were probably less exact -than the archeologists� lists of them. We now know pretty well in which +the archeologists� choice; in real life, they were probably less exact +than the archeologists� lists of them. We now know pretty well in which of the early traditions the various early groups belong. @@ -1635,9 +1635,9 @@ production must have been passed on from one generation to another. I could even guess that the notions of the ideal type of one or the other of these tools stood out in the minds of men of those times -somewhat like a symbol of �perfect tool for good job.� If this were -so--remember it�s only a wild guess of mine--then men were already -symbol users. Now let�s go on a further step to the fact that the words +somewhat like a symbol of �perfect tool for good job.� If this were +so--remember it�s only a wild guess of mine--then men were already +symbol users. Now let�s go on a further step to the fact that the words men speak are simply sounds, each different sound being a symbol for a different meaning. If standardized tool-making suggests symbol-making, is it also possible that crude word-symbols were also being made? I @@ -1650,7 +1650,7 @@ of our second step is more suggestive, although we may not yet feel sure that many of the earlier pebble tools were man-made products. But with the step to standardization and the appearance of the traditions, I believe we must surely be dealing with the traces of culture-bearing -_men_. The �conventional understandings� which Professor Redfield�s +_men_. The �conventional understandings� which Professor Redfield�s definition of culture suggests are now evidenced for us in the persistent habits for the preparation of stone tools. Were we able to see the other things these prehistoric men must have made--in materials @@ -1666,19 +1666,19 @@ In the last chapter, I told you that many of the older archeologists and human paleontologists used to think that modern man was very old. The supposed ages of Piltdown and Galley Hill were given as evidence of the great age of anatomically modern man, and some interpretations -of the Swanscombe and Font�chevade fossils were taken to support +of the Swanscombe and Font�chevade fossils were taken to support this view. The conclusion was that there were two parallel lines or -�phyla� of men already present well back in the Pleistocene. The -first of these, the more primitive or �paleoanthropic� line, was +�phyla� of men already present well back in the Pleistocene. The +first of these, the more primitive or �paleoanthropic� line, was said to include Heidelberg, the proto-neanderthaloids and classic -Neanderthal. The more anatomically modern or �neanthropic� line was +Neanderthal. The more anatomically modern or �neanthropic� line was thought to consist of Piltdown and the others mentioned above. The Neanderthaler or paleoanthropic line was thought to have become extinct after the first phase of the last great glaciation. Of course, the modern or neanthropic line was believed to have persisted into the -present, as the basis for the world�s population today. But with +present, as the basis for the world�s population today. But with Piltdown liquidated, Galley Hill known to be very late, and Swanscombe -and Font�chevade otherwise interpreted, there is little left of the +and Font�chevade otherwise interpreted, there is little left of the so-called parallel phyla theory. While the theory was in vogue, however, and as long as the European @@ -1695,9 +1695,9 @@ where they had actually been dropped by the men who made and used them. The tools came, rather, from the secondary hodge-podge of the glacial gravels. I tried to give you a picture of the bulldozing action of glaciers (p. 40) and of the erosion and weathering that were -side-effects of a glacially conditioned climate on the earth�s surface. +side-effects of a glacially conditioned climate on the earth�s surface. As we said above, if one simply plucks tools out of the redeposited -gravels, his natural tendency is to �type� the tools by groups, and to +gravels, his natural tendency is to �type� the tools by groups, and to think that the groups stand for something _on their own_. In 1906, M. Victor Commont actually made a rare find of what seems @@ -1705,15 +1705,15 @@ to have been a kind of workshop site, on a terrace above the Somme river in France. Here, Commont realized, flake tools appeared clearly in direct association with core-biface tools. Few prehistorians paid attention to Commont or his site, however. It was easier to believe -that flake tools represented a distinct �culture� and that this -�culture� was that of the Neanderthaler or paleoanthropic line, and -that the core-bifaces stood for another �culture� which was that of the +that flake tools represented a distinct �culture� and that this +�culture� was that of the Neanderthaler or paleoanthropic line, and +that the core-bifaces stood for another �culture� which was that of the supposed early modern or neanthropic line. Of course, I am obviously skipping many details here. Some later sites with Neanderthal fossils do seem to have only flake tools, but other such sites have both types of tools. The flake tools which appeared _with_ the core-bifaces in the Swanscombe gravels were never made much of, although it -was embarrassing for the parallel phyla people that Font�chevade +was embarrassing for the parallel phyla people that Font�chevade ran heavily to flake tools. All in all, the parallel phyla theory flourished because it seemed so neat and easy to understand. @@ -1722,20 +1722,20 @@ TRADITIONS ARE TOOL-MAKING HABITS, NOT CULTURES In case you think I simply enjoy beating a dead horse, look in any standard book on prehistory written twenty (or even ten) years ago, or -in most encyclopedias. You�ll find that each of the individual tool -types, of the West, at least, was supposed to represent a �culture.� -The �cultures� were believed to correspond to parallel lines of human +in most encyclopedias. You�ll find that each of the individual tool +types, of the West, at least, was supposed to represent a �culture.� +The �cultures� were believed to correspond to parallel lines of human evolution. In 1937, Mr. Harper Kelley strongly re-emphasized the importance -of Commont�s workshop site and the presence of flake tools with -core-bifaces. Next followed Dr. Movius� clear delineation of the +of Commont�s workshop site and the presence of flake tools with +core-bifaces. Next followed Dr. Movius� clear delineation of the chopper-chopping tool tradition of the Far East. This spoiled the nice symmetry of the flake-tool = paleoanthropic, core-biface = neanthropic equations. Then came increasing understanding of the importance of the pebble tools in Africa, and the location of several more workshop sites there, especially at Olorgesailie in Kenya. Finally came the -liquidation of Piltdown and the deflation of Galley Hill�s date. So it +liquidation of Piltdown and the deflation of Galley Hill�s date. So it is at last possible to picture an individual prehistoric man making a flake tool to do one job and a core-biface tool to do another. Commont showed us this picture in 1906, but few believed him. @@ -1751,7 +1751,7 @@ that of the cave on Mount Carmel in Palestine, where the blended pre-neanderthaloid, 70 per cent modern-type skulls were found. Here, in the same level with the skulls, were 9,784 flint tools. Of these, only three--doubtless strays--were core-bifaces; all the rest were flake -tools or flake chips. We noted above how the Font�chevade cave ran to +tools or flake chips. We noted above how the Font�chevade cave ran to flake tools. The only conclusion I would draw from this is that times and circumstances did exist in which prehistoric men needed only flake tools. So they only made flake tools for those particular times and @@ -1773,13 +1773,13 @@ piece of bone. From the gravels which yield the Clactonian flakes of England comes the fire-hardened point of a wooden spear. There are also the chance finds of the fossil human bones themselves, of which we spoke in the last chapter. Aside from the cave of Peking man, none -of the earliest tools have been found in caves. Open air or �workshop� +of the earliest tools have been found in caves. Open air or �workshop� sites which do not seem to have been disturbed later by some geological agency are very rare. The chart on page 65 shows graphically what the situation in west-central Europe seems to have been. It is not yet certain whether -there were pebble tools there or not. The Font�chevade cave comes +there were pebble tools there or not. The Font�chevade cave comes into the picture about 100,000 years ago or more. But for the earlier hundreds of thousands of years--below the red-dotted line on the chart--the tools we find come almost entirely from the haphazard @@ -1790,13 +1790,13 @@ kinds of all-purpose tools. Almost any one of them could be used for hacking, chopping, cutting, and scraping; so the men who used them must have been living in a rough and ready sort of way. They found or hunted their food wherever they could. In the anthropological jargon, they -were �food-gatherers,� pure and simple. +were �food-gatherers,� pure and simple. Because of the mixture in the gravels and in the materials they -carried, we can�t be sure which animals these men hunted. Bones of +carried, we can�t be sure which animals these men hunted. Bones of the larger animals turn up in the gravels, but they could just as well belong to the animals who hunted the men, rather than the other -way about. We don�t know. This is why camp sites like Commont�s and +way about. We don�t know. This is why camp sites like Commont�s and Olorgesailie in Kenya are so important when we do find them. The animal bones at Olorgesailie belonged to various mammals of extremely large size. Probably they were taken in pit-traps, but there are a number of @@ -1809,18 +1809,18 @@ animal. Professor F. Clark Howell recently returned from excavating another important open air site at Isimila in Tanganyika. The site yielded the bones of many fossil animals and also thousands of core-bifaces, -flakes, and choppers. But Howell�s reconstruction of the food-getting -habits of the Isimila people certainly suggests that the word �hunting� -is too dignified for what they did; �scavenging� would be much nearer +flakes, and choppers. But Howell�s reconstruction of the food-getting +habits of the Isimila people certainly suggests that the word �hunting� +is too dignified for what they did; �scavenging� would be much nearer the mark. During a great part of this time the climate was warm and pleasant. The second interglacial period (the time between the second and third great alpine glaciations) lasted a long time, and during much of this time -the climate may have been even better than ours is now. We don�t know +the climate may have been even better than ours is now. We don�t know that earlier prehistoric men in Europe or Africa lived in caves. They may not have needed to; much of the weather may have been so nice that -they lived in the open. Perhaps they didn�t wear clothes, either. +they lived in the open. Perhaps they didn�t wear clothes, either. WHAT THE PEKING CAVE-FINDS TELL US @@ -1832,7 +1832,7 @@ were bones of dangerous animals, members of the wolf, bear, and cat families. Some of the cat bones belonged to beasts larger than tigers. There were also bones of other wild animals: buffalo, camel, deer, elephants, horses, sheep, and even ostriches. Seventy per cent of the -animals Peking man killed were fallow deer. It�s much too cold and dry +animals Peking man killed were fallow deer. It�s much too cold and dry in north China for all these animals to live there today. So this list helps us know that the weather was reasonably warm, and that there was enough rain to grow grass for the grazing animals. The list also helps @@ -1840,7 +1840,7 @@ the paleontologists to date the find. Peking man also seems to have eaten plant food, for there are hackberry seeds in the debris of the cave. His tools were made of sandstone and -quartz and sometimes of a rather bad flint. As we�ve already seen, they +quartz and sometimes of a rather bad flint. As we�ve already seen, they belong in the chopper-tool tradition. It seems fairly clear that some of the edges were chipped by right-handed people. There are also many split pieces of heavy bone. Peking man probably split them so he could @@ -1850,10 +1850,10 @@ Many of these split bones were the bones of Peking men. Each one of the skulls had already had the base broken out of it. In no case were any of the bones resting together in their natural relation to one another. There is nothing like a burial; all of the bones are scattered. Now -it�s true that animals could have scattered bodies that were not cared +it�s true that animals could have scattered bodies that were not cared for or buried. But splitting bones lengthwise and carefully removing the base of a skull call for both the tools and the people to use them. -It�s pretty clear who the people were. Peking man was a cannibal. +It�s pretty clear who the people were. Peking man was a cannibal. * * * * * @@ -1862,8 +1862,8 @@ prehistoric men. In those days life was rough. You evidently had to watch out not only for dangerous animals but also for your fellow men. You ate whatever you could catch or find growing. But you had sense enough to build fires, and you had already formed certain habits for -making the kinds of stone tools you needed. That�s about all we know. -But I think we�ll have to admit that cultural beginnings had been made, +making the kinds of stone tools you needed. That�s about all we know. +But I think we�ll have to admit that cultural beginnings had been made, and that these early people were really _men_. @@ -1876,16 +1876,16 @@ MORE EVIDENCE of Culture While the dating is not yet sure, the material that we get from caves in Europe must go back to about 100,000 years ago; the time of the -classic Neanderthal group followed soon afterwards. We don�t know why +classic Neanderthal group followed soon afterwards. We don�t know why there is no earlier material in the caves; apparently they were not used before the last interglacial phase (the period just before the last great glaciation). We know that men of the classic Neanderthal group were living in caves from about 75,000 to 45,000 years ago. New radioactive carbon dates even suggest that some of the traces of -culture we�ll describe in this chapter may have lasted to about 35,000 +culture we�ll describe in this chapter may have lasted to about 35,000 years ago. Probably some of the pre-neanderthaloid types of men had also lived in caves. But we have so far found their bones in caves only -in Palestine and at Font�chevade. +in Palestine and at Font�chevade. THE CAVE LAYERS @@ -1893,7 +1893,7 @@ THE CAVE LAYERS In parts of France, some peasants still live in caves. In prehistoric time, many generations of people lived in them. As a result, many caves have deep layers of debris. The first people moved in and lived -on the rock floor. They threw on the floor whatever they didn�t want, +on the rock floor. They threw on the floor whatever they didn�t want, and they tracked in mud; nobody bothered to clean house in those days. Their debris--junk and mud and garbage and what not--became packed into a layer. As time went on, and generations passed, the layer grew @@ -1910,20 +1910,20 @@ earliest to latest. This is the _stratification_ we talked about (p. [Illustration: SECTION OF SHELTER ON LOWER TERRACE, LE MOUSTIER] -While we may find a mix-up in caves, it�s not nearly as bad as the +While we may find a mix-up in caves, it�s not nearly as bad as the mixing up that was done by glaciers. The animal bones and shells, the fireplaces, the bones of men, and the tools the men made all belong -together, if they come from one layer. That�s the reason why the cave +together, if they come from one layer. That�s the reason why the cave of Peking man is so important. It is also the reason why the caves in Europe and the Near East are so important. We can get an idea of which things belong together and which lot came earliest and which latest. In most cases, prehistoric men lived only in the mouths of caves. -They didn�t like the dark inner chambers as places to live in. They +They didn�t like the dark inner chambers as places to live in. They preferred rock-shelters, at the bases of overhanging cliffs, if there was enough overhang to give shelter. When the weather was good, they no -doubt lived in the open air as well. I�ll go on using the term �cave� -since it�s more familiar, but remember that I really mean rock-shelter, +doubt lived in the open air as well. I�ll go on using the term �cave� +since it�s more familiar, but remember that I really mean rock-shelter, as a place in which people actually lived. The most important European cave sites are in Spain, France, and @@ -1933,29 +1933,29 @@ found when the out-of-the-way parts of Europe, Africa, and Asia are studied. -AN �INDUSTRY� DEFINED +AN �INDUSTRY� DEFINED We have already seen that the earliest European cave materials are -those from the cave of Font�chevade. Movius feels certain that the +those from the cave of Font�chevade. Movius feels certain that the lowest materials here date back well into the third interglacial stage, -that which lay between the Riss (next to the last) and the W�rm I +that which lay between the Riss (next to the last) and the W�rm I (first stage of the last) alpine glaciations. This material consists of an _industry_ of stone tools, apparently all made in the flake -tradition. This is the first time we have used the word �industry.� +tradition. This is the first time we have used the word �industry.� It is useful to call all of the different tools found together in one layer and made of _one kind of material_ an industry; that is, the tools must be found together as men left them. Tools taken from the glacial gravels (or from windswept desert surfaces or river gravels -or any geological deposit) are not �together� in this sense. We might -say the latter have only �geological,� not �archeological� context. +or any geological deposit) are not �together� in this sense. We might +say the latter have only �geological,� not �archeological� context. Archeological context means finding things just as men left them. We -can tell what tools go together in an �industrial� sense only if we +can tell what tools go together in an �industrial� sense only if we have archeological context. -Up to now, the only things we could have called �industries� were the +Up to now, the only things we could have called �industries� were the worked stone industry and perhaps the worked (?) bone industry of the Peking cave. We could add some of the very clear cases of open air -sites, like Olorgesailie. We couldn�t use the term for the stone tools +sites, like Olorgesailie. We couldn�t use the term for the stone tools from the glacial gravels, because we do not know which tools belonged together. But when the cave materials begin to appear in Europe, we can begin to speak of industries. Most of the European caves of this time @@ -1964,16 +1964,16 @@ contain industries of flint tools alone. THE EARLIEST EUROPEAN CAVE LAYERS -We�ve just mentioned the industry from what is said to be the oldest +We�ve just mentioned the industry from what is said to be the oldest inhabited cave in Europe; that is, the industry from the deepest layer -of the site at Font�chevade. Apparently it doesn�t amount to much. The +of the site at Font�chevade. Apparently it doesn�t amount to much. The tools are made of stone, in the flake tradition, and are very poorly worked. This industry is called _Tayacian_. Its type tool seems to be a smallish flake tool, but there are also larger flakes which seem to have been fashioned for hacking. In fact, the type tool seems to be simply a smaller edition of the Clactonian tool (pictured on p. 45). -None of the Font�chevade tools are really good. There are scrapers, +None of the Font�chevade tools are really good. There are scrapers, and more or less pointed tools, and tools that may have been used for hacking and chopping. Many of the tools from the earlier glacial gravels are better made than those of this first industry we see in @@ -2005,7 +2005,7 @@ core-biface and the flake traditions. The core-biface tools usually make up less than half of all the tools in the industry. However, the name of the biface type of tool is generally given to the whole industry. It is called the _Acheulean_, actually a late form of it, as -�Acheulean� is also used for earlier core-biface tools taken from the +�Acheulean� is also used for earlier core-biface tools taken from the glacial gravels. In western Europe, the name used is _Upper Acheulean_ or _Micoquian_. The same terms have been borrowed to name layers E and F in the Tabun cave, on Mount Carmel in Palestine. @@ -2029,7 +2029,7 @@ those used for at least one of the flake industries we shall mention presently. There is very little else in these early cave layers. We do not have -a proper �industry� of bone tools. There are traces of fire, and of +a proper �industry� of bone tools. There are traces of fire, and of animal bones, and a few shells. In Palestine, there are many more bones of deer than of gazelle in these layers; the deer lives in a wetter climate than does the gazelle. In the European cave layers, the @@ -2043,18 +2043,18 @@ bones of fossil men definitely in place with this industry. FLAKE INDUSTRIES FROM THE CAVES Two more stone industries--the _Levalloisian_ and the -�_Mousterian_�--turn up at approximately the same time in the European +�_Mousterian_�--turn up at approximately the same time in the European cave layers. Their tools seem to be mainly in the flake tradition, but according to some of the authorities their preparation also shows some combination with the habits by which the core-biface tools were prepared. -Now notice that I don�t tell you the Levalloisian and the �Mousterian� +Now notice that I don�t tell you the Levalloisian and the �Mousterian� layers are both above the late Acheulean layers. Look at the cave -section (p. 57) and you�ll find that some �Mousterian of Acheulean -tradition� appears above some �typical Mousterian.� This means that +section (p. 57) and you�ll find that some �Mousterian of Acheulean +tradition� appears above some �typical Mousterian.� This means that there may be some kinds of Acheulean industries that are later than -some kinds of �Mousterian.� The same is true of the Levalloisian. +some kinds of �Mousterian.� The same is true of the Levalloisian. There were now several different kinds of habits that men used in making stone tools. These habits were based on either one or the other @@ -2072,7 +2072,7 @@ were no patent laws in those days. The extremely complicated interrelationships of the different habits used by the tool-makers of this range of time are at last being -systematically studied. M. Fran�ois Bordes has developed a statistical +systematically studied. M. Fran�ois Bordes has developed a statistical method of great importance for understanding these tool preparation habits. @@ -2081,22 +2081,22 @@ THE LEVALLOISIAN AND MOUSTERIAN The easiest Levalloisian tool to spot is a big flake tool. The trick in making it was to fashion carefully a big chunk of stone (called -the Levalloisian �tortoise core,� because it resembles the shape of +the Levalloisian �tortoise core,� because it resembles the shape of a turtle-shell) and then to whack this in such a way that a large flake flew off. This large thin flake, with sharp cutting edges, is the finished Levalloisian tool. There were various other tools in a Levalloisian industry, but this is the characteristic _Levalloisian_ tool. -There are several �typical Mousterian� stone tools. Different from -the tools of the Levalloisian type, these were made from �disc-like -cores.� There are medium-sized flake �side scrapers.� There are also -some small pointed tools and some small �hand axes.� The last of these +There are several �typical Mousterian� stone tools. Different from +the tools of the Levalloisian type, these were made from �disc-like +cores.� There are medium-sized flake �side scrapers.� There are also +some small pointed tools and some small �hand axes.� The last of these tool types is often a flake worked on both of the flat sides (that is, bifacially). There are also pieces of flint worked into the form of crude balls. The pointed tools may have been fixed on shafts to make short jabbing spears; the round flint balls may have been used as -bolas. Actually, we don�t _know_ what either tool was used for. The +bolas. Actually, we don�t _know_ what either tool was used for. The points and side scrapers are illustrated (pp. 64 and 66). [Illustration: LEVALLOIS FLAKE] @@ -2108,9 +2108,9 @@ Nowadays the archeologists are less and less sure of the importance of any one specific tool type and name. Twenty years ago, they used to speak simply of Acheulean or Levalloisian or Mousterian tools. Now, more and more, _all_ of the tools from some one layer in a -cave are called an �industry,� which is given a mixed name. Thus we -have �Levalloiso-Mousterian,� and �Acheuleo-Levalloisian,� and even -�Acheuleo-Mousterian� (or �Mousterian of Acheulean tradition�). Bordes� +cave are called an �industry,� which is given a mixed name. Thus we +have �Levalloiso-Mousterian,� and �Acheuleo-Levalloisian,� and even +�Acheuleo-Mousterian� (or �Mousterian of Acheulean tradition�). Bordes� systematic work is beginning to clear up some of our confusion. The time of these late Acheuleo-Levalloiso-Mousterioid industries @@ -2120,16 +2120,16 @@ phase of the last great glaciation. It was also the time that the classic group of Neanderthal men was living in Europe. A number of the Neanderthal fossil finds come from these cave layers. Before the different habits of tool preparation were understood it used to be -popular to say Neanderthal man was �Mousterian man.� I think this is -wrong. What used to be called �Mousterian� is now known to be a variety +popular to say Neanderthal man was �Mousterian man.� I think this is +wrong. What used to be called �Mousterian� is now known to be a variety of industries with tools of both core-biface and flake habits, and -so mixed that the word �Mousterian� used alone really doesn�t mean +so mixed that the word �Mousterian� used alone really doesn�t mean anything. The Neanderthalers doubtless understood the tool preparation habits by means of which Acheulean, Levalloisian and Mousterian type tools were produced. We also have the more modern-like Mount Carmel people, found in a cave layer of Palestine with tools almost entirely -in the flake tradition, called �Levalloiso-Mousterian,� and the -Font�chevade-Tayacian (p. 59). +in the flake tradition, called �Levalloiso-Mousterian,� and the +Font�chevade-Tayacian (p. 59). [Illustration: MOUSTERIAN POINT] @@ -2165,7 +2165,7 @@ which seem to have served as anvils or chopping blocks, are fairly common. Bits of mineral, used as coloring matter, have also been found. We -don�t know what the color was used for. +don�t know what the color was used for. [Illustration: MOUSTERIAN SIDE SCRAPER] @@ -2230,7 +2230,7 @@ might suggest some notion of hoarding up the spirits or the strength of bears killed in the hunt. Probably the people lived in small groups, as hunting and food-gathering seldom provide enough food for large groups of people. These groups probably had some kind of leader or -�chief.� Very likely the rude beginnings of rules for community life +�chief.� Very likely the rude beginnings of rules for community life and politics, and even law, were being made. But what these were, we do not know. We can only guess about such things, as we can only guess about many others; for example, how the idea of a family must have been @@ -2246,8 +2246,8 @@ small. The mixtures and blendings of the habits used in making stone tools must mean that there were also mixtures and blends in many of the other ideas and beliefs of these small groups. And what this probably means is that there was no one _culture_ of the time. It is -certainly unlikely that there were simply three cultures, �Acheulean,� -�Levalloisian,� and �Mousterian,� as has been thought in the past. +certainly unlikely that there were simply three cultures, �Acheulean,� +�Levalloisian,� and �Mousterian,� as has been thought in the past. Rather there must have been a great variety of loosely related cultures at about the same stage of advancement. We could say, too, that here we really begin to see, for the first time, that remarkable ability @@ -2272,7 +2272,7 @@ related habits for the making of tools. But the men who made them must have looked much like the men of the West. Their tools were different, but just as useful. -As to what the men of the West looked like, I�ve already hinted at all +As to what the men of the West looked like, I�ve already hinted at all we know so far (pp. 29 ff.). The Neanderthalers were present at the time. Some more modern-like men must have been about, too, since fossils of them have turned up at Mount Carmel in Palestine, and at @@ -2306,7 +2306,7 @@ A NEW TRADITION APPEARS Something new was probably beginning to happen in the European-Mediterranean area about 40,000 years ago, though all the rest of the Old World seems to have been going on as it had been. I -can�t be sure of this because the information we are using as a basis +can�t be sure of this because the information we are using as a basis for dates is very inaccurate for the areas outside of Europe and the Mediterranean. @@ -2325,7 +2325,7 @@ drawing shows. It has sharp cutting edges, and makes a very useful knife. The real trick is to be able to make one. It is almost impossible to make a blade out of any stone but flint or a natural volcanic glass called obsidian. And even if you have flint or obsidian, -you first have to work up a special cone-shaped �blade-core,� from +you first have to work up a special cone-shaped �blade-core,� from which to whack off blades. [Illustration: PLAIN BLADE] @@ -2351,8 +2351,8 @@ found in equally early cave levels in Syria; their popularity there seems to fluctuate a bit. Some more or less parallel-sided flakes are known in the Levalloisian industry in France, but they are probably no earlier than Tabun E. The Tabun blades are part of a local late -�Acheulean� industry, which is characterized by core-biface �hand -axes,� but which has many flake tools as well. Professor F. E. +�Acheulean� industry, which is characterized by core-biface �hand +axes,� but which has many flake tools as well. Professor F. E. Zeuner believes that this industry may be more than 120,000 years old; actually its date has not yet been fixed, but it is very old--older than the fossil finds of modern-like men in the same caves. @@ -2371,7 +2371,7 @@ We are not sure just where the earliest _persisting_ habits for the production of blade tools developed. Impressed by the very early momentary appearance of blades at Tabun on Mount Carmel, Professor Dorothy A. Garrod first favored the Near East as a center of origin. -She spoke of �some as yet unidentified Asiatic centre,� which she +She spoke of �some as yet unidentified Asiatic centre,� which she thought might be in the highlands of Iran or just beyond. But more recent work has been done in this area, especially by Professor Coon, and the blade tools do not seem to have an early appearance there. When @@ -2395,21 +2395,21 @@ core (and the striking of the Levalloisian flake from it) might have followed through to the conical core and punch technique for the production of blades. Professor Garrod is much impressed with the speed of change during the later phases of the last glaciation, and its -probable consequences. She speaks of �the greater number of industries +probable consequences. She speaks of �the greater number of industries having enough individual character to be classified as distinct ... -since evolution now starts to outstrip diffusion.� Her �evolution� here +since evolution now starts to outstrip diffusion.� Her �evolution� here is of course an industrial evolution rather than a biological one. Certainly the people of Europe had begun to make blade tools during the warm spell after the first phase of the last glaciation. By about 40,000 years ago blades were well established. The bones of the blade -tool makers we�ve found so far indicate that anatomically modern men +tool makers we�ve found so far indicate that anatomically modern men had now certainly appeared. Unfortunately, only a few fossil men have so far been found from the very beginning of the blade tool range in Europe (or elsewhere). What I certainly shall _not_ tell you is that conquering bands of fine, strong, anatomically modern men, armed with superior blade tools, came sweeping out of the East to exterminate the -lowly Neanderthalers. Even if we don�t know exactly what happened, I�d -lay a good bet it wasn�t that simple. +lowly Neanderthalers. Even if we don�t know exactly what happened, I�d +lay a good bet it wasn�t that simple. We do know a good deal about different blade industries in Europe. Almost all of them come from cave layers. There is a great deal of @@ -2418,7 +2418,7 @@ this complication; in fact, it doubtless simplifies it too much. But it may suggest all the complication of industries which is going on at this time. You will note that the upper portion of my much simpler chart (p. 65) covers the same material (in the section -marked �Various Blade-Tool Industries�). That chart is certainly too +marked �Various Blade-Tool Industries�). That chart is certainly too simplified. You will realize that all this complication comes not only from @@ -2429,7 +2429,7 @@ a good deal of climatic change at this time. The plants and animals that men used for food were changing, too. The great variety of tools and industries we now find reflect these changes and the ability of men to keep up with the times. Now, for example, is the first time we are -sure that there are tools to _make_ other tools. They also show men�s +sure that there are tools to _make_ other tools. They also show men�s increasing ability to adapt themselves. @@ -2437,15 +2437,15 @@ SPECIAL TYPES OF BLADE TOOLS The most useful tools that appear at this time were made from blades. - 1. The �backed� blade. This is a knife made of a flint blade, with - one edge purposely blunted, probably to save the user�s fingers + 1. The �backed� blade. This is a knife made of a flint blade, with + one edge purposely blunted, probably to save the user�s fingers from being cut. There are several shapes of backed blades (p. 73). [Illustration: TWO BURINS] - 2. The _burin_ or �graver.� The burin was the original chisel. Its - cutting edge is _transverse_, like a chisel�s. Some burins are + 2. The _burin_ or �graver.� The burin was the original chisel. Its + cutting edge is _transverse_, like a chisel�s. Some burins are made like a screw-driver, save that burins are sharp. Others have edges more like the blade of a chisel or a push plane, with only one bevel. Burins were probably used to make slots in wood @@ -2456,29 +2456,29 @@ The most useful tools that appear at this time were made from blades. [Illustration: TANGED POINT] - 3. The �tanged� point. These stone points were used to tip arrows or + 3. The �tanged� point. These stone points were used to tip arrows or light spears. They were made from blades, and they had a long tang at the bottom where they were fixed to the shaft. At the place where the tang met the main body of the stone point, there was - a marked �shoulder,� the beginnings of a barb. Such points had + a marked �shoulder,� the beginnings of a barb. Such points had either one or two shoulders. [Illustration: NOTCHED BLADE] - 4. The �notched� or �strangulated� blade. Along with the points for + 4. The �notched� or �strangulated� blade. Along with the points for arrows or light spears must go a tool to prepare the arrow or - spear shaft. Today, such a tool would be called a �draw-knife� or - a �spoke-shave,� and this is what the notched blades probably are. + spear shaft. Today, such a tool would be called a �draw-knife� or + a �spoke-shave,� and this is what the notched blades probably are. Our spoke-shaves have sharp straight cutting blades and really - �shave.� Notched blades of flint probably scraped rather than cut. + �shave.� Notched blades of flint probably scraped rather than cut. - 5. The �awl,� �drill,� or �borer.� These blade tools are worked out + 5. The �awl,� �drill,� or �borer.� These blade tools are worked out to a spike-like point. They must have been used for making holes in wood, bone, shell, skin, or other things. [Illustration: DRILL OR AWL] - 6. The �end-scraper on a blade� is a tool with one or both ends + 6. The �end-scraper on a blade� is a tool with one or both ends worked so as to give a good scraping edge. It could have been used to hollow out wood or bone, scrape hides, remove bark from trees, and a number of other things (p. 78). @@ -2489,11 +2489,11 @@ usually made of blades, but the best examples are so carefully worked on both sides (bifacially) that it is impossible to see the original blade. This tool is - 7. The �laurel leaf� point. Some of these tools were long and + 7. The �laurel leaf� point. Some of these tools were long and dagger-like, and must have been used as knives or daggers. Others - were small, called �willow leaf,� and must have been mounted on + were small, called �willow leaf,� and must have been mounted on spear or arrow shafts. Another typical Solutrean tool is the - �shouldered� point. Both the �laurel leaf� and �shouldered� point + �shouldered� point. Both the �laurel leaf� and �shouldered� point types are illustrated (see above and p. 79). [Illustration: END-SCRAPER ON A BLADE] @@ -2507,17 +2507,17 @@ second is a core tool. [Illustration: SHOULDERED POINT] - 8. The �keel-shaped round scraper� is usually small and quite round, + 8. The �keel-shaped round scraper� is usually small and quite round, and has had chips removed up to a peak in the center. It is called - �keel-shaped� because it is supposed to look (when upside down) + �keel-shaped� because it is supposed to look (when upside down) like a section through a boat. Actually, it looks more like a tent or an umbrella. Its outer edges are sharp all the way around, and it was probably a general purpose scraping tool (see illustration, p. 81). - 9. The �keel-shaped nosed scraper� is a much larger and heavier tool + 9. The �keel-shaped nosed scraper� is a much larger and heavier tool than the round scraper. It was made on a core with a flat bottom, - and has one nicely worked end or �nose.� Such tools are usually + and has one nicely worked end or �nose.� Such tools are usually large enough to be easily grasped, and probably were used like push planes (see illustration, p. 81). @@ -2530,7 +2530,7 @@ the most easily recognized blade tools, although they show differences in detail at different times. There are also many other kinds. Not all of these tools appear in any one industry at one time. Thus the different industries shown in the chart (p. 72) each have only some -of the blade tools we�ve just listed, and also a few flake tools. Some +of the blade tools we�ve just listed, and also a few flake tools. Some industries even have a few core tools. The particular types of blade tools appearing in one cave layer or another, and the frequency of appearance of the different types, tell which industry we have in each @@ -2545,15 +2545,15 @@ to appear. There are knives, pins, needles with eyes, and little double-pointed straight bars of bone that were probably fish-hooks. The fish-line would have been fastened in the center of the bar; when the fish swallowed the bait, the bar would have caught cross-wise in the -fish�s mouth. +fish�s mouth. One quite special kind of bone tool is a long flat point for a light spear. It has a deep notch cut up into the breadth of its base, and is -called a �split-based bone point� (p. 82). We know examples of bone +called a �split-based bone point� (p. 82). We know examples of bone beads from these times, and of bone handles for flint tools. Pierced teeth of some animals were worn as beads or pendants, but I am not sure -that elks� teeth were worn this early. There are even spool-shaped -�buttons� or toggles. +that elks� teeth were worn this early. There are even spool-shaped +�buttons� or toggles. [Illustration: SPLIT-BASED BONE POINT] @@ -2595,12 +2595,12 @@ almost to have served as sketch blocks. The surfaces of these various objects may show animals, or rather abstract floral designs, or geometric designs. -[Illustration: �VENUS� FIGURINE FROM WILLENDORF] +[Illustration: �VENUS� FIGURINE FROM WILLENDORF] Some of the movable art is not done on tools. The most remarkable examples of this class are little figures of women. These women seem to be pregnant, and their most female characteristics are much emphasized. -It is thought that these �Venus� or �Mother-goddess� figurines may be +It is thought that these �Venus� or �Mother-goddess� figurines may be meant to show the great forces of nature--fertility and the birth of life. @@ -2616,21 +2616,21 @@ are different styles in the cave art. The really great cave art is pretty well restricted to southern France and Cantabrian (northwestern) Spain. -There are several interesting things about the �Franco-Cantabrian� cave +There are several interesting things about the �Franco-Cantabrian� cave art. It was done deep down in the darkest and most dangerous parts of the caves, although the men lived only in the openings of caves. If you think what they must have had for lights--crude lamps of hollowed stone have been found, which must have burned some kind of oil or grease, with a matted hair or fiber wick--and of the animals that may have -lurked in the caves, you�ll understand the part about danger. Then, -too, we�re sure the pictures these people painted were not simply to be +lurked in the caves, you�ll understand the part about danger. Then, +too, we�re sure the pictures these people painted were not simply to be looked at and admired, for they painted one picture right over other pictures which had been done earlier. Clearly, it was the _act_ of _painting_ that counted. The painter had to go way down into the most mysterious depths of the earth and create an animal in paint. Possibly he believed that by doing this he gained some sort of magic power over the same kind of animal when he hunted it in the open air. It certainly -doesn�t look as if he cared very much about the picture he painted--as +doesn�t look as if he cared very much about the picture he painted--as a finished product to be admired--for he or somebody else soon went down and painted another animal right over the one he had done. @@ -2683,10 +2683,10 @@ it. Their art is another example of the direction the human mind was taking. And when I say human, I mean it in the fullest sense, for this is the time in which fully modern man has appeared. On page 34, we -spoke of the Cro-Magnon group and of the Combe Capelle-Br�nn group of -Caucasoids and of the Grimaldi �Negroids,� who are no longer believed +spoke of the Cro-Magnon group and of the Combe Capelle-Br�nn group of +Caucasoids and of the Grimaldi �Negroids,� who are no longer believed to be Negroid. I doubt that any one of these groups produced most of -the achievements of the times. It�s not yet absolutely sure which +the achievements of the times. It�s not yet absolutely sure which particular group produced the great cave art. The artists were almost certainly a blend of several (no doubt already mixed) groups. The pair of Grimaldians were buried in a grave with a sprinkling of red ochre, @@ -2705,9 +2705,9 @@ also found about the shore of the Mediterranean basin, and it moved into northern Europe as the last glaciation pulled northward. People began making blade tools of very small size. They learned how to chip very slender and tiny blades from a prepared core. Then they made these -little blades into tiny triangles, half-moons (�lunates�), trapezoids, +little blades into tiny triangles, half-moons (�lunates�), trapezoids, and several other geometric forms. These little tools are called -�microliths.� They are so small that most of them must have been fixed +�microliths.� They are so small that most of them must have been fixed in handles or shafts. [Illustration: MICROLITHS @@ -2726,7 +2726,7 @@ One corner of each little triangle stuck out, and the whole thing made a fine barbed harpoon. In historic times in Egypt, geometric trapezoidal microliths were still in use as arrowheads. They were fastened--broad end out--on the end of an arrow shaft. It seems queer -to give an arrow a point shaped like a �T.� Actually, the little points +to give an arrow a point shaped like a �T.� Actually, the little points were very sharp, and must have pierced the hides of animals very easily. We also think that the broader cutting edge of the point may have caused more bleeding than a pointed arrowhead would. In hunting @@ -2739,7 +2739,7 @@ is some evidence that they appear early in the Near East. Their use was very common in northwest Africa but this came later. The microlith makers who reached south Russia and central Europe possibly moved up out of the Near East. Or it may have been the other way around; we -simply don�t yet know. +simply don�t yet know. Remember that the microliths we are talking about here were made from carefully prepared little blades, and are often geometric in outline. @@ -2749,7 +2749,7 @@ even some flake scrapers, in most microlithic industries. I emphasize this bladelet and the geometric character of the microlithic industries of the western Old World, since there has sometimes been confusion in the matter. Sometimes small flake chips, utilized as minute pointed -tools, have been called �microliths.� They may be _microlithic_ in size +tools, have been called �microliths.� They may be _microlithic_ in size in terms of the general meaning of the word, but they do not seem to belong to the sub-tradition of the blade tool preparation habits which we have been discussing here. @@ -2763,10 +2763,10 @@ in western Asia too, and early, although Professor Garrod is no longer sure that the whole tradition originated in the Near East. If you look again at my chart (p. 72) you will note that in western Asia I list some of the names of the western European industries, but with the -qualification �-like� (for example, �Gravettian-like�). The western +qualification �-like� (for example, �Gravettian-like�). The western Asiatic blade-tool industries do vaguely recall some aspects of those of western Europe, but we would probably be better off if we used -completely local names for them. The �Emiran� of my chart is such an +completely local names for them. The �Emiran� of my chart is such an example; its industry includes a long spike-like blade point which has no western European counterpart. @@ -2774,13 +2774,13 @@ When we last spoke of Africa (p. 66), I told you that stone tools there were continuing in the Levalloisian flake tradition, and were becoming smaller. At some time during this process, two new tool types appeared in northern Africa: one was the Aterian point with -a tang (p. 67), and the other was a sort of �laurel leaf� point, -called the �Sbaikian.� These two tool types were both produced from +a tang (p. 67), and the other was a sort of �laurel leaf� point, +called the �Sbaikian.� These two tool types were both produced from flakes. The Sbaikian points, especially, are roughly similar to some of the Solutrean points of Europe. It has been suggested that both the Sbaikian and Aterian points may be seen on their way to France through their appearance in the Spanish cave deposits of Parpallo, but there is -also a rival �pre-Solutrean� in central Europe. We still do not know +also a rival �pre-Solutrean� in central Europe. We still do not know whether there was any contact between the makers of these north African tools and the Solutrean tool-makers. What does seem clear is that the blade-tool tradition itself arrived late in northern Africa. @@ -2788,11 +2788,11 @@ blade-tool tradition itself arrived late in northern Africa. NETHER AFRICA -Blade tools and �laurel leaf� points and some other probably late +Blade tools and �laurel leaf� points and some other probably late stone tool types also appear in central and southern Africa. There are geometric microliths on bladelets and even some coarse pottery in east Africa. There is as yet no good way of telling just where these -items belong in time; in broad geological terms they are �late.� +items belong in time; in broad geological terms they are �late.� Some people have guessed that they are as early as similar European and Near Eastern examples, but I doubt it. The makers of small-sized Levalloisian flake tools occupied much of Africa until very late in @@ -2823,18 +2823,18 @@ ancestors of the American Indians came from Asia. The stone-tool traditions of Europe, Africa, the Near and Middle East, and central Siberia, did _not_ move into the New World. With only a very few special or late exceptions, there are _no_ core-bifaces, -flakes, or blade tools of the Old World. Such things just haven�t been +flakes, or blade tools of the Old World. Such things just haven�t been found here. -This is why I say it�s a shame we don�t know more of the end of the +This is why I say it�s a shame we don�t know more of the end of the chopper-tool tradition in the Far East. According to Weidenreich, the Mongoloids were in the Far East long before the end of the last glaciation. If the genetics of the blood group types do demand a non-Mongoloid ancestry for the American Indians, who else may have been in the Far East 25,000 years ago? We know a little about the habits for making stone tools which these first people brought with them, -and these habits don�t conform with those of the western Old World. -We�d better keep our eyes open for whatever happened to the end of +and these habits don�t conform with those of the western Old World. +We�d better keep our eyes open for whatever happened to the end of the chopper-tool tradition in northern China; already there are hints that it lasted late there. Also we should watch future excavations in eastern Siberia. Perhaps we shall find the chopper-tool tradition @@ -2846,13 +2846,13 @@ THE NEW ERA Perhaps it comes in part from the way I read the evidence and perhaps in part it is only intuition, but I feel that the materials of this chapter suggest a new era in the ways of life. Before about 40,000 -years ago, people simply �gathered� their food, wandering over large +years ago, people simply �gathered� their food, wandering over large areas to scavenge or to hunt in a simple sort of way. But here we -have seen them �settling-in� more, perhaps restricting themselves in +have seen them �settling-in� more, perhaps restricting themselves in their wanderings and adapting themselves to a given locality in more intensive ways. This intensification might be suggested by the word -�collecting.� The ways of life we described in the earlier chapters -were �food-gathering� ways, but now an era of �food-collecting� has +�collecting.� The ways of life we described in the earlier chapters +were �food-gathering� ways, but now an era of �food-collecting� has begun. We shall see further intensifications of it in the next chapter. @@ -2883,8 +2883,8 @@ The last great glaciation of the Ice Age was a two-part affair, with a sub-phase at the end of the second part. In Europe the last sub-phase of this glaciation commenced somewhere around 15,000 years ago. Then the glaciers began to melt back, for the last time. Remember that -Professor Antevs (p. 19) isn�t sure the Ice Age is over yet! This -melting sometimes went by fits and starts, and the weather wasn�t +Professor Antevs (p. 19) isn�t sure the Ice Age is over yet! This +melting sometimes went by fits and starts, and the weather wasn�t always changing for the better; but there was at least one time when European weather was even better than it is now. @@ -2927,16 +2927,16 @@ Sweden. Much of this north European material comes from bogs and swamps where it had become water-logged and has kept very well. Thus we have much more complete _assemblages_[4] than for any time earlier. - [4] �Assemblage� is a useful word when there are different kinds of + [4] �Assemblage� is a useful word when there are different kinds of archeological materials belonging together, from one area and of - one time. An assemblage is made up of a number of �industries� + one time. An assemblage is made up of a number of �industries� (that is, all the tools in chipped stone, all the tools in bone, all the tools in wood, the traces of houses, etc.) and everything else that manages to survive, such as the art, the burials, the bones of the animals used as food, and the traces of plant foods; in fact, everything that has been left to us and can be used to help reconstruct the lives of the people to - whom it once belonged. Our own present-day �assemblage� would be + whom it once belonged. Our own present-day �assemblage� would be the sum total of all the objects in our mail-order catalogues, department stores and supply houses of every sort, our churches, our art galleries and other buildings, together with our roads, @@ -2976,7 +2976,7 @@ found. It seems likely that the Maglemosian bog finds are remains of summer camps, and that in winter the people moved to higher and drier regions. -Childe calls them the �Forest folk�; they probably lived much the +Childe calls them the �Forest folk�; they probably lived much the same sort of life as did our pre-agricultural Indians of the north central states. They hunted small game or deer; they did a great deal of fishing; they collected what plant food they could find. In fact, @@ -3010,7 +3010,7 @@ South of the north European belt the hunting-food-collecting peoples were living on as best they could during this time. One interesting group, which seems to have kept to the regions of sandy soil and scrub forest, made great quantities of geometric microliths. These are the -materials called _Tardenoisian_. The materials of the �Forest folk� of +materials called _Tardenoisian_. The materials of the �Forest folk� of France and central Europe generally are called _Azilian_; Dr. Movius believes the term might best be restricted to the area south of the Loire River. @@ -3032,24 +3032,24 @@ to it than this. Professor Mathiassen of Copenhagen, who knows the archeological remains of this time very well, poses a question. He speaks of the material -as being neither rich nor progressive, in fact �rather stagnant,� but -he goes on to add that the people had a certain �receptiveness� and +as being neither rich nor progressive, in fact �rather stagnant,� but +he goes on to add that the people had a certain �receptiveness� and were able to adapt themselves quickly when the next change did come. -My own understanding of the situation is that the �Forest folk� made +My own understanding of the situation is that the �Forest folk� made nothing as spectacular as had the producers of the earlier Magdalenian assemblage and the Franco-Cantabrian art. On the other hand, they _seem_ to have been making many more different kinds of tools for many more different kinds of tasks than had their Ice Age forerunners. I -emphasize �seem� because the preservation in the Maglemosian bogs +emphasize �seem� because the preservation in the Maglemosian bogs is very complete; certainly we cannot list anywhere near as many different things for earlier times as we did for the Maglemosians (p. 94). I believe this experimentation with all kinds of new tools and gadgets, this intensification of adaptiveness (p. 91), this -�receptiveness,� even if it is still only pointed toward hunting, +�receptiveness,� even if it is still only pointed toward hunting, fishing, and food-collecting, is an important thing. Remember that the only marker we have handy for the _beginning_ of -this tendency toward �receptiveness� and experimentation is the +this tendency toward �receptiveness� and experimentation is the little microlithic blade tools of various geometric forms. These, we saw, began before the last ice had melted away, and they lasted on in use for a very long time. I wish there were a better marker than @@ -3063,7 +3063,7 @@ CHANGES IN OTHER AREAS? All this last section was about Europe. How about the rest of the world when the last glaciers were melting away? -We simply don�t know much about this particular time in other parts +We simply don�t know much about this particular time in other parts of the world except in Europe, the Mediterranean basin and the Middle East. People were certainly continuing to move into the New World by way of Siberia and the Bering Strait about this time. But for the @@ -3075,10 +3075,10 @@ clear information. REAL CHANGE AND PRELUDE IN THE NEAR EAST The appearance of the microliths and the developments made by the -�Forest folk� of northwestern Europe also mark an end. They show us +�Forest folk� of northwestern Europe also mark an end. They show us the terminal phase of the old food-collecting way of life. It grows increasingly clear that at about the same time that the Maglemosian and -other �Forest folk� were adapting themselves to hunting, fishing, and +other �Forest folk� were adapting themselves to hunting, fishing, and collecting in new ways to fit the post-glacial environment, something completely new was being made ready in western Asia. @@ -3098,7 +3098,7 @@ simply gathering or collecting it. When their food-production became reasonably effective, people could and did settle down in village-farming communities. With the appearance of the little farming villages, a new way of life was actually under way. Professor Childe -has good reason to speak of the �food-producing revolution,� for it was +has good reason to speak of the �food-producing revolution,� for it was indeed a revolution. @@ -3117,8 +3117,8 @@ before the _how_ and _why_ answers begin to appear. Anthropologically trained archeologists are fascinated with the cultures of men in times of great change. About ten or twelve thousand years ago, the general level of culture in many parts of the world seems to have been ready -for change. In northwestern Europe, we saw that cultures �changed -just enough so that they would not have to change.� We linked this to +for change. In northwestern Europe, we saw that cultures �changed +just enough so that they would not have to change.� We linked this to environmental changes with the coming of post-glacial times. In western Asia, we archeologists can prove that the food-producing @@ -3155,7 +3155,7 @@ living as the Maglemosians did? These are the questions we still have to face. -CULTURAL �RECEPTIVENESS� AND PROMISING ENVIRONMENTS +CULTURAL �RECEPTIVENESS� AND PROMISING ENVIRONMENTS Until the archeologists and the natural scientists--botanists, geologists, zoologists, and general ecologists--have spent many more @@ -3163,15 +3163,15 @@ years on the problem, we shall not have full _how_ and _why_ answers. I do think, however, that we are beginning to understand what to look for. We shall have to learn much more of what makes the cultures of men -�receptive� and experimental. Did change in the environment alone -force it? Was it simply a case of Professor Toynbee�s �challenge and -response?� I cannot believe the answer is quite that simple. Were it -so simple, we should want to know why the change hadn�t come earlier, +�receptive� and experimental. Did change in the environment alone +force it? Was it simply a case of Professor Toynbee�s �challenge and +response?� I cannot believe the answer is quite that simple. Were it +so simple, we should want to know why the change hadn�t come earlier, along with earlier environmental changes. We shall not know the answer, however, until we have excavated the traces of many more cultures of the time in question. We shall doubtless also have to learn more about, and think imaginatively about, the simpler cultures still left today. -The �mechanics� of culture in general will be bound to interest us. +The �mechanics� of culture in general will be bound to interest us. It will also be necessary to learn much more of the environments of 10,000 to 12,000 years ago. In which regions of the world were the @@ -3228,7 +3228,7 @@ THE OLD THEORY TOO SIMPLE FOR THE FACTS This theory was set up before we really knew anything in detail about the later prehistory of the Near and Middle East. We now know that -the facts which have been found don�t fit the old theory at all well. +the facts which have been found don�t fit the old theory at all well. Also, I have yet to find an American meteorologist who feels that we know enough about the changes in the weather pattern to say that it can have been so simple and direct. And, of course, the glacial ice which @@ -3238,7 +3238,7 @@ of great alpine glaciers, and long periods of warm weather in between. If the rain belt moved north as the glaciers melted for the last time, it must have moved in the same direction in earlier times. Thus, the forced neighborliness of men, plants, and animals in river valleys and -oases must also have happened earlier. Why didn�t domestication happen +oases must also have happened earlier. Why didn�t domestication happen earlier, then? Furthermore, it does not seem to be in the oases and river valleys @@ -3275,20 +3275,20 @@ archeologists, probably through habit, favor an old scheme of Grecized names for the subdivisions: paleolithic, mesolithic, neolithic. I refuse to use these words myself. They have meant too many different things to too many different people and have tended to hide some pretty -fuzzy thinking. Probably you haven�t even noticed my own scheme of -subdivision up to now, but I�d better tell you in general what it is. +fuzzy thinking. Probably you haven�t even noticed my own scheme of +subdivision up to now, but I�d better tell you in general what it is. I think of the earliest great group of archeological materials, from which we can deduce only a food-gathering way of culture, as the -_food-gathering stage_. I say �stage� rather than �age,� because it +_food-gathering stage_. I say �stage� rather than �age,� because it is not quite over yet; there are still a few primitive people in out-of-the-way parts of the world who remain in the _food-gathering stage_. In fact, Professor Julian Steward would probably prefer to call it a food-gathering _level_ of existence, rather than a stage. This would be perfectly acceptable to me. I also tend to find myself using _collecting_, rather than _gathering_, for the more recent aspects or -era of the stage, as the word �collecting� appears to have more sense -of purposefulness and specialization than does �gathering� (see p. +era of the stage, as the word �collecting� appears to have more sense +of purposefulness and specialization than does �gathering� (see p. 91). Now, while I think we could make several possible subdivisions of the @@ -3297,22 +3297,22 @@ believe the only one which means much to us here is the last or _terminal sub-era of food-collecting_ of the whole food-gathering stage. The microliths seem to mark its approach in the northwestern part of the Old World. It is really shown best in the Old World by -the materials of the �Forest folk,� the cultural adaptation to the +the materials of the �Forest folk,� the cultural adaptation to the post-glacial environment in northwestern Europe. We talked about -the �Forest folk� at the beginning of this chapter, and I used the +the �Forest folk� at the beginning of this chapter, and I used the Maglemosian assemblage of Denmark as an example. [5] It is difficult to find words which have a sequence or gradation of meaning with respect to both development and a range of time in the past, or with a range of time from somewhere in the past which is perhaps not yet ended. One standard Webster definition - of _stage_ is: �One of the steps into which the material - development of man ... is divided.� I cannot find any dictionary + of _stage_ is: �One of the steps into which the material + development of man ... is divided.� I cannot find any dictionary definition that suggests which of the words, _stage_ or _era_, has the meaning of a longer span of time. Therefore, I have chosen to let my eras be shorter, and to subdivide my stages - into eras. Webster gives _era_ as: �A signal stage of history, - an epoch.� When I want to subdivide my eras, I find myself using + into eras. Webster gives _era_ as: �A signal stage of history, + an epoch.� When I want to subdivide my eras, I find myself using _sub-eras_. Thus I speak of the _eras_ within a _stage_ and of the _sub-eras_ within an _era_; that is, I do so when I feel that I really have to, and when the evidence is clear enough to @@ -3328,9 +3328,9 @@ realms of culture. It is rather that for most of prehistoric time the materials left to the archeologists tend to limit our deductions to technology and economics. -I�m so soon out of my competence, as conventional ancient history +I�m so soon out of my competence, as conventional ancient history begins, that I shall only suggest the earlier eras of the -food-producing stage to you. This book is about prehistory, and I�m not +food-producing stage to you. This book is about prehistory, and I�m not a universal historian. @@ -3339,28 +3339,28 @@ THE TWO EARLIEST ERAS OF THE FOOD-PRODUCING STAGE The food-producing stage seems to appear in western Asia with really revolutionary suddenness. It is seen by the relative speed with which the traces of new crafts appear in the earliest village-farming -community sites we�ve dug. It is seen by the spread and multiplication +community sites we�ve dug. It is seen by the spread and multiplication of these sites themselves, and the remarkable growth in human -population we deduce from this increase in sites. We�ll look at some +population we deduce from this increase in sites. We�ll look at some of these sites and the archeological traces they yield in the next chapter. When such village sites begin to appear, I believe we are in the _era of the primary village-farming community_. I also believe this is the second era of the food-producing stage. The first era of the food-producing stage, I believe, was an _era of -incipient cultivation and animal domestication_. I keep saying �I -believe� because the actual evidence for this earlier era is so slight +incipient cultivation and animal domestication_. I keep saying �I +believe� because the actual evidence for this earlier era is so slight that one has to set it up mainly by playing a hunch for it. The reason for playing the hunch goes about as follows. One thing we seem to be able to see, in the food-collecting era in general, is a tendency for people to begin to settle down. This settling down seemed to become further intensified in the terminal -era. How this is connected with Professor Mathiassen�s �receptiveness� +era. How this is connected with Professor Mathiassen�s �receptiveness� and the tendency to be experimental, we do not exactly know. The evidence from the New World comes into play here as well as that from the Old World. With this settling down in one place, the people of the -terminal era--especially the �Forest folk� whom we know best--began +terminal era--especially the �Forest folk� whom we know best--began making a great variety of new things. I remarked about this earlier in the chapter. Dr. Robert M. Adams is of the opinion that this atmosphere of experimentation with new tools--with new ways of collecting food--is @@ -3368,9 +3368,9 @@ the kind of atmosphere in which one might expect trials at planting and at animal domestication to have been made. We first begin to find traces of more permanent life in outdoor camp sites, although caves were still inhabited at the beginning of the terminal era. It is not -surprising at all that the �Forest folk� had already domesticated the +surprising at all that the �Forest folk� had already domesticated the dog. In this sense, the whole era of food-collecting was becoming ready -and almost �incipient� for cultivation and animal domestication. +and almost �incipient� for cultivation and animal domestication. Northwestern Europe was not the place for really effective beginnings in agriculture and animal domestication. These would have had to take @@ -3425,13 +3425,13 @@ zone which surrounds the drainage basin of the Tigris and Euphrates Rivers at elevations of from approximately 2,000 to 5,000 feet. The lower alluvial land of the Tigris-Euphrates basin itself has very little rainfall. Some years ago Professor James Henry Breasted called -the alluvial lands of the Tigris-Euphrates a part of the �fertile -crescent.� These alluvial lands are very fertile if irrigated. Breasted +the alluvial lands of the Tigris-Euphrates a part of the �fertile +crescent.� These alluvial lands are very fertile if irrigated. Breasted was most interested in the oriental civilizations of conventional ancient history, and irrigation had been discovered before they appeared. -The country of hilly flanks above Breasted�s crescent receives from +The country of hilly flanks above Breasted�s crescent receives from 10 to 20 or more inches of winter rainfall each year, which is about what Kansas has. Above the hilly-flanks zone tower the peaks and ridges of the Lebanon-Amanus chain bordering the coast-line from Palestine @@ -3440,7 +3440,7 @@ range of the Iraq-Iran borderland. This rugged mountain frame for our hilly-flanks zone rises to some magnificent alpine scenery, with peaks of from ten to fifteen thousand feet in elevation. There are several gaps in the Mediterranean coastal portion of the frame, through which -the winter�s rain-bearing winds from the sea may break so as to carry +the winter�s rain-bearing winds from the sea may break so as to carry rain to the foothills of the Taurus and the Zagros. The picture I hope you will have from this description is that of an @@ -3482,7 +3482,7 @@ hilly-flanks zone in their wild state. With a single exception--that of the dog--the earliest positive evidence of domestication includes the two forms of wheat, the barley, and the goat. The evidence comes from within the hilly-flanks zone. -However, it comes from a settled village proper, Jarmo (which I�ll +However, it comes from a settled village proper, Jarmo (which I�ll describe in the next chapter), and is thus from the era of the primary village-farming community. We are still without positive evidence of domesticated grain and animals in the first era of the food-producing @@ -3534,9 +3534,9 @@ and the spread of ideas of people who had passed on into one of the more developed eras. In many cases, the terminal era of food-collecting was ended by the incoming of the food-producing peoples themselves. For example, the practices of food-production were carried into Europe -by the actual movement of some numbers of peoples (we don�t know how +by the actual movement of some numbers of peoples (we don�t know how many) who had reached at least the level of the primary village-farming -community. The �Forest folk� learned food-production from them. There +community. The �Forest folk� learned food-production from them. There was never an era of incipient cultivation and domestication proper in Europe, if my hunch is right. @@ -3547,16 +3547,16 @@ The way I see it, two things were required in order that an era of incipient cultivation and domestication could begin. First, there had to be the natural environment of a nuclear area, with its whole group of plants and animals capable of domestication. This is the aspect of -the matter which we�ve said is directly given by nature. But it is +the matter which we�ve said is directly given by nature. But it is quite possible that such an environment with such a group of plants and animals in it may have existed well before ten thousand years ago in the Near East. It is also quite possible that the same promising condition may have existed in regions which never developed into nuclear areas proper. Here, again, we come back to the cultural factor. -I think it was that �atmosphere of experimentation� we�ve talked about -once or twice before. I can�t define it for you, other than to say that +I think it was that �atmosphere of experimentation� we�ve talked about +once or twice before. I can�t define it for you, other than to say that by the end of the Ice Age, the general level of many cultures was ready -for change. Ask me how and why this was so, and I�ll tell you we don�t +for change. Ask me how and why this was so, and I�ll tell you we don�t know yet, and that if we did understand this kind of question, there would be no need for me to go on being a prehistorian! @@ -3590,7 +3590,7 @@ such collections for the modern wild forms of animals and plants from some of our nuclear areas. In the nuclear area in the Near East, some of the wild animals, at least, have already become extinct. There are no longer wild cattle or wild horses in western Asia. We know they were -there from the finds we�ve made in caves of late Ice Age times, and +there from the finds we�ve made in caves of late Ice Age times, and from some slightly later sites. @@ -3601,7 +3601,7 @@ incipient era of cultivation and animal domestication. I am closing this chapter with descriptions of two of the best Near Eastern examples I know of. You may not be satisfied that what I am able to describe makes a full-bodied era of development at all. Remember, however, that -I�ve told you I�m largely playing a kind of a hunch, and also that the +I�ve told you I�m largely playing a kind of a hunch, and also that the archeological materials of this era will always be extremely difficult to interpret. At the beginning of any new way of life, there will be a great tendency for people to make-do, at first, with tools and habits @@ -3613,7 +3613,7 @@ THE NATUFIAN, AN ASSEMBLAGE OF THE INCIPIENT ERA The assemblage called the Natufian comes from the upper layers of a number of caves in Palestine. Traces of its flint industry have also -turned up in Syria and Lebanon. We don�t know just how old it is. I +turned up in Syria and Lebanon. We don�t know just how old it is. I guess that it probably falls within five hundred years either way of about 5000 B.C. @@ -3662,7 +3662,7 @@ pendants. There were also beads and pendants of pierced teeth and shell. A number of Natufian burials have been found in the caves; some burials were grouped together in one grave. The people who were buried within the Mount Carmel cave were laid on their backs in an extended position, -while those on the terrace seem to have been �flexed� (placed in their +while those on the terrace seem to have been �flexed� (placed in their graves in a curled-up position). This may mean no more than that it was easier to dig a long hole in cave dirt than in the hard-packed dirt of the terrace. The people often had some kind of object buried with them, @@ -3679,7 +3679,7 @@ beads. GROUND STONE BONE] -The animal bones of the Natufian layers show beasts of a �modern� type, +The animal bones of the Natufian layers show beasts of a �modern� type, but with some differences from those of present-day Palestine. The bones of the gazelle far outnumber those of the deer; since gazelles like a much drier climate than deer, Palestine must then have had much @@ -3692,9 +3692,9 @@ Maglemosian of northern Europe. More recently, it has been reported that a domesticated goat is also part of the Natufian finds. The study of the human bones from the Natufian burials is not yet -complete. Until Professor McCown�s study becomes available, we may note -Professor Coon�s assessment that these people were of a �basically -Mediterranean type.� +complete. Until Professor McCown�s study becomes available, we may note +Professor Coon�s assessment that these people were of a �basically +Mediterranean type.� THE KARIM SHAHIR ASSEMBLAGE @@ -3704,11 +3704,11 @@ of a temporary open site or encampment. It lies on the top of a bluff in the Kurdish hill-country of northeastern Iraq. It was dug by Dr. Bruce Howe of the expedition I directed in 1950-51 for the Oriental Institute and the American Schools of Oriental Research. In 1954-55, -our expedition located another site, M�lefaat, with general resemblance +our expedition located another site, M�lefaat, with general resemblance to Karim Shahir, but about a hundred miles north of it. In 1956, Dr. Ralph Solecki located still another Karim Shahir type of site called Zawi Chemi Shanidar. The Zawi Chemi site has a radiocarbon date of 8900 -� 300 B.C. +� 300 B.C. Karim Shahir has evidence of only one very shallow level of occupation. It was probably not lived on very long, although the people who lived @@ -3717,7 +3717,7 @@ layer yielded great numbers of fist-sized cracked pieces of limestone, which had been carried up from the bed of a stream at the bottom of the bluff. We think these cracked stones had something to do with a kind of architecture, but we were unable to find positive traces of hut plans. -At M�lefaat and Zawi Chemi, there were traces of rounded hut plans. +At M�lefaat and Zawi Chemi, there were traces of rounded hut plans. As in the Natufian, the great bulk of small objects of the Karim Shahir assemblage was in chipped flint. A large proportion of the flint tools @@ -3737,7 +3737,7 @@ clay figurines which seemed to be of animal form. UNBAKED CLAY SHELL BONE - �ARCHITECTURE�] + �ARCHITECTURE�] Karim Shahir did not yield direct evidence of the kind of vegetable food its people ate. The animal bones showed a considerable @@ -3746,7 +3746,7 @@ domestication--sheep, goat, cattle, horse, dog--as compared with animal bones from the earlier cave sites of the area, which have a high proportion of bones of wild forms like deer and gazelle. But we do not know that any of the Karim Shahir animals were actually domesticated. -Some of them may have been, in an �incipient� way, but we have no means +Some of them may have been, in an �incipient� way, but we have no means at the moment that will tell us from the bones alone. @@ -3761,7 +3761,7 @@ goat, and the general animal situation at Karim Shahir to hint at an incipient approach to food-production. At Karim Shahir, there was the tendency to settle down out in the open; this is echoed by the new reports of open air Natufian sites. The large number of cracked stones -certainly indicates that it was worth the peoples� while to have some +certainly indicates that it was worth the peoples� while to have some kind of structure, even if the site as a whole was short-lived. It is a part of my hunch that these things all point toward @@ -3771,13 +3771,13 @@ which we shall look at next, are fully food-producing, the Natufian and Karim Shahir folk had not yet arrived. I think they were part of a general build-up to full scale food-production. They were possibly controlling a few animals of several kinds and perhaps one or two -plants, without realizing the full possibilities of this �control� as a +plants, without realizing the full possibilities of this �control� as a new way of life. This is why I think of the Karim Shahir and Natufian folk as being at a level, or in an era, of incipient cultivation and domestication. But we shall have to do a great deal more excavation in this range of time -before we�ll get the kind of positive information we need. +before we�ll get the kind of positive information we need. SUMMARY @@ -3798,7 +3798,7 @@ history. We know the earliest village-farming communities appeared in western Asia, in a nuclear area. We do not yet know why the Near Eastern -experiment came first, or why it didn�t happen earlier in some other +experiment came first, or why it didn�t happen earlier in some other nuclear area. Apparently, the level of culture and the promise of the natural environment were ready first in western Asia. The next sites we look at will show a simple but effective food-production already @@ -3835,7 +3835,7 @@ contrast between food-collecting and food-producing as ways of life. THE DIFFERENCE BETWEEN FOOD-COLLECTORS AND FOOD-PRODUCERS -Childe used the word �revolution� because of the radical change that +Childe used the word �revolution� because of the radical change that took place in the habits and customs of man. Food-collectors--that is, hunters, fishers, berry- and nut-gatherers--had to live in small groups or bands, for they had to be ready to move wherever their food supply @@ -3851,7 +3851,7 @@ for clothing beyond the tools that were probably used to dress the skins of animals; no time to think of much of anything but food and protection and disposal of the dead when death did come: an existence which takes nature as it finds it, which does little or nothing to -modify nature--all in all, a savage�s existence, and a very tough one. +modify nature--all in all, a savage�s existence, and a very tough one. A man who spends his whole life following animals just to kill them to eat, or moving from one berry patch to another, is really living just like an animal himself. @@ -3859,10 +3859,10 @@ like an animal himself. THE FOOD-PRODUCING ECONOMY -Against this picture let me try to draw another--that of man�s life -after food-production had begun. His meat was stored �on the hoof,� +Against this picture let me try to draw another--that of man�s life +after food-production had begun. His meat was stored �on the hoof,� his grain in silos or great pottery jars. He lived in a house: it was -worth his while to build one, because he couldn�t move far from his +worth his while to build one, because he couldn�t move far from his fields and flocks. In his neighborhood enough food could be grown and enough animals bred so that many people were kept busy. They all lived close to their flocks and fields, in a village. The village was @@ -3872,7 +3872,7 @@ Children and old men could shepherd the animals by day or help with the lighter work in the fields. After the crops had been harvested the younger men might go hunting and some of them would fish, but the food they brought in was only an addition to the food in the village; the -villagers wouldn�t starve, even if the hunters and fishermen came home +villagers wouldn�t starve, even if the hunters and fishermen came home empty-handed. There was more time to do different things, too. They began to modify @@ -3885,23 +3885,23 @@ people in the village who were becoming full-time craftsmen. Other things were changing, too. The villagers must have had to agree on new rules for living together. The head man of the village had problems different from those of the chief of the small -food-collectors� band. If somebody�s flock of sheep spoiled a wheat +food-collectors� band. If somebody�s flock of sheep spoiled a wheat field, the owner wanted payment for the grain he lost. The chief of the hunters was never bothered with such questions. Even the gods had changed. The spirits and the magic that had been used by hunters -weren�t of any use to the villagers. They needed gods who would watch +weren�t of any use to the villagers. They needed gods who would watch over the fields and the flocks, and they eventually began to erect buildings where their gods might dwell, and where the men who knew most about the gods might live. -WAS FOOD-PRODUCTION A �REVOLUTION�? +WAS FOOD-PRODUCTION A �REVOLUTION�? If you can see the difference between these two pictures--between life in the food-collecting stage and life after food-production -had begun--you�ll see why Professor Childe speaks of a revolution. -By revolution, he doesn�t mean that it happened over night or that -it happened only once. We don�t know exactly how long it took. Some +had begun--you�ll see why Professor Childe speaks of a revolution. +By revolution, he doesn�t mean that it happened over night or that +it happened only once. We don�t know exactly how long it took. Some people think that all these changes may have occurred in less than 500 years, but I doubt that. The incipient era was probably an affair of some duration. Once the level of the village-farming community had @@ -3915,7 +3915,7 @@ been achieved with truly revolutionary suddenness. GAPS IN OUR KNOWLEDGE OF THE NEAR EAST -If you�ll look again at the chart (p. 111) you�ll see that I have +If you�ll look again at the chart (p. 111) you�ll see that I have very few sites and assemblages to name in the incipient era of cultivation and domestication, and not many in the earlier part of the primary village-farming level either. Thanks in no small part @@ -3926,20 +3926,20 @@ yard-stick here. But I am far from being able to show you a series of Sears Roebuck catalogues, even century by century, for any part of the nuclear area. There is still a great deal of earth to move, and a great mass of material to recover and interpret before we even begin to -understand �how� and �why.� +understand �how� and �why.� Perhaps here, because this kind of archeology is really my specialty, -you�ll excuse it if I become personal for a moment. I very much look +you�ll excuse it if I become personal for a moment. I very much look forward to having further part in closing some of the gaps in knowledge -of the Near East. This is not, as I�ve told you, the spectacular +of the Near East. This is not, as I�ve told you, the spectacular range of Near Eastern archeology. There are no royal tombs, no gold, no great buildings or sculpture, no writing, in fact nothing to excite the normal museum at all. Nevertheless it is a range which, idea-wise, gives the archeologist tremendous satisfaction. The country of the hilly flanks is an exciting combination of green grasslands and mountainous ridges. The Kurds, who inhabit the part of the area -in which I�ve worked most recently, are an extremely interesting and -hospitable people. Archeologists don�t become rich, but I�ll forego +in which I�ve worked most recently, are an extremely interesting and +hospitable people. Archeologists don�t become rich, but I�ll forego the Cadillac for any bright spring morning in the Kurdish hills, on a good site with a happy crew of workmen and an interested and efficient staff. It is probably impossible to convey the full feeling which life @@ -3965,15 +3965,15 @@ like the use of pottery borrowed from the more developed era of the same time in the nuclear area. The same general explanation doubtless holds true for certain materials in Egypt, along the upper Nile and in the Kharga oasis: these materials, called Sebilian III, the Khartoum -�neolithic,� and the Khargan microlithic, are from surface sites, +�neolithic,� and the Khargan microlithic, are from surface sites, not from caves. The chart (p. 111) shows where I would place these materials in era and time. [Illustration: THE HILLY FLANKS OF THE CRESCENT AND EARLY SITES OF THE NEAR EAST] -Both M�lefaat and Dr. Solecki�s Zawi Chemi Shanidar site appear to have -been slightly more �settled in� than was Karim Shahir itself. But I do +Both M�lefaat and Dr. Solecki�s Zawi Chemi Shanidar site appear to have +been slightly more �settled in� than was Karim Shahir itself. But I do not think they belong to the era of farming-villages proper. The first site of this era, in the hills of Iraqi Kurdistan, is Jarmo, on which we have spent three seasons of work. Following Jarmo comes a variety of @@ -3989,9 +3989,9 @@ times when their various cultures flourished, there must have been many little villages which shared the same general assemblage. We are only now beginning to locate them again. Thus, if I speak of Jarmo, or Jericho, or Sialk as single examples of their particular kinds of -assemblages, I don�t mean that they were unique at all. I think I could +assemblages, I don�t mean that they were unique at all. I think I could take you to the sites of at least three more Jarmos, within twenty -miles of the original one. They are there, but they simply haven�t yet +miles of the original one. They are there, but they simply haven�t yet been excavated. In 1956, a Danish expedition discovered material of Jarmo type at Shimshara, only two dozen miles northeast of Jarmo, and below an assemblage of Hassunan type (which I shall describe presently). @@ -4000,15 +4000,15 @@ below an assemblage of Hassunan type (which I shall describe presently). THE GAP BETWEEN KARIM SHAHIR AND JARMO As we see the matter now, there is probably still a gap in the -available archeological record between the Karim Shahir-M�lefaat-Zawi +available archeological record between the Karim Shahir-M�lefaat-Zawi Chemi group (of the incipient era) and that of Jarmo (of the village-farming era). Although some items of the Jarmo type materials do reflect the beginnings of traditions set in the Karim Shahir group (see p. 120), there is not a clear continuity. Moreover--to the degree that we may trust a few radiocarbon dates--there would appear to be around two thousand years of difference in time. The single -available Zawi Chemi �date� is 8900 � 300 B.C.; the most reasonable -group of �dates� from Jarmo average to about 6750 � 200 B.C. I am +available Zawi Chemi �date� is 8900 � 300 B.C.; the most reasonable +group of �dates� from Jarmo average to about 6750 � 200 B.C. I am uncertain about this two thousand years--I do not think it can have been so long. @@ -4021,7 +4021,7 @@ JARMO, IN THE KURDISH HILLS, IRAQ The site of Jarmo has a depth of deposit of about twenty-seven feet, and approximately a dozen layers of architectural renovation and -change. Nevertheless it is a �one period� site: its assemblage remains +change. Nevertheless it is a �one period� site: its assemblage remains essentially the same throughout, although one or two new items are added in later levels. It covers about four acres of the top of a bluff, below which runs a small stream. Jarmo lies in the hill country @@ -4078,7 +4078,7 @@ human beings in clay; one type of human figurine they favored was that of a markedly pregnant woman, probably the expression of some sort of fertility spirit. They provided their house floors with baked-in-place depressions, either as basins or hearths, and later with domed ovens of -clay. As we�ve noted, the houses themselves were of clay or mud; one +clay. As we�ve noted, the houses themselves were of clay or mud; one could almost say they were built up like a house-sized pot. Then, finally, the idea of making portable pottery itself appeared, although I very much doubt that the people of the Jarmo village discovered the @@ -4095,11 +4095,11 @@ over three hundred miles to the north. Already a bulk carrying trade had been established--the forerunner of commerce--and the routes were set by which, in later times, the metal trade was to move. -There are now twelve radioactive carbon �dates� from Jarmo. The most -reasonable cluster of determinations averages to about 6750 � 200 -B.C., although there is a completely unreasonable range of �dates� +There are now twelve radioactive carbon �dates� from Jarmo. The most +reasonable cluster of determinations averages to about 6750 � 200 +B.C., although there is a completely unreasonable range of �dates� running from 3250 to 9250 B.C.! _If_ I am right in what I take to be -�reasonable,� the first flush of the food-producing revolution had been +�reasonable,� the first flush of the food-producing revolution had been achieved almost nine thousand years ago. @@ -4117,7 +4117,7 @@ it, but the Hassunan sites seem to cluster at slightly lower elevations than those we have been talking about so far. The catalogue of the Hassuna assemblage is of course more full and -elaborate than that of Jarmo. The Iraqi government�s archeologists +elaborate than that of Jarmo. The Iraqi government�s archeologists who dug Hassuna itself, exposed evidence of increasing architectural know-how. The walls of houses were still formed of puddled mud; sun-dried bricks appear only in later periods. There were now several @@ -4130,16 +4130,16 @@ largely disappeared by Hassunan times. The flint work of the Hassunan catalogue is, by and large, a wretched affair. We might guess that the kinaesthetic concentration of the Hassuna craftsmen now went into other categories; that is, they suddenly discovered they might have more fun -working with the newer materials. It�s a shame, for example, that none +working with the newer materials. It�s a shame, for example, that none of their weaving is preserved for us. The two available radiocarbon determinations from Hassunan contexts -stand at about 5100 and 5600 B.C. � 250 years. +stand at about 5100 and 5600 B.C. � 250 years. OTHER EARLY VILLAGE SITES IN THE NUCLEAR AREA -I�ll now name and very briefly describe a few of the other early +I�ll now name and very briefly describe a few of the other early village assemblages either in or adjacent to the hilly flanks of the crescent. Unfortunately, we do not have radioactive carbon dates for many of these materials. We may guess that some particular assemblage, @@ -4177,7 +4177,7 @@ ecological niche, some seven hundred feet below sea level; it is geographically within the hilly-flanks zone but environmentally not part of it. -Several radiocarbon �dates� for Jericho fall within the range of those +Several radiocarbon �dates� for Jericho fall within the range of those I find reasonable for Jarmo, and their internal statistical consistency is far better than that for the Jarmo determinations. It is not yet clear exactly what this means. @@ -4226,7 +4226,7 @@ how things were made are different; the Sialk assemblage represents still another cultural pattern. I suspect it appeared a bit later in time than did that of Hassuna. There is an important new item in the Sialk catalogue. The Sialk people made small drills or pins of -hammered copper. Thus the metallurgist�s specialized craft had made its +hammered copper. Thus the metallurgist�s specialized craft had made its appearance. There is at least one very early Iranian site on the inward slopes @@ -4246,7 +4246,7 @@ shore of the Fayum lake. The Fayum materials come mainly from grain bins or silos. Another site, Merimde, in the western part of the Nile delta, shows the remains of a true village, but it may be slightly later than the settlement of the Fayum. There are radioactive carbon -�dates� for the Fayum materials at about 4275 B.C. � 320 years, which +�dates� for the Fayum materials at about 4275 B.C. � 320 years, which is almost fifteen hundred years later than the determinations suggested for the Hassunan or Syro-Cilician assemblages. I suspect that this is a somewhat over-extended indication of the time it took for the @@ -4260,13 +4260,13 @@ the mound called Shaheinab. The Shaheinab catalogue roughly corresponds to that of the Fayum; the distance between the two places, as the Nile flows, is roughly 1,500 miles. Thus it took almost a thousand years for the new way of life to be carried as far south into Africa as Khartoum; -the two Shaheinab �dates� average about 3300 B.C. � 400 years. +the two Shaheinab �dates� average about 3300 B.C. � 400 years. If the movement was up the Nile (southward), as these dates suggest, then I suspect that the earliest available village material of middle Egypt, the so-called Tasian, is also later than that of the Fayum. The Tasian materials come from a few graves near a village called Deir -Tasa, and I have an uncomfortable feeling that the Tasian �assemblage� +Tasa, and I have an uncomfortable feeling that the Tasian �assemblage� may be mainly an artificial selection of poor examples of objects which belong in the following range of time. @@ -4280,7 +4280,7 @@ spread outward in space from the nuclear area, as time went on. There is good archeological evidence that both these processes took place. For the hill country of northeastern Iraq, in the nuclear area, we have already noticed how the succession (still with gaps) from Karim -Shahir, through M�lefaat and Jarmo, to Hassuna can be charted (see +Shahir, through M�lefaat and Jarmo, to Hassuna can be charted (see chart, p. 111). In the next chapter, we shall continue this charting and description of what happened in Iraq upward through time. We also watched traces of the new way of life move through space up the Nile @@ -4299,7 +4299,7 @@ appearance of the village-farming community there--is still an open one. In the last chapter, we noted the probability of an independent nuclear area in southeastern Asia. Professor Carl Sauer strongly champions the great importance of this area as _the_ original center -of agricultural pursuits, as a kind of �cradle� of all incipient eras +of agricultural pursuits, as a kind of �cradle� of all incipient eras of the Old World at least. While there is certainly not the slightest archeological evidence to allow us to go that far, we may easily expect that an early southeast Asian development would have been felt in @@ -4311,13 +4311,13 @@ way of life moved well beyond Khartoum in Africa. THE SPREAD OF THE VILLAGE-FARMING COMMUNITY WAY OF LIFE INTO EUROPE -How about Europe? I won�t give you many details. You can easily imagine +How about Europe? I won�t give you many details. You can easily imagine that the late prehistoric prelude to European history is a complicated affair. We all know very well how complicated an area Europe is now, with its welter of different languages and cultures. Remember, however, that a great deal of archeology has been done on the late prehistory of Europe, and very little on that of further Asia and Africa. If we knew -as much about these areas as we do of Europe, I expect we�d find them +as much about these areas as we do of Europe, I expect we�d find them just as complicated. This much is clear for Europe, as far as the spread of the @@ -4329,21 +4329,21 @@ in western Asia. I do not, of course, mean that there were traveling salesmen who carried these ideas and things to Europe with a commercial gleam in their eyes. The process took time, and the ideas and things must have been passed on from one group of people to the next. There -was also some actual movement of peoples, but we don�t know the size of +was also some actual movement of peoples, but we don�t know the size of the groups that moved. -The story of the �colonization� of Europe by the first farmers is +The story of the �colonization� of Europe by the first farmers is thus one of (1) the movement from the eastern Mediterranean lands of some people who were farmers; (2) the spread of ideas and things beyond the Near East itself and beyond the paths along which the -�colonists� moved; and (3) the adaptations of the ideas and things -by the indigenous �Forest folk�, about whose �receptiveness� Professor +�colonists� moved; and (3) the adaptations of the ideas and things +by the indigenous �Forest folk�, about whose �receptiveness� Professor Mathiassen speaks (p. 97). It is important to note that the resulting cultures in the new European environment were European, not Near -Eastern. The late Professor Childe remarked that �the peoples of the +Eastern. The late Professor Childe remarked that �the peoples of the West were not slavish imitators; they adapted the gifts from the East ... into a new and organic whole capable of developing on its own -original lines.� +original lines.� THE WAYS TO EUROPE @@ -4389,19 +4389,19 @@ Hill, the earliest known trace of village-farming communities in England, is about 2500 B.C. I would expect about 5500 B.C. to be a safe date to give for the well-developed early village communities of Syro-Cilicia. We suspect that the spread throughout Europe did not -proceed at an even rate. Professor Piggott writes that �at a date +proceed at an even rate. Professor Piggott writes that �at a date probably about 2600 B.C., simple agricultural communities were being established in Spain and southern France, and from the latter region a spread northwards can be traced ... from points on the French seaboard of the [English] Channel ... there were emigrations of a certain number of these tribes by boat, across to the chalk lands of Wessex and Sussex [in England], probably not more than three or four generations later -than the formation of the south French colonies.� +than the formation of the south French colonies.� New radiocarbon determinations are becoming available all the time--already several suggest that the food-producing way of life had reached the lower Rhine and Holland by 4000 B.C. But not all -prehistorians accept these �dates,� so I do not show them on my map +prehistorians accept these �dates,� so I do not show them on my map (p. 139). @@ -4427,7 +4427,7 @@ concentric sets of banks and ditches. Traces of oblong timber houses have been found, but not within the enclosures. The second type of structure is mine-shafts, dug down into the chalk beds where good flint for the making of axes or hoes could be found. The third type -of structure is long simple mounds or �unchambered barrows,� in one +of structure is long simple mounds or �unchambered barrows,� in one end of which burials were made. It has been commonly believed that the Windmill Hill assemblage belonged entirely to the cultural tradition which moved up through France to the Channel. Professor Piggott is now @@ -4443,12 +4443,12 @@ consists mainly of tombs and the contents of tombs, with only very rare settlement sites. The tombs were of some size and received the bodies of many people. The tombs themselves were built of stone, heaped over with earth; the stones enclosed a passage to a central chamber -(�passage graves�), or to a simple long gallery, along the sides of -which the bodies were laid (�gallery graves�). The general type of -construction is called �megalithic� (= great stone), and the whole +(�passage graves�), or to a simple long gallery, along the sides of +which the bodies were laid (�gallery graves�). The general type of +construction is called �megalithic� (= great stone), and the whole earth-mounded structure is often called a _barrow_. Since many have -proper chambers, in one sense or another, we used the term �unchambered -barrow� above to distinguish those of the Windmill Hill type from these +proper chambers, in one sense or another, we used the term �unchambered +barrow� above to distinguish those of the Windmill Hill type from these megalithic structures. There is some evidence for sacrifice, libations, and ceremonial fires, and it is clear that some form of community ritual was focused on the megalithic tombs. @@ -4466,7 +4466,7 @@ The third early British group of antiquities of this general time It is not so certain that the people who made this assemblage, called Peterborough, were actually farmers. While they may on occasion have practiced a simple agriculture, many items of their assemblage link -them closely with that of the �Forest folk� of earlier times in +them closely with that of the �Forest folk� of earlier times in England and in the Baltic countries. Their pottery is decorated with impressions of cords and is quite different from that of Windmill Hill and the megalithic builders. In addition, the distribution of their @@ -4479,7 +4479,7 @@ to acquire the raw material for stone axes. A probably slightly later culture, whose traces are best known from Skara Brae on Orkney, also had its roots in those cultures of the -Baltic area which fused out of the meeting of the �Forest folk� and +Baltic area which fused out of the meeting of the �Forest folk� and the peoples who took the eastern way into Europe. Skara Brae is very well preserved, having been built of thin stone slabs about which dune-sand drifted after the village died. The individual houses, the @@ -4498,14 +4498,14 @@ details which I have omitted in order to shorten the story. I believe some of the difficulty we have in understanding the establishment of the first farming communities in Europe is with -the word �colonization.� We have a natural tendency to think of -�colonization� as it has happened within the last few centuries. In the +the word �colonization.� We have a natural tendency to think of +�colonization� as it has happened within the last few centuries. In the case of the colonization of the Americas, for example, the colonists came relatively quickly, and in increasingly vast numbers. They had vastly superior technical, political, and war-making skills, compared with those of the Indians. There was not much mixing with the Indians. The case in Europe five or six thousand years ago must have been very -different. I wonder if it is even proper to call people �colonists� +different. I wonder if it is even proper to call people �colonists� who move some miles to a new region, settle down and farm it for some years, then move on again, generation after generation? The ideas and the things which these new people carried were only _potentially_ @@ -4521,12 +4521,12 @@ migrants were moving by boat, long distances may have been covered in a short time. Remember, however, we seem to have about three thousand years between the early Syro-Cilician villages and Windmill Hill. -Let me repeat Professor Childe again. �The peoples of the West were +Let me repeat Professor Childe again. �The peoples of the West were not slavish imitators: they adapted the gifts from the East ... into a new and organic whole capable of developing on its own original -lines.� Childe is of course completely conscious of the fact that his -�peoples of the West� were in part the descendants of migrants who came -originally from the �East,� bringing their �gifts� with them. This +lines.� Childe is of course completely conscious of the fact that his +�peoples of the West� were in part the descendants of migrants who came +originally from the �East,� bringing their �gifts� with them. This was the late prehistoric achievement of Europe--to take new ideas and things and some migrant peoples and, by mixing them with the old in its own environments, to forge a new and unique series of cultures. @@ -4553,14 +4553,14 @@ things first happened there and also because I know it best. There is another interesting thing, too. We have seen that the first experiment in village-farming took place in the Near East. So did -the first experiment in civilization. Both experiments �took.� The +the first experiment in civilization. Both experiments �took.� The traditions we live by today are based, ultimately, on those ancient beginnings in food-production and civilization in the Near East. -WHAT �CIVILIZATION� MEANS +WHAT �CIVILIZATION� MEANS -I shall not try to define �civilization� for you; rather, I shall +I shall not try to define �civilization� for you; rather, I shall tell you what the word brings to my mind. To me civilization means urbanization: the fact that there are cities. It means a formal political set-up--that there are kings or governing bodies that the @@ -4606,7 +4606,7 @@ of Mexico, the Mayas of Yucatan and Guatemala, and the Incas of the Andes were civilized. -WHY DIDN�T CIVILIZATION COME TO ALL FOOD-PRODUCERS? +WHY DIDN�T CIVILIZATION COME TO ALL FOOD-PRODUCERS? Once you have food-production, even at the well-advanced level of the village-farming community, what else has to happen before you @@ -4625,13 +4625,13 @@ early civilization, is still an open and very interesting question. WHERE CIVILIZATION FIRST APPEARED IN THE NEAR EAST You remember that our earliest village-farming communities lay along -the hilly flanks of a great �crescent.� (See map on p. 125.) -Professor Breasted�s �fertile crescent� emphasized the rich river +the hilly flanks of a great �crescent.� (See map on p. 125.) +Professor Breasted�s �fertile crescent� emphasized the rich river valleys of the Nile and the Tigris-Euphrates Rivers. Our hilly-flanks area of the crescent zone arches up from Egypt through Palestine and Syria, along southern Turkey into northern Iraq, and down along the southwestern fringe of Iran. The earliest food-producing villages we -know already existed in this area by about 6750 B.C. (� 200 years). +know already existed in this area by about 6750 B.C. (� 200 years). Now notice that this hilly-flanks zone does not include southern Mesopotamia, the alluvial land of the lower Tigris and Euphrates in @@ -4639,7 +4639,7 @@ Iraq, or the Nile Valley proper. The earliest known villages of classic Mesopotamia and Egypt seem to appear fifteen hundred or more years after those of the hilly-flanks zone. For example, the early Fayum village which lies near a lake west of the Nile Valley proper (see p. -135) has a radiocarbon date of 4275 B.C. � 320 years. It was in the +135) has a radiocarbon date of 4275 B.C. � 320 years. It was in the river lands, however, that the immediate beginnings of civilization were made. @@ -4657,8 +4657,8 @@ THE HILLY-FLANKS ZONE VERSUS THE RIVER LANDS Why did these two civilizations spring up in these two river lands which apparently were not even part of the area where the -village-farming community began? Why didn�t we have the first -civilizations in Palestine, Syria, north Iraq, or Iran, where we�re +village-farming community began? Why didn�t we have the first +civilizations in Palestine, Syria, north Iraq, or Iran, where we�re sure food-production had had a long time to develop? I think the probable answer gives a clue to the ways in which civilization began in Egypt and Mesopotamia. @@ -4669,7 +4669,7 @@ and Syria. There are pleasant mountain slopes, streams running out to the sea, and rain, at least in the winter months. The rain belt and the foothills of the Turkish mountains also extend to northern Iraq and on to the Iranian plateau. The Iranian plateau has its mountain valleys, -streams, and some rain. These hilly flanks of the �crescent,� through +streams, and some rain. These hilly flanks of the �crescent,� through most of its arc, are almost made-to-order for beginning farmers. The grassy slopes of the higher hills would be pasture for their herds and flocks. As soon as the earliest experiments with agriculture and @@ -4720,10 +4720,10 @@ Obviously, we can no longer find the first dikes or reservoirs of the Nile Valley, or the first canals or ditches of Mesopotamia. The same land has been lived on far too long for any traces of the first attempts to be left; or, especially in Egypt, it has been covered by -the yearly deposits of silt, dropped by the river floods. But we�re +the yearly deposits of silt, dropped by the river floods. But we�re pretty sure the first food-producers of Egypt and southern Mesopotamia must have made such dikes, canals, and ditches. In the first place, -there can�t have been enough rain for them to grow things otherwise. +there can�t have been enough rain for them to grow things otherwise. In the second place, the patterns for such projects seem to have been pretty well set by historic times. @@ -4733,10 +4733,10 @@ CONTROL OF THE RIVERS THE BUSINESS OF EVERYONE Here, then, is a _part_ of the reason why civilization grew in Egypt and Mesopotamia first--not in Palestine, Syria, or Iran. In the latter areas, people could manage to produce their food as individuals. It -wasn�t too hard; there were rain and some streams, and good pasturage +wasn�t too hard; there were rain and some streams, and good pasturage for the animals even if a crop or two went wrong. In Egypt and Mesopotamia, people had to put in a much greater amount of work, and -this work couldn�t be individual work. Whole villages or groups of +this work couldn�t be individual work. Whole villages or groups of people had to turn out to fix dikes or dig ditches. The dikes had to be repaired and the ditches carefully cleared of silt each year, or they would become useless. @@ -4745,7 +4745,7 @@ There also had to be hard and fast rules. The person who lived nearest the ditch or the reservoir must not be allowed to take all the water and leave none for his neighbors. It was not only a business of learning to control the rivers and of making their waters do the -farmer�s work. It also meant controlling men. But once these men had +farmer�s work. It also meant controlling men. But once these men had managed both kinds of controls, what a wonderful yield they had! The soil was already fertile, and the silt which came in the floods and ditches kept adding fertile soil. @@ -4756,7 +4756,7 @@ THE GERM OF CIVILIZATION IN EGYPT AND MESOPOTAMIA This learning to work together for the common good was the real germ of the Egyptian and the Mesopotamian civilizations. The bare elements of civilization were already there: the need for a governing hand and for -laws to see that the communities� work was done and that the water was +laws to see that the communities� work was done and that the water was justly shared. You may object that there is a sort of chicken and egg paradox in this idea. How could the people set up the rules until they had managed to get a way to live, and how could they manage to get a @@ -4781,12 +4781,12 @@ My explanation has been pointed particularly at Egypt and Mesopotamia. I have already told you that the irrigation and water-control part of it does not apply to the development of the Aztecs or the Mayas, or perhaps anybody else. But I think that a fair part of the story of -Egypt and Mesopotamia must be as I�ve just told you. +Egypt and Mesopotamia must be as I�ve just told you. I am particularly anxious that you do _not_ understand me to mean that irrigation _caused_ civilization. I am sure it was not that simple at all. For, in fact, a complex and highly engineered irrigation system -proper did not come until later times. Let�s say rather that the simple +proper did not come until later times. Let�s say rather that the simple beginnings of irrigation allowed and in fact encouraged a great number of things in the technological, political, social, and moral realms of culture. We do not yet understand what all these things were or how @@ -4842,7 +4842,7 @@ the mound which later became the holy Sumerian city of Eridu, Iraqi archeologists uncovered a handsome painted pottery. Pottery of the same type had been noticed earlier by German archeologists on the surface of a small mound, awash in the spring floods, near the remains of the -Biblical city of Erich (Sumerian = Uruk; Arabic = Warka). This �Eridu� +Biblical city of Erich (Sumerian = Uruk; Arabic = Warka). This �Eridu� pottery, which is about all we have of the assemblage of the people who once produced it, may be seen as a blend of the Samarran and Halafian painted pottery styles. This may over-simplify the case, but as yet we @@ -4864,7 +4864,7 @@ seems to move into place before the Halaf manifestation is finished, and to blend with it. The Ubaidian assemblage in the south is by far the more spectacular. The development of the temple has been traced at Eridu from a simple little structure to a monumental building some -62 feet long, with a pilaster-decorated fa�ade and an altar in its +62 feet long, with a pilaster-decorated fa�ade and an altar in its central chamber. There is painted Ubaidian pottery, but the style is hurried and somewhat careless and gives the _impression_ of having been a cheap mass-production means of decoration when compared with the @@ -4879,7 +4879,7 @@ turtle-like faces are another item in the southern Ubaidian assemblage. There is a large Ubaid cemetery at Eridu, much of it still awaiting excavation. The few skeletons so far tentatively studied reveal a -completely modern type of �Mediterraneanoid�; the individuals whom the +completely modern type of �Mediterraneanoid�; the individuals whom the skeletons represent would undoubtedly blend perfectly into the modern population of southern Iraq. What the Ubaidian assemblage says to us is that these people had already adapted themselves and their culture to @@ -4925,7 +4925,7 @@ woven stuffs must have been the mediums of exchange. Over what area did the trading net-work of Ubaid extend? We start with the idea that the Ubaidian assemblage is most richly developed in the south. We assume, I think, correctly, that it represents a cultural flowering of the south. -On the basis of the pottery of the still elusive �Eridu� immigrants +On the basis of the pottery of the still elusive �Eridu� immigrants who had first followed the rivers into alluvial Mesopotamia, we get the notion that the characteristic painted pottery style of Ubaid was developed in the southland. If this reconstruction is correct @@ -4935,7 +4935,7 @@ assemblage of (and from the southern point of view, _fairly_ pure) Ubaidian material in northern Iraq. The pottery appears all along the Iranian flanks, even well east of the head of the Persian Gulf, and ends in a later and spectacular flourish in an extremely handsome -painted style called the �Susa� style. Ubaidian pottery has been noted +painted style called the �Susa� style. Ubaidian pottery has been noted up the valleys of both of the great rivers, well north of the Iraqi and Syrian borders on the southern flanks of the Anatolian plateau. It reaches the Mediterranean Sea and the valley of the Orontes in @@ -4965,10 +4965,10 @@ Mesopotamia. Next, much to our annoyance, we have what is almost a temporary black-out. According to the system of terminology I favor, our next -�assemblage� after that of Ubaid is called the _Warka_ phase, from +�assemblage� after that of Ubaid is called the _Warka_ phase, from the Arabic name for the site of Uruk or Erich. We know it only from six or seven levels in a narrow test-pit at Warka, and from an even -smaller hole at another site. This �assemblage,� so far, is known only +smaller hole at another site. This �assemblage,� so far, is known only by its pottery, some of which still bears Ubaidian style painting. The characteristic Warkan pottery is unpainted, with smoothed red or gray surfaces and peculiar shapes. Unquestionably, there must be a great @@ -4979,7 +4979,7 @@ have to excavate it! THE DAWN OF CIVILIZATION After our exasperation with the almost unknown Warka interlude, -following the brilliant �false dawn� of Ubaid, we move next to an +following the brilliant �false dawn� of Ubaid, we move next to an assemblage which yields traces of a preponderance of those elements which we noted (p. 144) as meaning civilization. This assemblage is that called _Proto-Literate_; it already contains writing. On @@ -4988,8 +4988,8 @@ history--and no longer prehistory--the assemblage is named for the historical implications of its content, and no longer after the name of the site where it was first found. Since some of the older books used site-names for this assemblage, I will tell you that the Proto-Literate -includes the latter half of what used to be called the �Uruk period� -_plus_ all of what used to be called the �Jemdet Nasr period.� It shows +includes the latter half of what used to be called the �Uruk period� +_plus_ all of what used to be called the �Jemdet Nasr period.� It shows a consistent development from beginning to end. I shall, in fact, leave much of the description and the historic @@ -5033,18 +5033,18 @@ mental block seems to have been removed. Clay tablets bearing pictographic signs are the Proto-Literate forerunners of cuneiform writing. The earliest examples are not well -understood but they seem to be �devices for making accounts and -for remembering accounts.� Different from the later case in Egypt, +understood but they seem to be �devices for making accounts and +for remembering accounts.� Different from the later case in Egypt, where writing appears fully formed in the earliest examples, the development from simple pictographic signs to proper cuneiform writing may be traced, step by step, in Mesopotamia. It is most probable that the development of writing was connected with the temple and -the need for keeping account of the temple�s possessions. Professor +the need for keeping account of the temple�s possessions. Professor Jacobsen sees writing as a means for overcoming space, time, and the -increasing complications of human affairs: �Literacy, which began +increasing complications of human affairs: �Literacy, which began with ... civilization, enhanced mightily those very tendencies in its development which characterize it as a civilization and mark it off as -such from other types of culture.� +such from other types of culture.� [Illustration: RELIEF ON A PROTO-LITERATE STONE VASE, WARKA @@ -5098,7 +5098,7 @@ civilized way of life. I suppose you could say that the difference in the approach is that as a prehistorian I have been looking forward or upward in time, while the -historians look backward to glimpse what I�ve been describing here. My +historians look backward to glimpse what I�ve been describing here. My base-line was half a million years ago with a being who had little more than the capacity to make tools and fire to distinguish him from the animals about him. Thus my point of view and that of the conventional @@ -5114,17 +5114,17 @@ End of PREHISTORY [Illustration] -You�ll doubtless easily recall your general course in ancient history: +You�ll doubtless easily recall your general course in ancient history: how the Sumerian dynasties of Mesopotamia were supplanted by those of Babylonia, how the Hittite kingdom appeared in Anatolian Turkey, and about the three great phases of Egyptian history. The literate kingdom of Crete arose, and by 1500 B.C. there were splendid fortified Mycenean towns on the mainland of Greece. This was the time--about the whole eastern end of the Mediterranean--of what Professor Breasted called the -�first great internationalism,� with flourishing trade, international +�first great internationalism,� with flourishing trade, international treaties, and royal marriages between Egyptians, Babylonians, and -Hittites. By 1200 B.C., the whole thing had fragmented: �the peoples of -the sea were restless in their isles,� and the great ancient centers in +Hittites. By 1200 B.C., the whole thing had fragmented: �the peoples of +the sea were restless in their isles,� and the great ancient centers in Egypt, Mesopotamia, and Anatolia were eclipsed. Numerous smaller states arose--Assyria, Phoenicia, Israel--and the Trojan war was fought. Finally Assyria became the paramount power of all the Near East, @@ -5135,7 +5135,7 @@ but casting them with its own tradition into a new mould, arose in mainland Greece. I once shocked my Classical colleagues to the core by referring to -Greece as �a second degree derived civilization,� but there is much +Greece as �a second degree derived civilization,� but there is much truth in this. The principles of bronze- and then of iron-working, of the alphabet, and of many other elements in Greek culture were borrowed from western Asia. Our debt to the Greeks is too well known for me even @@ -5146,7 +5146,7 @@ Greece fell in its turn to Rome, and in 55 B.C. Caesar invaded Britain. I last spoke of Britain on page 142; I had chosen it as my single example for telling you something of how the earliest farming communities were established in Europe. Now I will continue with -Britain�s later prehistory, so you may sense something of the end of +Britain�s later prehistory, so you may sense something of the end of prehistory itself. Remember that Britain is simply a single example we select; the same thing could be done for all the other countries of Europe, and will be possible also, some day, for further Asia and @@ -5186,20 +5186,20 @@ few Battle-axe folk elements, including, in fact, stone battle-axes, reached England with the earliest Beaker folk,[6] coming from the Rhineland. - [6] The British authors use the term �Beaker folk� to mean both + [6] The British authors use the term �Beaker folk� to mean both archeological assemblage and human physical type. They speak - of a �... tall, heavy-boned, rugged, and round-headed� strain + of a �... tall, heavy-boned, rugged, and round-headed� strain which they take to have developed, apparently in the Rhineland, by a mixture of the original (Spanish?) beaker-makers and the northeast European battle-axe makers. However, since the science of physical anthropology is very much in flux at the moment, and since I am not able to assess the evidence for these - physical types, I _do not_ use the term �folk� in this book with + physical types, I _do not_ use the term �folk� in this book with its usual meaning of standardized physical type. When I use - �folk� here, I mean simply _the makers of a given archeological + �folk� here, I mean simply _the makers of a given archeological assemblage_. The difficulty only comes when assemblages are named for some item in them; it is too clumsy to make an - adjective of the item and refer to a �beakerian� assemblage. + adjective of the item and refer to a �beakerian� assemblage. The Beaker folk settled earliest in the agriculturally fertile south and east. There seem to have been several phases of Beaker folk @@ -5211,7 +5211,7 @@ folk are known. They buried their dead singly, sometimes in conspicuous individual barrows with the dead warrior in his full trappings. The spectacular element in the assemblage of the Beaker folk is a group of large circular monuments with ditches and with uprights of wood or -stone. These �henges� became truly monumental several hundred years +stone. These �henges� became truly monumental several hundred years later; while they were occasionally dedicated with a burial, they were not primarily tombs. The effect of the invasion of the Beaker folk seems to cut across the whole fabric of life in Britain. @@ -5221,7 +5221,7 @@ seems to cut across the whole fabric of life in Britain. There was, however, a second major element in British life at this time. It shows itself in the less well understood traces of a group again called after one of the items in their catalogue, the Food-vessel -folk. There are many burials in these �food-vessel� pots in northern +folk. There are many burials in these �food-vessel� pots in northern England, Scotland, and Ireland, and the pottery itself seems to link back to that of the Peterborough assemblage. Like the earlier Peterborough people in the highland zone before them, the makers of @@ -5238,8 +5238,8 @@ MORE INVASIONS About 1500 B.C., the situation became further complicated by the arrival of new people in the region of southern England anciently called Wessex. The traces suggest the Brittany coast of France as a -source, and the people seem at first to have been a small but �heroic� -group of aristocrats. Their �heroes� are buried with wealth and +source, and the people seem at first to have been a small but �heroic� +group of aristocrats. Their �heroes� are buried with wealth and ceremony, surrounded by their axes and daggers of bronze, their gold ornaments, and amber and jet beads. These rich finds show that the trade-linkage these warriors patronized spread from the Baltic sources @@ -5265,10 +5265,10 @@ which must have been necessary before such a great monument could have been built. -�THIS ENGLAND� +�THIS ENGLAND� The range from 1900 to about 1400 B.C. includes the time of development -of the archeological features usually called the �Early Bronze Age� +of the archeological features usually called the �Early Bronze Age� in Britain. In fact, traces of the Wessex warriors persisted down to about 1200 B.C. The main regions of the island were populated, and the adjustments to the highland and lowland zones were distinct and well @@ -5279,7 +5279,7 @@ trading role, separated from the European continent but conveniently adjacent to it. The tin of Cornwall--so important in the production of good bronze--as well as the copper of the west and of Ireland, taken with the gold of Ireland and the general excellence of Irish -metal work, assured Britain a trader�s place in the then known world. +metal work, assured Britain a trader�s place in the then known world. Contacts with the eastern Mediterranean may have been by sea, with Cornish tin as the attraction, or may have been made by the Food-vessel middlemen on their trips to the Baltic coast. There they would have @@ -5292,9 +5292,9 @@ relative isolation gave some peace and also gave time for a leveling and further fusion of culture. The separate cultural traditions began to have more in common. The growing of barley, the herding of sheep and cattle, and the production of woolen garments were already features -common to all Britain�s inhabitants save a few in the remote highlands, +common to all Britain�s inhabitants save a few in the remote highlands, the far north, and the distant islands not yet fully touched by -food-production. The �personality of Britain� was being formed. +food-production. The �personality of Britain� was being formed. CREMATION BURIALS BEGIN @@ -5325,9 +5325,9 @@ which we shall mention below. The British cremation-burial-in-urns folk survived a long time in the highland zone. In the general British scheme, they make up what is -called the �Middle Bronze Age,� but in the highland zone they last +called the �Middle Bronze Age,� but in the highland zone they last until after 900 B.C. and are considered to be a specialized highland -�Late Bronze Age.� In the highland zone, these later cremation-burial +�Late Bronze Age.� In the highland zone, these later cremation-burial folk seem to have continued the older Food-vessel tradition of being middlemen in the metal market. @@ -5379,12 +5379,12 @@ to get a picture of estate or tribal boundaries which included village communities; we find a variety of tools in bronze, and even whetstones which show that iron has been honed on them (although the scarce iron has not been found). Let me give you the picture in Professor S. -Piggott�s words: �The ... Late Bronze Age of southern England was but +Piggott�s words: �The ... Late Bronze Age of southern England was but the forerunner of the earliest Iron Age in the same region, not only in the techniques of agriculture, but almost certainly in terms of ethnic kinship ... we can with some assurance talk of the Celts ... the great early Celtic expansion of the Continent is recognized to be that of the -Urnfield people.� +Urnfield people.� Thus, certainly by 500 B.C., there were people in Britain, some of whose descendants we may recognize today in name or language in remote @@ -5399,11 +5399,11 @@ efficient set of tools than does bronze. Iron tools seem first to have been made in quantity in Hittite Anatolia about 1500 B.C. In continental Europe, the earliest, so-called Hallstatt, iron-using cultures appeared in Germany soon after 750 B.C. Somewhat later, -Greek and especially Etruscan exports of _objets d�art_--which moved +Greek and especially Etruscan exports of _objets d�art_--which moved with a flourishing trans-Alpine wine trade--influenced the Hallstatt iron-working tradition. Still later new classical motifs, together with older Hallstatt, oriental, and northern nomad motifs, gave rise to a -new style in metal decoration which characterizes the so-called La T�ne +new style in metal decoration which characterizes the so-called La T�ne phase. A few iron users reached Britain a little before 400 B.C. Not long @@ -5422,7 +5422,7 @@ HILL-FORTS AND FARMS The earliest iron-users seem to have entrenched themselves temporarily within hill-top forts, mainly in the south. Gradually, they moved inland, establishing _individual_ farm sites with extensive systems -of rectangular fields. We recognize these fields by the �lynchets� or +of rectangular fields. We recognize these fields by the �lynchets� or lines of soil-creep which plowing left on the slopes of hills. New crops appeared; there were now bread wheat, oats, and rye, as well as barley. @@ -5434,7 +5434,7 @@ various outbuildings and pits for the storage of grain. Weaving was done on the farm, but not blacksmithing, which must have been a specialized trade. Save for the lack of firearms, the place might almost be taken for a farmstead on the American frontier in the early -1800�s. +1800�s. Toward 250 B.C. there seems to have been a hasty attempt to repair the hill-forts and to build new ones, evidently in response to signs of @@ -5446,9 +5446,9 @@ THE SECOND PHASE Perhaps the hill-forts were not entirely effective or perhaps a compromise was reached. In any case, the newcomers from the Marne district did establish themselves, first in the southeast and then to -the north and west. They brought iron with decoration of the La T�ne +the north and west. They brought iron with decoration of the La T�ne type and also the two-wheeled chariot. Like the Wessex warriors of -over a thousand years earlier, they made �heroes�� graves, with their +over a thousand years earlier, they made �heroes�� graves, with their warriors buried in the war-chariots and dressed in full trappings. [Illustration: CELTIC BUCKLE] @@ -5457,7 +5457,7 @@ The metal work of these Marnian newcomers is excellent. The peculiar Celtic art style, based originally on the classic tendril motif, is colorful and virile, and fits with Greek and Roman descriptions of Celtic love of color in dress. There is a strong trace of these -newcomers northward in Yorkshire, linked by Ptolemy�s description to +newcomers northward in Yorkshire, linked by Ptolemy�s description to the Parisii, doubtless part of the Celtic tribe which originally gave its name to Paris on the Seine. Near Glastonbury, in Somerset, two villages in swamps have been excavated. They seem to date toward the @@ -5469,7 +5469,7 @@ villagers. In Scotland, which yields its first iron tools at a date of about 100 B.C., and in northern Ireland even slightly earlier, the effects of the -two phases of newcomers tend especially to blend. Hill-forts, �brochs� +two phases of newcomers tend especially to blend. Hill-forts, �brochs� (stone-built round towers) and a variety of other strange structures seem to appear as the new ideas develop in the comparative isolation of northern Britain. @@ -5493,27 +5493,27 @@ at last, we can even begin to speak of dynasties and individuals. Some time before 55 B.C., the Catuvellauni, originally from the Marne district in France, had possessed themselves of a large part of southeastern England. They evidently sailed up the Thames and built a -town of over a hundred acres in area. Here ruled Cassivellaunus, �the -first man in England whose name we know,� and whose town Caesar sacked. +town of over a hundred acres in area. Here ruled Cassivellaunus, �the +first man in England whose name we know,� and whose town Caesar sacked. The town sprang up elsewhere again, however. THE END OF PREHISTORY Prehistory, strictly speaking, is now over in southern Britain. -Claudius� effective invasion took place in 43 A.D.; by 83 A.D., a raid +Claudius� effective invasion took place in 43 A.D.; by 83 A.D., a raid had been made as far north as Aberdeen in Scotland. But by 127 A.D., Hadrian had completed his wall from the Solway to the Tyne, and the Romans settled behind it. In Scotland, Romanization can have affected -the countryside very little. Professor Piggott adds that �... it is +the countryside very little. Professor Piggott adds that �... it is when the pressure of Romanization is relaxed by the break-up of the Dark Ages that we see again the Celtic metal-smiths handling their material with the same consummate skill as they had before the Roman Conquest, and with traditional styles that had not even then forgotten -their Marnian and Belgic heritage.� +their Marnian and Belgic heritage.� In fact, many centuries go by, in Britain as well as in the rest of -Europe, before the archeologist�s task is complete and the historian on +Europe, before the archeologist�s task is complete and the historian on his own is able to describe the ways of men in the past. @@ -5524,7 +5524,7 @@ you will have noticed how often I had to refer to the European continent itself. Britain, beyond the English Channel for all of her later prehistory, had a much simpler course of events than did most of the rest of Europe in later prehistoric times. This holds, in spite -of all the �invasions� and �reverberations� from the continent. Most +of all the �invasions� and �reverberations� from the continent. Most of Europe was the scene of an even more complicated ebb and flow of cultural change, save in some of its more remote mountain valleys and peninsulas. @@ -5536,7 +5536,7 @@ accounts and some good general accounts of part of the range from about 3000 B.C. to A.D. 1. I suspect that the difficulty of making a good book that covers all of its later prehistory is another aspect of what makes Europe so very complicated a continent today. The prehistoric -foundations for Europe�s very complicated set of civilizations, +foundations for Europe�s very complicated set of civilizations, cultures, and sub-cultures--which begin to appear as history proceeds--were in themselves very complicated. @@ -5552,8 +5552,8 @@ of their journeys. But by the same token, they had had time en route to take on their characteristic European aspects. Some time ago, Sir Cyril Fox wrote a famous book called _The -Personality of Britain_, sub-titled �Its Influence on Inhabitant and -Invader in Prehistoric and Early Historic Times.� We have not gone +Personality of Britain_, sub-titled �Its Influence on Inhabitant and +Invader in Prehistoric and Early Historic Times.� We have not gone into the post-Roman early historic period here; there are still the Anglo-Saxons and Normans to account for as well as the effects of the Romans. But what I have tried to do was to begin the story of @@ -5570,7 +5570,7 @@ Summary In the pages you have read so far, you have been brought through the -earliest 99 per cent of the story of man�s life on this planet. I have +earliest 99 per cent of the story of man�s life on this planet. I have left only 1 per cent of the story for the historians to tell. @@ -5601,7 +5601,7 @@ But I think there may have been a few. Certainly the pace of the first act accelerated with the swing from simple gathering to more intensified collecting. The great cave art of France and Spain was probably an expression of a climax. Even the ideas of burying the dead -and of the �Venus� figurines must also point to levels of human thought +and of the �Venus� figurines must also point to levels of human thought and activity that were over and above pure food-getting. @@ -5629,7 +5629,7 @@ five thousand years after the second act began. But it could never have happened in the first act at all. There is another curious thing about the first act. Many of the players -didn�t know it was over and they kept on with their roles long after +didn�t know it was over and they kept on with their roles long after the second act had begun. On the edges of the stage there are today some players who are still going on with the first act. The Eskimos, and the native Australians, and certain tribes in the Amazon jungle are @@ -5680,20 +5680,20 @@ act may have lessons for us and give depth to our thinking. I know there are at least _some_ lessons, even in the present incomplete state of our knowledge. The players who began the second act--that of food-production--separately, in different parts of the world, were not -all of one �pure race� nor did they have �pure� cultural traditions. +all of one �pure race� nor did they have �pure� cultural traditions. Some apparently quite mixed Mediterraneans got off to the first start on the second act and brought it to its first two climaxes as well. Peoples of quite different physical type achieved the first climaxes in China and in the New World. In our British example of how the late prehistory of Europe worked, we -listed a continuous series of �invasions� and �reverberations.� After +listed a continuous series of �invasions� and �reverberations.� After each of these came fusion. Even though the Channel protected Britain from some of the extreme complications of the mixture and fusion of continental Europe, you can see how silly it would be to refer to a -�pure� British race or a �pure� British culture. We speak of the United -States as a �melting pot.� But this is nothing new. Actually, Britain -and all the rest of the world have been �melting pots� at one time or +�pure� British race or a �pure� British culture. We speak of the United +States as a �melting pot.� But this is nothing new. Actually, Britain +and all the rest of the world have been �melting pots� at one time or another. By the time the written records of Mesopotamia and Egypt begin to turn @@ -5703,12 +5703,12 @@ itself, we are thrown back on prehistoric archeology. And this is as true for China, India, Middle America, and the Andes, as it is for the Near East. -There are lessons to be learned from all of man�s past, not simply +There are lessons to be learned from all of man�s past, not simply lessons of how to fight battles or win peace conferences, but of how human society evolves from one stage to another. Many of these lessons can only be looked for in the prehistoric past. So far, we have only made a beginning. There is much still to do, and many gaps in the story -are yet to be filled. The prehistorian�s job is to find the evidence, +are yet to be filled. The prehistorian�s job is to find the evidence, to fill the gaps, and to discover the lessons men have learned in the past. As I see it, this is not only an exciting but a very practical goal for which to strive. @@ -5745,7 +5745,7 @@ paperbound books.) GEOCHRONOLOGY AND THE ICE AGE -(Two general books. Some Pleistocene geologists disagree with Zeuner�s +(Two general books. Some Pleistocene geologists disagree with Zeuner�s interpretation of the dating evidence, but their points of view appear in professional journals, in articles too cumbersome to list here.) @@ -5815,7 +5815,7 @@ GENERAL PREHISTORY Press. Movius, Hallam L., Jr. - �Old World Prehistory: Paleolithic� in _Anthropology Today_. + �Old World Prehistory: Paleolithic� in _Anthropology Today_. Kroeber, A. L., ed. 1953. University of Chicago Press. Oakley, Kenneth P. @@ -5826,7 +5826,7 @@ GENERAL PREHISTORY _British Prehistory._ 1949. Oxford University Press. Pittioni, Richard - _Die Urgeschichtlichen Grundlagen der Europ�ischen Kultur._ + _Die Urgeschichtlichen Grundlagen der Europ�ischen Kultur._ 1949. Deuticke. (A single book which does attempt to cover the whole range of European prehistory to ca. 1 A.D.) @@ -5834,7 +5834,7 @@ GENERAL PREHISTORY THE NEAR EAST Adams, Robert M. - �Developmental Stages in Ancient Mesopotamia,� _in_ Steward, + �Developmental Stages in Ancient Mesopotamia,� _in_ Steward, Julian, _et al_, _Irrigation Civilizations: A Comparative Study_. 1955. Pan American Union. @@ -6000,7 +6000,7 @@ Index Bolas, 54 - Bordes, Fran�ois, 62 + Bordes, Fran�ois, 62 Borer, 77 @@ -6028,7 +6028,7 @@ Index killed by stampede, 86 Burials, 66, 86; - in �henges,� 164; + in �henges,� 164; in urns, 168 Burins, 75 @@ -6085,7 +6085,7 @@ Index Combe Capelle, 30 - Combe Capelle-Br�nn group, 34 + Combe Capelle-Br�nn group, 34 Commont, Victor, 51 @@ -6097,7 +6097,7 @@ Index Corrals for cattle, 140 - �Cradle of mankind,� 136 + �Cradle of mankind,� 136 Cremation, 167 @@ -6123,7 +6123,7 @@ Index Domestication, of animals, 100, 105, 107; of plants, 100 - �Dragon teeth� fossils in China, 28 + �Dragon teeth� fossils in China, 28 Drill, 77 @@ -6176,9 +6176,9 @@ Index Fayum, 135; radiocarbon date, 146 - �Fertile Crescent,� 107, 146 + �Fertile Crescent,� 107, 146 - Figurines, �Venus,� 84; + Figurines, �Venus,� 84; at Jarmo, 128; at Ubaid, 153 @@ -6197,7 +6197,7 @@ Index Flint industry, 127 - Font�chevade, 32, 56, 58 + Font�chevade, 32, 56, 58 Food-collecting, 104, 121; end of, 104 @@ -6223,7 +6223,7 @@ Index Food-vessel folk, 164 - �Forest folk,� 97, 98, 104, 110 + �Forest folk,� 97, 98, 104, 110 Fox, Sir Cyril, 174 @@ -6379,7 +6379,7 @@ Index Land bridges in Mediterranean, 19 - La T�ne phase, 170 + La T�ne phase, 170 Laurel leaf point, 78, 89 @@ -6404,7 +6404,7 @@ Index Mammoth, 93; in cave art, 85 - �Man-apes,� 26 + �Man-apes,� 26 Mango, 107 @@ -6435,7 +6435,7 @@ Index Microliths, 87; at Jarmo, 130; - �lunates,� 87; + �lunates,� 87; trapezoids, 87; triangles, 87 @@ -6443,7 +6443,7 @@ Index Mine-shafts, 140 - M�lefaat, 126, 127 + M�lefaat, 126, 127 Mongoloids, 29, 90 @@ -6453,9 +6453,9 @@ Index Mount Carmel, 11, 33, 52, 59, 64, 69, 113, 114 - �Mousterian man,� 64 + �Mousterian man,� 64 - �Mousterian� tools, 61, 62; + �Mousterian� tools, 61, 62; of Acheulean tradition, 62 Movius, H. L., 47 @@ -6471,7 +6471,7 @@ Index Near East, beginnings of civilization in, 20, 144; cave sites, 58; climate in Ice Age, 99; - �Fertile Crescent,� 107, 146; + �Fertile Crescent,� 107, 146; food-production in, 99; Natufian assemblage in, 113-115; stone tools, 114 @@ -6539,7 +6539,7 @@ Index Pig, wild, 108 - �Piltdown man,� 29 + �Piltdown man,� 29 Pins, 80 @@ -6578,7 +6578,7 @@ Index Race, 35; biological, 36; - �pure,� 16 + �pure,� 16 Radioactivity, 9, 10 @@ -6795,7 +6795,7 @@ Index Writing, 158; cuneiform, 158 - W�rm I glaciation, 58 + W�rm I glaciation, 58 Zebu cattle, domestication of, 107 @@ -6810,7 +6810,7 @@ Index -Transcriber�s note: +Transcriber�s note: Punctuation, hyphenation, and spelling were made consistent when a predominant preference was found in this book; otherwise they were not diff --git a/ciphers/transposition_cipher_encrypt_decrypt_file.py b/ciphers/transposition_cipher_encrypt_decrypt_file.py index 6296b1e6d709..b9630243d7f3 100644 --- a/ciphers/transposition_cipher_encrypt_decrypt_file.py +++ b/ciphers/transposition_cipher_encrypt_decrypt_file.py @@ -6,8 +6,8 @@ def main() -> None: - input_file = "Prehistoric Men.txt" - output_file = "Output.txt" + input_file = "./prehistoric_men.txt" + output_file = "./Output.txt" key = int(input("Enter key: ")) mode = input("Encrypt/Decrypt [e/d]: ") From 24dbdd0b88bdfd4ddb940cf0b681075c66842cc3 Mon Sep 17 00:00:00 2001 From: Raghav <83136390+Raghav-Bell@users.noreply.github.com> Date: Wed, 4 Oct 2023 11:38:13 +0530 Subject: [PATCH 247/757] Update coulombs_law.py docs (#9667) * Update coulombs_law.py distance is positive non zero real number (float type) hence corrected docs which says only integer. * Update physics/coulombs_law.py --------- Co-authored-by: Tianyi Zheng --- physics/coulombs_law.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/physics/coulombs_law.py b/physics/coulombs_law.py index 252e8ec0f74e..fe2d358f653e 100644 --- a/physics/coulombs_law.py +++ b/physics/coulombs_law.py @@ -32,7 +32,7 @@ def coulombs_law(q1: float, q2: float, radius: float) -> float: 17975103584.6 """ if radius <= 0: - raise ValueError("The radius is always a positive non zero integer") + raise ValueError("The radius is always a positive number") return round(((8.9875517923 * 10**9) * q1 * q2) / (radius**2), 2) From 3fd3497f15982a7286326b520b5e7b52767da1f3 Mon Sep 17 00:00:00 2001 From: Siddhant Totade Date: Wed, 4 Oct 2023 14:55:26 +0530 Subject: [PATCH 248/757] Add Comments (#9668) * docs : add comment in circular_linked_list.py and swap_nodes.py * docs : improve comments * docs : improved docs and tested on pre-commit * docs : add comment in circular_linked_list.py and swap_nodes.py * docs : improve comments * docs : improved docs and tested on pre-commit * docs : modified comments * Update circular_linked_list.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * docs : improved * Update data_structures/linked_list/circular_linked_list.py Co-authored-by: Christian Clauss * Update data_structures/linked_list/circular_linked_list.py Co-authored-by: Christian Clauss * Update data_structures/linked_list/swap_nodes.py Co-authored-by: Christian Clauss * Update data_structures/linked_list/swap_nodes.py Co-authored-by: Christian Clauss * Update data_structures/linked_list/swap_nodes.py Co-authored-by: Christian Clauss * Update data_structures/linked_list/swap_nodes.py Co-authored-by: Christian Clauss * Update requirements.txt Co-authored-by: Christian Clauss * Update data_structures/linked_list/circular_linked_list.py Co-authored-by: Christian Clauss * Apply suggestions from code review * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update circular_linked_list.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- .../linked_list/circular_linked_list.py | 87 ++++++++++++++++--- data_structures/linked_list/swap_nodes.py | 47 ++++++++-- 2 files changed, 113 insertions(+), 21 deletions(-) diff --git a/data_structures/linked_list/circular_linked_list.py b/data_structures/linked_list/circular_linked_list.py index d9544f4263a6..72212f46be15 100644 --- a/data_structures/linked_list/circular_linked_list.py +++ b/data_structures/linked_list/circular_linked_list.py @@ -6,16 +6,29 @@ class Node: def __init__(self, data: Any): + """ + Initialize a new Node with the given data. + Args: + data: The data to be stored in the node. + """ self.data: Any = data - self.next: Node | None = None + self.next: Node | None = None # Reference to the next node class CircularLinkedList: - def __init__(self): - self.head = None - self.tail = None + def __init__(self) -> None: + """ + Initialize an empty Circular Linked List. + """ + self.head = None # Reference to the head (first node) + self.tail = None # Reference to the tail (last node) def __iter__(self) -> Iterator[Any]: + """ + Iterate through all nodes in the Circular Linked List yielding their data. + Yields: + The data of each node in the linked list. + """ node = self.head while self.head: yield node.data @@ -24,25 +37,48 @@ def __iter__(self) -> Iterator[Any]: break def __len__(self) -> int: + """ + Get the length (number of nodes) in the Circular Linked List. + """ return sum(1 for _ in self) - def __repr__(self): + def __repr__(self) -> str: + """ + Generate a string representation of the Circular Linked List. + Returns: + A string of the format "1->2->....->N". + """ return "->".join(str(item) for item in iter(self)) def insert_tail(self, data: Any) -> None: + """ + Insert a node with the given data at the end of the Circular Linked List. + """ self.insert_nth(len(self), data) def insert_head(self, data: Any) -> None: + """ + Insert a node with the given data at the beginning of the Circular Linked List. + """ self.insert_nth(0, data) def insert_nth(self, index: int, data: Any) -> None: + """ + Insert the data of the node at the nth pos in the Circular Linked List. + Args: + index: The index at which the data should be inserted. + data: The data to be inserted. + + Raises: + IndexError: If the index is out of range. + """ if index < 0 or index > len(self): raise IndexError("list index out of range.") new_node = Node(data) if self.head is None: - new_node.next = new_node # first node points itself + new_node.next = new_node # First node points to itself self.tail = self.head = new_node - elif index == 0: # insert at head + elif index == 0: # Insert at the head new_node.next = self.head self.head = self.tail.next = new_node else: @@ -51,22 +87,43 @@ def insert_nth(self, index: int, data: Any) -> None: temp = temp.next new_node.next = temp.next temp.next = new_node - if index == len(self) - 1: # insert at tail + if index == len(self) - 1: # Insert at the tail self.tail = new_node - def delete_front(self): + def delete_front(self) -> Any: + """ + Delete and return the data of the node at the front of the Circular Linked List. + Raises: + IndexError: If the list is empty. + """ return self.delete_nth(0) def delete_tail(self) -> Any: + """ + Delete and return the data of the node at the end of the Circular Linked List. + Returns: + Any: The data of the deleted node. + Raises: + IndexError: If the index is out of range. + """ return self.delete_nth(len(self) - 1) def delete_nth(self, index: int = 0) -> Any: + """ + Delete and return the data of the node at the nth pos in Circular Linked List. + Args: + index (int): The index of the node to be deleted. Defaults to 0. + Returns: + Any: The data of the deleted node. + Raises: + IndexError: If the index is out of range. + """ if not 0 <= index < len(self): raise IndexError("list index out of range.") delete_node = self.head - if self.head == self.tail: # just one node + if self.head == self.tail: # Just one node self.head = self.tail = None - elif index == 0: # delete head node + elif index == 0: # Delete head node self.tail.next = self.tail.next.next self.head = self.head.next else: @@ -75,16 +132,22 @@ def delete_nth(self, index: int = 0) -> Any: temp = temp.next delete_node = temp.next temp.next = temp.next.next - if index == len(self) - 1: # delete at tail + if index == len(self) - 1: # Delete at tail self.tail = temp return delete_node.data def is_empty(self) -> bool: + """ + Check if the Circular Linked List is empty. + Returns: + bool: True if the list is empty, False otherwise. + """ return len(self) == 0 def test_circular_linked_list() -> None: """ + Test cases for the CircularLinkedList class. >>> test_circular_linked_list() """ circular_linked_list = CircularLinkedList() diff --git a/data_structures/linked_list/swap_nodes.py b/data_structures/linked_list/swap_nodes.py index 3f825756b3d2..da6aa07a79fd 100644 --- a/data_structures/linked_list/swap_nodes.py +++ b/data_structures/linked_list/swap_nodes.py @@ -2,30 +2,56 @@ class Node: - def __init__(self, data: Any): + def __init__(self, data: Any) -> None: + """ + Initialize a new Node with the given data. + + Args: + data: The data to be stored in the node. + + """ self.data = data - self.next = None + self.next = None # Reference to the next node class LinkedList: - def __init__(self): - self.head = None + def __init__(self) -> None: + """ + Initialize an empty Linked List. + """ + self.head = None # Reference to the head (first node) def print_list(self): + """ + Print the elements of the Linked List in order. + """ temp = self.head while temp is not None: print(temp.data, end=" ") temp = temp.next print() - # adding nodes - def push(self, new_data: Any): + def push(self, new_data: Any) -> None: + """ + Add a new node with the given data to the beginning of the Linked List. + Args: + new_data (Any): The data to be added to the new node. + """ new_node = Node(new_data) new_node.next = self.head self.head = new_node - # swapping nodes - def swap_nodes(self, node_data_1, node_data_2): + def swap_nodes(self, node_data_1, node_data_2) -> None: + """ + Swap the positions of two nodes in the Linked List based on their data values. + Args: + node_data_1: Data value of the first node to be swapped. + node_data_2: Data value of the second node to be swapped. + + + Note: + If either of the specified data values isn't found then, no swapping occurs. + """ if node_data_1 == node_data_2: return else: @@ -40,6 +66,7 @@ def swap_nodes(self, node_data_1, node_data_2): if node_1 is None or node_2 is None: return + # Swap the data values of the two nodes node_1.data, node_2.data = node_2.data, node_1.data @@ -48,8 +75,10 @@ def swap_nodes(self, node_data_1, node_data_2): for i in range(5, 0, -1): ll.push(i) + print("Original Linked List:") ll.print_list() ll.swap_nodes(1, 4) - print("After swapping") + print("After swapping the nodes whose data is 1 and 4:") + ll.print_list() From dfdd78135df938d948ba3044aca628aca08886e7 Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Wed, 4 Oct 2023 12:05:00 -0400 Subject: [PATCH 249/757] Fix mypy errors in circular_linked_list.py and swap_nodes.py (#9707) * updating DIRECTORY.md * Fix mypy errors in circular_linked_list.py * Fix mypy errors in swap_nodes.py --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 5 ++--- .../linked_list/circular_linked_list.py | 22 +++++++++++++------ data_structures/linked_list/swap_nodes.py | 4 ++-- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index 9a913aa786e1..4f4cc423d678 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -541,8 +541,8 @@ * [Basic Maths](maths/basic_maths.py) * [Binary Exp Mod](maths/binary_exp_mod.py) * [Binary Exponentiation](maths/binary_exponentiation.py) - * [Binary Exponentiation 2](maths/binary_exponentiation_2.py) * [Binary Exponentiation 3](maths/binary_exponentiation_3.py) + * [Binary Multiplication](maths/binary_multiplication.py) * [Binomial Coefficient](maths/binomial_coefficient.py) * [Binomial Distribution](maths/binomial_distribution.py) * [Bisection](maths/bisection.py) @@ -557,8 +557,7 @@ * [Decimal Isolate](maths/decimal_isolate.py) * [Decimal To Fraction](maths/decimal_to_fraction.py) * [Dodecahedron](maths/dodecahedron.py) - * [Double Factorial Iterative](maths/double_factorial_iterative.py) - * [Double Factorial Recursive](maths/double_factorial_recursive.py) + * [Double Factorial](maths/double_factorial.py) * [Dual Number Automatic Differentiation](maths/dual_number_automatic_differentiation.py) * [Entropy](maths/entropy.py) * [Euclidean Distance](maths/euclidean_distance.py) diff --git a/data_structures/linked_list/circular_linked_list.py b/data_structures/linked_list/circular_linked_list.py index 72212f46be15..ef6658733a95 100644 --- a/data_structures/linked_list/circular_linked_list.py +++ b/data_structures/linked_list/circular_linked_list.py @@ -20,8 +20,8 @@ def __init__(self) -> None: """ Initialize an empty Circular Linked List. """ - self.head = None # Reference to the head (first node) - self.tail = None # Reference to the tail (last node) + self.head: Node | None = None # Reference to the head (first node) + self.tail: Node | None = None # Reference to the tail (last node) def __iter__(self) -> Iterator[Any]: """ @@ -30,7 +30,7 @@ def __iter__(self) -> Iterator[Any]: The data of each node in the linked list. """ node = self.head - while self.head: + while node: yield node.data node = node.next if node == self.head: @@ -74,17 +74,20 @@ def insert_nth(self, index: int, data: Any) -> None: """ if index < 0 or index > len(self): raise IndexError("list index out of range.") - new_node = Node(data) + new_node: Node = Node(data) if self.head is None: new_node.next = new_node # First node points to itself self.tail = self.head = new_node elif index == 0: # Insert at the head new_node.next = self.head + assert self.tail is not None # List is not empty, tail exists self.head = self.tail.next = new_node else: - temp = self.head + temp: Node | None = self.head for _ in range(index - 1): + assert temp is not None temp = temp.next + assert temp is not None new_node.next = temp.next temp.next = new_node if index == len(self) - 1: # Insert at the tail @@ -120,16 +123,21 @@ def delete_nth(self, index: int = 0) -> Any: """ if not 0 <= index < len(self): raise IndexError("list index out of range.") - delete_node = self.head + + assert self.head is not None and self.tail is not None + delete_node: Node = self.head if self.head == self.tail: # Just one node self.head = self.tail = None elif index == 0: # Delete head node + assert self.tail.next is not None self.tail.next = self.tail.next.next self.head = self.head.next else: - temp = self.head + temp: Node | None = self.head for _ in range(index - 1): + assert temp is not None temp = temp.next + assert temp is not None and temp.next is not None delete_node = temp.next temp.next = temp.next.next if index == len(self) - 1: # Delete at tail diff --git a/data_structures/linked_list/swap_nodes.py b/data_structures/linked_list/swap_nodes.py index da6aa07a79fd..31dcb02bfa9a 100644 --- a/data_structures/linked_list/swap_nodes.py +++ b/data_structures/linked_list/swap_nodes.py @@ -11,7 +11,7 @@ def __init__(self, data: Any) -> None: """ self.data = data - self.next = None # Reference to the next node + self.next: Node | None = None # Reference to the next node class LinkedList: @@ -19,7 +19,7 @@ def __init__(self) -> None: """ Initialize an empty Linked List. """ - self.head = None # Reference to the head (first node) + self.head: Node | None = None # Reference to the head (first node) def print_list(self): """ From d74349793b613b0948608409a572426a9800c3a1 Mon Sep 17 00:00:00 2001 From: halfhearted <99018821+Arunsiva003@users.noreply.github.com> Date: Wed, 4 Oct 2023 22:09:28 +0530 Subject: [PATCH 250/757] Arunsiva003 patch 1 flatten tree (#9695) * infix to prefix missing feature added * infix to prefix missing feature added * infix to prefix missing feature added * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * infix to prefix missing feature added (comments) * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * infix to prefix missing feature added (comments) * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * newly updated infix_to_prefix * newly updated infix_to_prefix_2 * newly updated infix_to_prefix_3 * from the beginning * Created flatten_binarytree_to_linkedlist.py * Update flatten_binarytree_to_linkedlist.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update flatten_binarytree_to_linkedlist.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update flatten_binarytree_to_linkedlist.py * Update flatten_binarytree_to_linkedlist.py * Update flatten_binarytree_to_linkedlist.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update flatten_binarytree_to_linkedlist.py (space added) * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update flatten_binarytree_to_linkedlist.py space added * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update flatten_binarytree_to_linkedlist.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * flatten binary tree to linked list - 1 * flatten binary tree to linked list final * flatten binary tree to linked list final * review updated * Update flatten_binarytree_to_linkedlist.py * Update .pre-commit-config.yaml * Update flatten_binarytree_to_linkedlist.py * Update flatten_binarytree_to_linkedlist.py --------- Co-authored-by: ArunSiva Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- .../flatten_binarytree_to_linkedlist.py | 138 ++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 data_structures/binary_tree/flatten_binarytree_to_linkedlist.py diff --git a/data_structures/binary_tree/flatten_binarytree_to_linkedlist.py b/data_structures/binary_tree/flatten_binarytree_to_linkedlist.py new file mode 100644 index 000000000000..8820a509ecba --- /dev/null +++ b/data_structures/binary_tree/flatten_binarytree_to_linkedlist.py @@ -0,0 +1,138 @@ +""" +Binary Tree Flattening Algorithm + +This code defines an algorithm to flatten a binary tree into a linked list +represented using the right pointers of the tree nodes. It uses in-place +flattening and demonstrates the flattening process along with a display +function to visualize the flattened linked list. +https://www.geeksforgeeks.org/flatten-a-binary-tree-into-linked-list + +Author: Arunkumar A +Date: 04/09/2023 +""" +from __future__ import annotations + + +class TreeNode: + """ + A TreeNode has data variable and pointers to TreeNode objects + for its left and right children. + """ + + def __init__(self, data: int) -> None: + self.data = data + self.left: TreeNode | None = None + self.right: TreeNode | None = None + + +def build_tree() -> TreeNode: + """ + Build and return a sample binary tree. + + Returns: + TreeNode: The root of the binary tree. + + Examples: + >>> root = build_tree() + >>> root.data + 1 + >>> root.left.data + 2 + >>> root.right.data + 5 + >>> root.left.left.data + 3 + >>> root.left.right.data + 4 + >>> root.right.right.data + 6 + """ + root = TreeNode(1) + root.left = TreeNode(2) + root.right = TreeNode(5) + root.left.left = TreeNode(3) + root.left.right = TreeNode(4) + root.right.right = TreeNode(6) + return root + + +def flatten(root: TreeNode | None) -> None: + """ + Flatten a binary tree into a linked list in-place, where the linked list is + represented using the right pointers of the tree nodes. + + Args: + root (TreeNode): The root of the binary tree to be flattened. + + Examples: + >>> root = TreeNode(1) + >>> root.left = TreeNode(2) + >>> root.right = TreeNode(5) + >>> root.left.left = TreeNode(3) + >>> root.left.right = TreeNode(4) + >>> root.right.right = TreeNode(6) + >>> flatten(root) + >>> root.data + 1 + >>> root.right.right is None + False + >>> root.right.right = TreeNode(3) + >>> root.right.right.right is None + True + """ + if not root: + return + + # Flatten the left subtree + flatten(root.left) + + # Save the right subtree + right_subtree = root.right + + # Make the left subtree the new right subtree + root.right = root.left + root.left = None + + # Find the end of the new right subtree + current = root + while current.right: + current = current.right + + # Append the original right subtree to the end + current.right = right_subtree + + # Flatten the updated right subtree + flatten(right_subtree) + + +def display_linked_list(root: TreeNode | None) -> None: + """ + Display the flattened linked list. + + Args: + root (TreeNode | None): The root of the flattened linked list. + + Examples: + >>> root = TreeNode(1) + >>> root.right = TreeNode(2) + >>> root.right.right = TreeNode(3) + >>> display_linked_list(root) + 1 2 3 + >>> root = None + >>> display_linked_list(root) + + """ + current = root + while current: + if current.right is None: + print(current.data, end="") + break + print(current.data, end=" ") + current = current.right + + +if __name__ == "__main__": + print("Flattened Linked List:") + root = build_tree() + flatten(root) + display_linked_list(root) From 922d6a88b3be2ff0dd69dd47d90e40aa95afd105 Mon Sep 17 00:00:00 2001 From: Bama Charan Chhandogi Date: Wed, 4 Oct 2023 22:51:46 +0530 Subject: [PATCH 251/757] add median of matrix (#9363) * add median of matrix * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * update * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix formating * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Apply suggestions from code review --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- matrix/median_matrix.py | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 matrix/median_matrix.py diff --git a/matrix/median_matrix.py b/matrix/median_matrix.py new file mode 100644 index 000000000000..116e609a587c --- /dev/null +++ b/matrix/median_matrix.py @@ -0,0 +1,38 @@ +""" +https://en.wikipedia.org/wiki/Median +""" + + +def median(matrix: list[list[int]]) -> int: + """ + Calculate the median of a sorted matrix. + + Args: + matrix: A 2D matrix of integers. + + Returns: + The median value of the matrix. + + Examples: + >>> matrix = [[1, 3, 5], [2, 6, 9], [3, 6, 9]] + >>> median(matrix) + 5 + + >>> matrix = [[1, 2, 3], [4, 5, 6]] + >>> median(matrix) + 3 + """ + # Flatten the matrix into a sorted 1D list + linear = sorted(num for row in matrix for num in row) + + # Calculate the middle index + mid = (len(linear) - 1) // 2 + + # Return the median + return linear[mid] + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From d5806258d4f9eb0e5652e1edfac0613aacb71fb6 Mon Sep 17 00:00:00 2001 From: Bama Charan Chhandogi Date: Wed, 4 Oct 2023 23:48:59 +0530 Subject: [PATCH 252/757] add median of two sorted array (#9386) * add median of two sorted array * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix syntax * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix syntax * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * improve code * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * add documentation * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * update * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- data_structures/arrays/median_two_array.py | 61 ++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 data_structures/arrays/median_two_array.py diff --git a/data_structures/arrays/median_two_array.py b/data_structures/arrays/median_two_array.py new file mode 100644 index 000000000000..972b0ee44201 --- /dev/null +++ b/data_structures/arrays/median_two_array.py @@ -0,0 +1,61 @@ +""" +https://www.enjoyalgorithms.com/blog/median-of-two-sorted-arrays +""" + + +def find_median_sorted_arrays(nums1: list[int], nums2: list[int]) -> float: + """ + Find the median of two arrays. + + Args: + nums1: The first array. + nums2: The second array. + + Returns: + The median of the two arrays. + + Examples: + >>> find_median_sorted_arrays([1, 3], [2]) + 2.0 + + >>> find_median_sorted_arrays([1, 2], [3, 4]) + 2.5 + + >>> find_median_sorted_arrays([0, 0], [0, 0]) + 0.0 + + >>> find_median_sorted_arrays([], []) + Traceback (most recent call last): + ... + ValueError: Both input arrays are empty. + + >>> find_median_sorted_arrays([], [1]) + 1.0 + + >>> find_median_sorted_arrays([-1000], [1000]) + 0.0 + + >>> find_median_sorted_arrays([-1.1, -2.2], [-3.3, -4.4]) + -2.75 + """ + if not nums1 and not nums2: + raise ValueError("Both input arrays are empty.") + + # Merge the arrays into a single sorted array. + merged = sorted(nums1 + nums2) + total = len(merged) + + if total % 2 == 1: # If the total number of elements is odd + return float(merged[total // 2]) # then return the middle element + + # If the total number of elements is even, calculate + # the average of the two middle elements as the median. + middle1 = merged[total // 2 - 1] + middle2 = merged[total // 2] + return (float(middle1) + float(middle2)) / 2.0 + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From c16d2f8865c8ce28ae6d4d815d3f6c3008e94f74 Mon Sep 17 00:00:00 2001 From: Muhammad Umer Farooq <115654418+Muhammadummerr@users.noreply.github.com> Date: Wed, 4 Oct 2023 23:43:17 +0500 Subject: [PATCH 253/757] UPDATED rat_in_maze.py (#9148) * UPDATED rat_in_maze.py * Update reddit.py in Webprogramming b/c it was causing error in pre-commit tests while raising PR. * UPDATED rat_in_maze.py * fixed return type to only maze,otherwise raise valueError. * fixed whitespaces error,improved matrix visual. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * updated. * Try * updated * updated * Apply suggestions from code review --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- backtracking/rat_in_maze.py | 181 ++++++++++++++++++++++++++---------- 1 file changed, 130 insertions(+), 51 deletions(-) diff --git a/backtracking/rat_in_maze.py b/backtracking/rat_in_maze.py index 7bde886dd558..626c83cb4a15 100644 --- a/backtracking/rat_in_maze.py +++ b/backtracking/rat_in_maze.py @@ -1,91 +1,164 @@ from __future__ import annotations -def solve_maze(maze: list[list[int]]) -> bool: +def solve_maze( + maze: list[list[int]], + source_row: int, + source_column: int, + destination_row: int, + destination_column: int, +) -> list[list[int]]: """ This method solves the "rat in maze" problem. - In this problem we have some n by n matrix, a start point and an end point. - We want to go from the start to the end. In this matrix zeroes represent walls - and ones paths we can use. Parameters : - maze(2D matrix) : maze + - maze: A two dimensional matrix of zeros and ones. + - source_row: The row index of the starting point. + - source_column: The column index of the starting point. + - destination_row: The row index of the destination point. + - destination_column: The column index of the destination point. Returns: - Return: True if the maze has a solution or False if it does not. + - solution: A 2D matrix representing the solution path if it exists. + Raises: + - ValueError: If no solution exists or if the source or + destination coordinates are invalid. + Description: + This method navigates through a maze represented as an n by n matrix, + starting from a specified source cell and + aiming to reach a destination cell. + The maze consists of walls (1s) and open paths (0s). + By providing custom row and column values, the source and destination + cells can be adjusted. >>> maze = [[0, 1, 0, 1, 1], ... [0, 0, 0, 0, 0], ... [1, 0, 1, 0, 1], ... [0, 0, 1, 0, 0], ... [1, 0, 0, 1, 0]] - >>> solve_maze(maze) - [1, 0, 0, 0, 0] - [1, 1, 1, 1, 0] - [0, 0, 0, 1, 0] - [0, 0, 0, 1, 1] - [0, 0, 0, 0, 1] - True + >>> solve_maze(maze,0,0,len(maze)-1,len(maze)-1) # doctest: +NORMALIZE_WHITESPACE + [[0, 1, 1, 1, 1], + [0, 0, 0, 0, 1], + [1, 1, 1, 0, 1], + [1, 1, 1, 0, 0], + [1, 1, 1, 1, 0]] + + Note: + In the output maze, the zeros (0s) represent one of the possible + paths from the source to the destination. >>> maze = [[0, 1, 0, 1, 1], ... [0, 0, 0, 0, 0], ... [0, 0, 0, 0, 1], ... [0, 0, 0, 0, 0], ... [0, 0, 0, 0, 0]] - >>> solve_maze(maze) - [1, 0, 0, 0, 0] - [1, 0, 0, 0, 0] - [1, 0, 0, 0, 0] - [1, 0, 0, 0, 0] - [1, 1, 1, 1, 1] - True + >>> solve_maze(maze,0,0,len(maze)-1,len(maze)-1) # doctest: +NORMALIZE_WHITESPACE + [[0, 1, 1, 1, 1], + [0, 1, 1, 1, 1], + [0, 1, 1, 1, 1], + [0, 1, 1, 1, 1], + [0, 0, 0, 0, 0]] >>> maze = [[0, 0, 0], ... [0, 1, 0], ... [1, 0, 0]] - >>> solve_maze(maze) - [1, 1, 1] - [0, 0, 1] - [0, 0, 1] - True + >>> solve_maze(maze,0,0,len(maze)-1,len(maze)-1) # doctest: +NORMALIZE_WHITESPACE + [[0, 0, 0], + [1, 1, 0], + [1, 1, 0]] - >>> maze = [[0, 1, 0], + >>> maze = [[1, 0, 0], ... [0, 1, 0], ... [1, 0, 0]] - >>> solve_maze(maze) - No solution exists! - False + >>> solve_maze(maze,0,1,len(maze)-1,len(maze)-1) # doctest: +NORMALIZE_WHITESPACE + [[1, 0, 0], + [1, 1, 0], + [1, 1, 0]] + + >>> maze = [[1, 1, 0, 0, 1, 0, 0, 1], + ... [1, 0, 1, 0, 0, 1, 1, 1], + ... [0, 1, 0, 1, 0, 0, 1, 0], + ... [1, 1, 1, 0, 0, 1, 0, 1], + ... [0, 1, 0, 0, 1, 0, 1, 1], + ... [0, 0, 0, 1, 1, 1, 0, 1], + ... [0, 1, 0, 1, 0, 1, 1, 1], + ... [1, 1, 0, 0, 0, 0, 0, 1]] + >>> solve_maze(maze,0,2,len(maze)-1,2) # doctest: +NORMALIZE_WHITESPACE + [[1, 1, 0, 0, 1, 1, 1, 1], + [1, 1, 1, 0, 0, 1, 1, 1], + [1, 1, 1, 1, 0, 1, 1, 1], + [1, 1, 1, 0, 0, 1, 1, 1], + [1, 1, 0, 0, 1, 1, 1, 1], + [1, 1, 0, 1, 1, 1, 1, 1], + [1, 1, 0, 1, 1, 1, 1, 1], + [1, 1, 0, 1, 1, 1, 1, 1]] + >>> maze = [[1, 0, 0], + ... [0, 1, 1], + ... [1, 0, 1]] + >>> solve_maze(maze,0,1,len(maze)-1,len(maze)-1) + Traceback (most recent call last): + ... + ValueError: No solution exists! + + >>> maze = [[0, 0], + ... [1, 1]] + >>> solve_maze(maze,0,0,len(maze)-1,len(maze)-1) + Traceback (most recent call last): + ... + ValueError: No solution exists! >>> maze = [[0, 1], ... [1, 0]] - >>> solve_maze(maze) - No solution exists! - False + >>> solve_maze(maze,2,0,len(maze)-1,len(maze)-1) + Traceback (most recent call last): + ... + ValueError: Invalid source or destination coordinates + + >>> maze = [[1, 0, 0], + ... [0, 1, 0], + ... [1, 0, 0]] + >>> solve_maze(maze,0,1,len(maze),len(maze)-1) + Traceback (most recent call last): + ... + ValueError: Invalid source or destination coordinates """ size = len(maze) + # Check if source and destination coordinates are Invalid. + if not (0 <= source_row <= size - 1 and 0 <= source_column <= size - 1) or ( + not (0 <= destination_row <= size - 1 and 0 <= destination_column <= size - 1) + ): + raise ValueError("Invalid source or destination coordinates") # We need to create solution object to save path. - solutions = [[0 for _ in range(size)] for _ in range(size)] - solved = run_maze(maze, 0, 0, solutions) + solutions = [[1 for _ in range(size)] for _ in range(size)] + solved = run_maze( + maze, source_row, source_column, destination_row, destination_column, solutions + ) if solved: - print("\n".join(str(row) for row in solutions)) + return solutions else: - print("No solution exists!") - return solved + raise ValueError("No solution exists!") -def run_maze(maze: list[list[int]], i: int, j: int, solutions: list[list[int]]) -> bool: +def run_maze( + maze: list[list[int]], + i: int, + j: int, + destination_row: int, + destination_column: int, + solutions: list[list[int]], +) -> bool: """ This method is recursive starting from (i, j) and going in one of four directions: up, down, left, right. If a path is found to destination it returns True otherwise it returns False. - Parameters: - maze(2D matrix) : maze + Parameters + maze: A two dimensional matrix of zeros and ones. i, j : coordinates of matrix - solutions(2D matrix) : solutions + solutions: A two dimensional matrix of solutions. Returns: Boolean if path is found True, Otherwise False. """ size = len(maze) # Final check point. - if i == j == (size - 1): - solutions[i][j] = 1 + if i == destination_row and j == destination_column and maze[i][j] == 0: + solutions[i][j] = 0 return True lower_flag = (not i < 0) and (not j < 0) # Check lower bounds @@ -93,21 +166,27 @@ def run_maze(maze: list[list[int]], i: int, j: int, solutions: list[list[int]]) if lower_flag and upper_flag: # check for already visited and block points. - block_flag = (not solutions[i][j]) and (not maze[i][j]) + block_flag = (solutions[i][j]) and (not maze[i][j]) if block_flag: # check visited - solutions[i][j] = 1 + solutions[i][j] = 0 # check for directions if ( - run_maze(maze, i + 1, j, solutions) - or run_maze(maze, i, j + 1, solutions) - or run_maze(maze, i - 1, j, solutions) - or run_maze(maze, i, j - 1, solutions) + run_maze(maze, i + 1, j, destination_row, destination_column, solutions) + or run_maze( + maze, i, j + 1, destination_row, destination_column, solutions + ) + or run_maze( + maze, i - 1, j, destination_row, destination_column, solutions + ) + or run_maze( + maze, i, j - 1, destination_row, destination_column, solutions + ) ): return True - solutions[i][j] = 0 + solutions[i][j] = 1 return False return False @@ -115,4 +194,4 @@ def run_maze(maze: list[list[int]], i: int, j: int, solutions: list[list[int]]) if __name__ == "__main__": import doctest - doctest.testmod() + doctest.testmod(optionflags=doctest.NORMALIZE_WHITESPACE) From 26d650ec2820e265e69c88608959a3e18f28c5d5 Mon Sep 17 00:00:00 2001 From: piyush-poddar <143445461+piyush-poddar@users.noreply.github.com> Date: Thu, 5 Oct 2023 01:58:19 +0530 Subject: [PATCH 254/757] Moved relu.py from maths/ to neural_network/activation_functions (#9753) * Moved file relu.py from maths/ to neural_network/activation_functions * Renamed relu.py to rectified_linear_unit.py * Renamed relu.py to rectified_linear_unit.py in DIRECTORY.md --- DIRECTORY.md | 2 +- .../activation_functions/rectified_linear_unit.py | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename maths/relu.py => neural_network/activation_functions/rectified_linear_unit.py (100%) diff --git a/DIRECTORY.md b/DIRECTORY.md index 4f4cc423d678..696a059bb4c8 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -639,7 +639,6 @@ * [Quadratic Equations Complex Numbers](maths/quadratic_equations_complex_numbers.py) * [Radians](maths/radians.py) * [Radix2 Fft](maths/radix2_fft.py) - * [Relu](maths/relu.py) * [Remove Digit](maths/remove_digit.py) * [Runge Kutta](maths/runge_kutta.py) * [Segmented Sieve](maths/segmented_sieve.py) @@ -710,6 +709,7 @@ * [Exponential Linear Unit](neural_network/activation_functions/exponential_linear_unit.py) * [Leaky Rectified Linear Unit](neural_network/activation_functions/leaky_rectified_linear_unit.py) * [Scaled Exponential Linear Unit](neural_network/activation_functions/scaled_exponential_linear_unit.py) + * [Rectified Linear Unit](neural_network/activation_functions/rectified_linear_unit.py) * [Back Propagation Neural Network](neural_network/back_propagation_neural_network.py) * [Convolution Neural Network](neural_network/convolution_neural_network.py) * [Perceptron](neural_network/perceptron.py) diff --git a/maths/relu.py b/neural_network/activation_functions/rectified_linear_unit.py similarity index 100% rename from maths/relu.py rename to neural_network/activation_functions/rectified_linear_unit.py From 6a391d113d8f0efdd69e69c8da7b44766594449a Mon Sep 17 00:00:00 2001 From: Raghav <83136390+Raghav-Bell@users.noreply.github.com> Date: Thu, 5 Oct 2023 04:46:19 +0530 Subject: [PATCH 255/757] Added Photoelectric effect equation (#9666) * Added Photoelectric effect equation Photoelectric effect is one of the demonstration of quanta of energy. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fixed doctest Co-authored-by: Rohan Anand <96521078+rohan472000@users.noreply.github.com> * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Rohan Anand <96521078+rohan472000@users.noreply.github.com> --- physics/photoelectric_effect.py | 67 +++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 physics/photoelectric_effect.py diff --git a/physics/photoelectric_effect.py b/physics/photoelectric_effect.py new file mode 100644 index 000000000000..3a0138ffe045 --- /dev/null +++ b/physics/photoelectric_effect.py @@ -0,0 +1,67 @@ +""" +The photoelectric effect is the emission of electrons when electromagnetic radiation , +such as light, hits a material. Electrons emitted in this manner are called +photoelectrons. + +In 1905, Einstein proposed a theory of the photoelectric effect using a concept that +light consists of tiny packets of energy known as photons or light quanta. Each packet +carries energy hv that is proportional to the frequency v of the corresponding +electromagnetic wave. The proportionality constant h has become known as the +Planck constant. In the range of kinetic energies of the electrons that are removed from +their varying atomic bindings by the absorption of a photon of energy hv, the highest +kinetic energy K_max is : + +K_max = hv-W + +Here, W is the minimum energy required to remove an electron from the surface of the +material. It is called the work function of the surface + +Reference: https://en.wikipedia.org/wiki/Photoelectric_effect + +""" + +PLANCK_CONSTANT_JS = 6.6261 * pow(10, -34) # in SI (Js) +PLANCK_CONSTANT_EVS = 4.1357 * pow(10, -15) # in eVs + + +def maximum_kinetic_energy( + frequency: float, work_function: float, in_ev: bool = False +) -> float: + """ + Calculates the maximum kinetic energy of emitted electron from the surface. + if the maximum kinetic energy is zero then no electron will be emitted + or given electromagnetic wave frequency is small. + + frequency (float): Frequency of electromagnetic wave. + work_function (float): Work function of the surface. + in_ev (optional)(bool): Pass True if values are in eV. + + Usage example: + >>> maximum_kinetic_energy(1000000,2) + 0 + >>> maximum_kinetic_energy(1000000,2,True) + 0 + >>> maximum_kinetic_energy(10000000000000000,2,True) + 39.357000000000006 + >>> maximum_kinetic_energy(-9,20) + Traceback (most recent call last): + ... + ValueError: Frequency can't be negative. + + >>> maximum_kinetic_energy(1000,"a") + Traceback (most recent call last): + ... + TypeError: unsupported operand type(s) for -: 'float' and 'str' + + """ + if frequency < 0: + raise ValueError("Frequency can't be negative.") + if in_ev: + return max(PLANCK_CONSTANT_EVS * frequency - work_function, 0) + return max(PLANCK_CONSTANT_JS * frequency - work_function, 0) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 2fd43c0f7ff1d7f72fa65a528ddabccf90c89a0d Mon Sep 17 00:00:00 2001 From: Tauseef Hilal Tantary Date: Thu, 5 Oct 2023 05:03:12 +0530 Subject: [PATCH 256/757] [New Algorithm] - Bell Numbers (#9324) * Add Bell Numbers * Use descriptive variable names * Add type hints * Fix failing tests --- maths/bell_numbers.py | 78 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 maths/bell_numbers.py diff --git a/maths/bell_numbers.py b/maths/bell_numbers.py new file mode 100644 index 000000000000..660ec6e6aa09 --- /dev/null +++ b/maths/bell_numbers.py @@ -0,0 +1,78 @@ +""" +Bell numbers represent the number of ways to partition a set into non-empty +subsets. This module provides functions to calculate Bell numbers for sets of +integers. In other words, the first (n + 1) Bell numbers. + +For more information about Bell numbers, refer to: +https://en.wikipedia.org/wiki/Bell_number +""" + + +def bell_numbers(max_set_length: int) -> list[int]: + """ + Calculate Bell numbers for the sets of lengths from 0 to max_set_length. + In other words, calculate first (max_set_length + 1) Bell numbers. + + Args: + max_set_length (int): The maximum length of the sets for which + Bell numbers are calculated. + + Returns: + list: A list of Bell numbers for sets of lengths from 0 to max_set_length. + + Examples: + >>> bell_numbers(0) + [1] + >>> bell_numbers(1) + [1, 1] + >>> bell_numbers(5) + [1, 1, 2, 5, 15, 52] + """ + if max_set_length < 0: + raise ValueError("max_set_length must be non-negative") + + bell = [0] * (max_set_length + 1) + bell[0] = 1 + + for i in range(1, max_set_length + 1): + for j in range(i): + bell[i] += _binomial_coefficient(i - 1, j) * bell[j] + + return bell + + +def _binomial_coefficient(total_elements: int, elements_to_choose: int) -> int: + """ + Calculate the binomial coefficient C(total_elements, elements_to_choose) + + Args: + total_elements (int): The total number of elements. + elements_to_choose (int): The number of elements to choose. + + Returns: + int: The binomial coefficient C(total_elements, elements_to_choose). + + Examples: + >>> _binomial_coefficient(5, 2) + 10 + >>> _binomial_coefficient(6, 3) + 20 + """ + if elements_to_choose in {0, total_elements}: + return 1 + + if elements_to_choose > total_elements - elements_to_choose: + elements_to_choose = total_elements - elements_to_choose + + coefficient = 1 + for i in range(elements_to_choose): + coefficient *= total_elements - i + coefficient //= i + 1 + + return coefficient + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 1fda96b7044d9fa08c84f09f54a345ebf052b2eb Mon Sep 17 00:00:00 2001 From: Sanket Kittad <86976526+sanketkittad@users.noreply.github.com> Date: Thu, 5 Oct 2023 05:10:14 +0530 Subject: [PATCH 257/757] Palindromic (#9288) * added longest palindromic subsequence * removed * added longest palindromic subsequence * added longest palindromic subsequence link * added comments --- .../longest_palindromic_subsequence.py | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 dynamic_programming/longest_palindromic_subsequence.py diff --git a/dynamic_programming/longest_palindromic_subsequence.py b/dynamic_programming/longest_palindromic_subsequence.py new file mode 100644 index 000000000000..a60d95e460e6 --- /dev/null +++ b/dynamic_programming/longest_palindromic_subsequence.py @@ -0,0 +1,44 @@ +""" +author: Sanket Kittad +Given a string s, find the longest palindromic subsequence's length in s. +Input: s = "bbbab" +Output: 4 +Explanation: One possible longest palindromic subsequence is "bbbb". +Leetcode link: https://leetcode.com/problems/longest-palindromic-subsequence/description/ +""" + + +def longest_palindromic_subsequence(input_string: str) -> int: + """ + This function returns the longest palindromic subsequence in a string + >>> longest_palindromic_subsequence("bbbab") + 4 + >>> longest_palindromic_subsequence("bbabcbcab") + 7 + """ + n = len(input_string) + rev = input_string[::-1] + m = len(rev) + dp = [[-1] * (m + 1) for i in range(n + 1)] + for i in range(n + 1): + dp[i][0] = 0 + for i in range(m + 1): + dp[0][i] = 0 + + # create and initialise dp array + for i in range(1, n + 1): + for j in range(1, m + 1): + # If characters at i and j are the same + # include them in the palindromic subsequence + if input_string[i - 1] == rev[j - 1]: + dp[i][j] = 1 + dp[i - 1][j - 1] + else: + dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]) + + return dp[n][m] + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 935d1d3225ede4c0650165d5dfd8f5eb35b54f5e Mon Sep 17 00:00:00 2001 From: Vipin Karthic <143083087+vipinkarthic@users.noreply.github.com> Date: Thu, 5 Oct 2023 11:27:55 +0530 Subject: [PATCH 258/757] Added Mirror Formulae Equation (#9717) * Python mirror_formulae.py is added to the repository * Changes done after reading readme.md * Changes for running doctest on all platforms * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Change 2 for Doctests * Changes for doctest 2 * updating DIRECTORY.md --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 9 ++- physics/mirror_formulae.py | 127 +++++++++++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+), 1 deletion(-) create mode 100644 physics/mirror_formulae.py diff --git a/DIRECTORY.md b/DIRECTORY.md index 696a059bb4c8..5f23cbd6c922 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -170,6 +170,7 @@ ## Data Structures * Arrays + * [Median Two Array](data_structures/arrays/median_two_array.py) * [Permutations](data_structures/arrays/permutations.py) * [Prefix Sum](data_structures/arrays/prefix_sum.py) * [Product Sum](data_structures/arrays/product_sum.py) @@ -185,6 +186,7 @@ * [Diff Views Of Binary Tree](data_structures/binary_tree/diff_views_of_binary_tree.py) * [Distribute Coins](data_structures/binary_tree/distribute_coins.py) * [Fenwick Tree](data_structures/binary_tree/fenwick_tree.py) + * [Flatten Binarytree To Linkedlist](data_structures/binary_tree/flatten_binarytree_to_linkedlist.py) * [Inorder Tree Traversal 2022](data_structures/binary_tree/inorder_tree_traversal_2022.py) * [Is Bst](data_structures/binary_tree/is_bst.py) * [Lazy Segment Tree](data_structures/binary_tree/lazy_segment_tree.py) @@ -324,6 +326,7 @@ * [Longest Common Substring](dynamic_programming/longest_common_substring.py) * [Longest Increasing Subsequence](dynamic_programming/longest_increasing_subsequence.py) * [Longest Increasing Subsequence O(Nlogn)](dynamic_programming/longest_increasing_subsequence_o(nlogn).py) + * [Longest Palindromic Subsequence](dynamic_programming/longest_palindromic_subsequence.py) * [Longest Sub Array](dynamic_programming/longest_sub_array.py) * [Matrix Chain Order](dynamic_programming/matrix_chain_order.py) * [Max Non Adjacent Sum](dynamic_programming/max_non_adjacent_sum.py) @@ -539,6 +542,7 @@ * [Average Mode](maths/average_mode.py) * [Bailey Borwein Plouffe](maths/bailey_borwein_plouffe.py) * [Basic Maths](maths/basic_maths.py) + * [Bell Numbers](maths/bell_numbers.py) * [Binary Exp Mod](maths/binary_exp_mod.py) * [Binary Exponentiation](maths/binary_exponentiation.py) * [Binary Exponentiation 3](maths/binary_exponentiation_3.py) @@ -690,6 +694,7 @@ * [Matrix Class](matrix/matrix_class.py) * [Matrix Operation](matrix/matrix_operation.py) * [Max Area Of Island](matrix/max_area_of_island.py) + * [Median Matrix](matrix/median_matrix.py) * [Nth Fibonacci Using Matrix Exponentiation](matrix/nth_fibonacci_using_matrix_exponentiation.py) * [Pascal Triangle](matrix/pascal_triangle.py) * [Rotate Matrix](matrix/rotate_matrix.py) @@ -708,8 +713,8 @@ * Activation Functions * [Exponential Linear Unit](neural_network/activation_functions/exponential_linear_unit.py) * [Leaky Rectified Linear Unit](neural_network/activation_functions/leaky_rectified_linear_unit.py) - * [Scaled Exponential Linear Unit](neural_network/activation_functions/scaled_exponential_linear_unit.py) * [Rectified Linear Unit](neural_network/activation_functions/rectified_linear_unit.py) + * [Scaled Exponential Linear Unit](neural_network/activation_functions/scaled_exponential_linear_unit.py) * [Back Propagation Neural Network](neural_network/back_propagation_neural_network.py) * [Convolution Neural Network](neural_network/convolution_neural_network.py) * [Perceptron](neural_network/perceptron.py) @@ -756,9 +761,11 @@ * [Kinetic Energy](physics/kinetic_energy.py) * [Lorentz Transformation Four Vector](physics/lorentz_transformation_four_vector.py) * [Malus Law](physics/malus_law.py) + * [Mirror Formulae](physics/mirror_formulae.py) * [N Body Simulation](physics/n_body_simulation.py) * [Newtons Law Of Gravitation](physics/newtons_law_of_gravitation.py) * [Newtons Second Law Of Motion](physics/newtons_second_law_of_motion.py) + * [Photoelectric Effect](physics/photoelectric_effect.py) * [Potential Energy](physics/potential_energy.py) * [Rms Speed Of Molecule](physics/rms_speed_of_molecule.py) * [Shear Stress](physics/shear_stress.py) diff --git a/physics/mirror_formulae.py b/physics/mirror_formulae.py new file mode 100644 index 000000000000..f1b4ac2c7baf --- /dev/null +++ b/physics/mirror_formulae.py @@ -0,0 +1,127 @@ +""" +This module contains the functions to calculate the focal length, object distance +and image distance of a mirror. + +The mirror formula is an equation that relates the object distance (u), +image distance (v), and focal length (f) of a spherical mirror. +It is commonly used in optics to determine the position and characteristics +of an image formed by a mirror. It is expressed using the formulae : + +------------------- +| 1/f = 1/v + 1/u | +------------------- + +Where, +f = Focal length of the spherical mirror (metre) +v = Image distance from the mirror (metre) +u = Object distance from the mirror (metre) + + +The signs of the distances are taken with respect to the sign convention. +The sign convention is as follows: + 1) Object is always placed to the left of mirror + 2) Distances measured in the direction of the incident ray are positive + and the distances measured in the direction opposite to that of the incident + rays are negative. + 3) All distances are measured from the pole of the mirror. + + +There are a few assumptions that are made while using the mirror formulae. +They are as follows: + 1) Thin Mirror: The mirror is assumed to be thin, meaning its thickness is + negligible compared to its radius of curvature. This assumption allows + us to treat the mirror as a two-dimensional surface. + 2) Spherical Mirror: The mirror is assumed to have a spherical shape. While this + assumption may not hold exactly for all mirrors, it is a reasonable approximation + for most practical purposes. + 3) Small Angles: The angles involved in the derivation are assumed to be small. + This assumption allows us to use the small-angle approximation, where the tangent + of a small angle is approximately equal to the angle itself. It simplifies the + calculations and makes the derivation more manageable. + 4) Paraxial Rays: The mirror formula is derived using paraxial rays, which are + rays that are close to the principal axis and make small angles with it. This + assumption ensures that the rays are close enough to the principal axis, making the + calculations more accurate. + 5) Reflection and Refraction Laws: The derivation assumes that the laws of + reflection and refraction hold. + These laws state that the angle of incidence is equal to the angle of reflection + for reflection, and the incident and refracted rays lie in the same plane and + obey Snell's law for refraction. + +(Description and Assumptions adapted from +https://www.collegesearch.in/articles/mirror-formula-derivation) + +(Sign Convention adapted from +https://www.toppr.com/ask/content/concept/sign-convention-for-mirrors-210189/) + + +""" + + +def focal_length(distance_of_object: float, distance_of_image: float) -> float: + """ + >>> from math import isclose + >>> isclose(focal_length(10, 20), 6.66666666666666) + True + >>> from math import isclose + >>> isclose(focal_length(9.5, 6.7), 3.929012346) + True + >>> focal_length(0, 20) + Traceback (most recent call last): + ... + ValueError: Invalid inputs. Enter non zero values with respect + to the sign convention. + """ + + if distance_of_object == 0 or distance_of_image == 0: + raise ValueError( + "Invalid inputs. Enter non zero values with respect to the sign convention." + ) + focal_length = 1 / ((1 / distance_of_object) + (1 / distance_of_image)) + return focal_length + + +def object_distance(focal_length: float, distance_of_image: float) -> float: + """ + >>> from math import isclose + >>> isclose(object_distance(30, 20), -60.0) + True + >>> from math import isclose + >>> isclose(object_distance(10.5, 11.7), 102.375) + True + >>> object_distance(90, 0) + Traceback (most recent call last): + ... + ValueError: Invalid inputs. Enter non zero values with respect + to the sign convention. + """ + + if distance_of_image == 0 or focal_length == 0: + raise ValueError( + "Invalid inputs. Enter non zero values with respect to the sign convention." + ) + object_distance = 1 / ((1 / focal_length) - (1 / distance_of_image)) + return object_distance + + +def image_distance(focal_length: float, distance_of_object: float) -> float: + """ + >>> from math import isclose + >>> isclose(image_distance(10, 40), 13.33333333) + True + >>> from math import isclose + >>> isclose(image_distance(1.5, 6.7), 1.932692308) + True + >>> image_distance(0, 0) + Traceback (most recent call last): + ... + ValueError: Invalid inputs. Enter non zero values with respect + to the sign convention. + """ + + if distance_of_object == 0 or focal_length == 0: + raise ValueError( + "Invalid inputs. Enter non zero values with respect to the sign convention." + ) + image_distance = 1 / ((1 / focal_length) - (1 / distance_of_object)) + return image_distance From 4b6301d4ce91638d39689f7be7db797f99623964 Mon Sep 17 00:00:00 2001 From: rtang09 <49603415+rtang09@users.noreply.github.com> Date: Wed, 4 Oct 2023 23:12:08 -0700 Subject: [PATCH 259/757] Fletcher 16 (#9775) * Add files via upload * Update fletcher16.py * Update fletcher16.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update fletcher16.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update fletcher16.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update fletcher16.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- hashes/fletcher16.py | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 hashes/fletcher16.py diff --git a/hashes/fletcher16.py b/hashes/fletcher16.py new file mode 100644 index 000000000000..7c23c98d72c5 --- /dev/null +++ b/hashes/fletcher16.py @@ -0,0 +1,36 @@ +""" +The Fletcher checksum is an algorithm for computing a position-dependent +checksum devised by John G. Fletcher (1934–2012) at Lawrence Livermore Labs +in the late 1970s.[1] The objective of the Fletcher checksum was to +provide error-detection properties approaching those of a cyclic +redundancy check but with the lower computational effort associated +with summation techniques. + +Source: https://en.wikipedia.org/wiki/Fletcher%27s_checksum +""" + + +def fletcher16(text: str) -> int: + """ + Loop through every character in the data and add to two sums. + + >>> fletcher16('hello world') + 6752 + >>> fletcher16('onethousandfourhundredthirtyfour') + 28347 + >>> fletcher16('The quick brown fox jumps over the lazy dog.') + 5655 + """ + data = bytes(text, "ascii") + sum1 = 0 + sum2 = 0 + for character in data: + sum1 = (sum1 + character) % 255 + sum2 = (sum1 + sum2) % 255 + return (sum2 << 8) | sum1 + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 0d324de7ab9c354d958fd93f6046d0111014d95a Mon Sep 17 00:00:00 2001 From: Vipin Karthic <143083087+vipinkarthic@users.noreply.github.com> Date: Thu, 5 Oct 2023 13:18:15 +0530 Subject: [PATCH 260/757] Doctest Error Correction of mirror_formulae.py (#9782) * Python mirror_formulae.py is added to the repository * Changes done after reading readme.md * Changes for running doctest on all platforms * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Change 2 for Doctests * Changes for doctest 2 * updating DIRECTORY.md * Doctest whitespace error rectification to mirror_formulae.py * updating DIRECTORY.md --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: Christian Clauss --- DIRECTORY.md | 1 + physics/mirror_formulae.py | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index 5f23cbd6c922..b0ba3c3852da 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -469,6 +469,7 @@ * [Djb2](hashes/djb2.py) * [Elf](hashes/elf.py) * [Enigma Machine](hashes/enigma_machine.py) + * [Fletcher16](hashes/fletcher16.py) * [Hamming Code](hashes/hamming_code.py) * [Luhn](hashes/luhn.py) * [Md5](hashes/md5.py) diff --git a/physics/mirror_formulae.py b/physics/mirror_formulae.py index f1b4ac2c7baf..7efc52438140 100644 --- a/physics/mirror_formulae.py +++ b/physics/mirror_formulae.py @@ -66,7 +66,7 @@ def focal_length(distance_of_object: float, distance_of_image: float) -> float: >>> from math import isclose >>> isclose(focal_length(9.5, 6.7), 3.929012346) True - >>> focal_length(0, 20) + >>> focal_length(0, 20) # doctest: +NORMALIZE_WHITESPACE Traceback (most recent call last): ... ValueError: Invalid inputs. Enter non zero values with respect @@ -89,7 +89,7 @@ def object_distance(focal_length: float, distance_of_image: float) -> float: >>> from math import isclose >>> isclose(object_distance(10.5, 11.7), 102.375) True - >>> object_distance(90, 0) + >>> object_distance(90, 0) # doctest: +NORMALIZE_WHITESPACE Traceback (most recent call last): ... ValueError: Invalid inputs. Enter non zero values with respect @@ -112,7 +112,7 @@ def image_distance(focal_length: float, distance_of_object: float) -> float: >>> from math import isclose >>> isclose(image_distance(1.5, 6.7), 1.932692308) True - >>> image_distance(0, 0) + >>> image_distance(0, 0) # doctest: +NORMALIZE_WHITESPACE Traceback (most recent call last): ... ValueError: Invalid inputs. Enter non zero values with respect From f3be0ae9e60a0ed2185e55c0758ddf401e604f8c Mon Sep 17 00:00:00 2001 From: Naman <37952726+namansharma18899@users.noreply.github.com> Date: Thu, 5 Oct 2023 14:07:23 +0530 Subject: [PATCH 261/757] Added largest pow of 2 le num (#9374) --- bit_manipulation/largest_pow_of_two_le_num.py | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 bit_manipulation/largest_pow_of_two_le_num.py diff --git a/bit_manipulation/largest_pow_of_two_le_num.py b/bit_manipulation/largest_pow_of_two_le_num.py new file mode 100644 index 000000000000..6ef827312199 --- /dev/null +++ b/bit_manipulation/largest_pow_of_two_le_num.py @@ -0,0 +1,60 @@ +""" +Author : Naman Sharma +Date : October 2, 2023 + +Task: +To Find the largest power of 2 less than or equal to a given number. + +Implementation notes: Use bit manipulation. +We start from 1 & left shift the set bit to check if (res<<1)<=number. +Each left bit shift represents a pow of 2. + +For example: +number: 15 +res: 1 0b1 + 2 0b10 + 4 0b100 + 8 0b1000 + 16 0b10000 (Exit) +""" + + +def largest_pow_of_two_le_num(number: int) -> int: + """ + Return the largest power of two less than or equal to a number. + + >>> largest_pow_of_two_le_num(0) + 0 + >>> largest_pow_of_two_le_num(1) + 1 + >>> largest_pow_of_two_le_num(-1) + 0 + >>> largest_pow_of_two_le_num(3) + 2 + >>> largest_pow_of_two_le_num(15) + 8 + >>> largest_pow_of_two_le_num(99) + 64 + >>> largest_pow_of_two_le_num(178) + 128 + >>> largest_pow_of_two_le_num(999999) + 524288 + >>> largest_pow_of_two_le_num(99.9) + Traceback (most recent call last): + ... + TypeError: Input value must be a 'int' type + """ + if isinstance(number, float): + raise TypeError("Input value must be a 'int' type") + if number <= 0: + return 0 + res = 1 + while (res << 1) <= number: + res <<= 1 + return res + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From e29024d14ade8ff4cdb43d1da6a7738f44685e5e Mon Sep 17 00:00:00 2001 From: Rohan Sardar <77870108+RohanSardar@users.noreply.github.com> Date: Thu, 5 Oct 2023 14:22:40 +0530 Subject: [PATCH 262/757] Program to convert a given string to Pig Latin (#9712) * Program to convert a given string to Pig Latin This is a program to convert a user given string to its respective Pig Latin form As per wikipedia (link: https://en.wikipedia.org/wiki/Pig_Latin#Rules) For words that begin with consonant sounds, all letters before the initial vowel are placed at the end of the word sequence. Then, "ay" is added, as in the following examples: "pig" = "igpay" "latin" = "atinlay" "banana" = "ananabay" When words begin with consonant clusters (multiple consonants that form one sound), the whole sound is added to the end when speaking or writing. "friends" = "iendsfray" "smile" = "ilesmay" "string" = "ingstray" For words that begin with vowel sounds, one just adds "hay", "way" or "yay" to the end. Examples are: "eat" = "eatway" "omelet" = "omeletway" "are" = "areway" * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update pig_latin.py Added f-string * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update pig_latin.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update pig_latin.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update pig_latin.py * Update pig_latin.py * Update pig_latin.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- strings/pig_latin.py | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 strings/pig_latin.py diff --git a/strings/pig_latin.py b/strings/pig_latin.py new file mode 100644 index 000000000000..457dbb5a6cf6 --- /dev/null +++ b/strings/pig_latin.py @@ -0,0 +1,44 @@ +def pig_latin(word: str) -> str: + """Compute the piglatin of a given string. + + https://en.wikipedia.org/wiki/Pig_Latin + + Usage examples: + >>> pig_latin("pig") + 'igpay' + >>> pig_latin("latin") + 'atinlay' + >>> pig_latin("banana") + 'ananabay' + >>> pig_latin("friends") + 'iendsfray' + >>> pig_latin("smile") + 'ilesmay' + >>> pig_latin("string") + 'ingstray' + >>> pig_latin("eat") + 'eatway' + >>> pig_latin("omelet") + 'omeletway' + >>> pig_latin("are") + 'areway' + >>> pig_latin(" ") + '' + >>> pig_latin(None) + '' + """ + if not (word or "").strip(): + return "" + word = word.lower() + if word[0] in "aeiou": + return f"{word}way" + for i, char in enumerate(word): # noqa: B007 + if char in "aeiou": + break + return f"{word[i:]}{word[:i]}ay" + + +if __name__ == "__main__": + print(f"{pig_latin('friends') = }") + word = input("Enter a word: ") + print(f"{pig_latin(word) = }") From dffbe458c07d492b9c599376233f9f6295527339 Mon Sep 17 00:00:00 2001 From: Chris O <46587501+ChrisO345@users.noreply.github.com> Date: Fri, 6 Oct 2023 00:26:33 +1300 Subject: [PATCH 263/757] Update contributing guidelines to say not to open new issues for algorithms (#9760) * updated CONTRIBUTING.md with markdown anchors and issues * removed testing header from previous PR --- CONTRIBUTING.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7a67ce33cd62..bf3420185c1a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -25,8 +25,12 @@ We appreciate any contribution, from fixing a grammar mistake in a comment to im Your contribution will be tested by our [automated testing on GitHub Actions](https://github.com/TheAlgorithms/Python/actions) to save time and mental energy. After you have submitted your pull request, you should see the GitHub Actions tests start to run at the bottom of your submission page. If those tests fail, then click on the ___details___ button try to read through the GitHub Actions output to understand the failure. If you do not understand, please leave a comment on your submission page and a community member will try to help. +#### Issues + If you are interested in resolving an [open issue](https://github.com/TheAlgorithms/Python/issues), simply make a pull request with your proposed fix. __We do not assign issues in this repo__ so please do not ask for permission to work on an issue. +__Do not__ create an issue to contribute an algorithm. Please submit a pull request instead. + Please help us keep our issue list small by adding `Fixes #{$ISSUE_NUMBER}` to the description of pull requests that resolve open issues. For example, if your pull request fixes issue #10, then please add the following to its description: ``` From 0e3ea3fbab0297f38ed48b9e2f694cc43f8af567 Mon Sep 17 00:00:00 2001 From: Kamil <32775019+quant12345@users.noreply.github.com> Date: Thu, 5 Oct 2023 16:30:39 +0500 Subject: [PATCH 264/757] Fermat_little_theorem type annotation (#9794) * Replacing the generator with numpy vector operations from lu_decomposition. * Revert "Replacing the generator with numpy vector operations from lu_decomposition." This reverts commit ad217c66165898d62b76cc89ba09c2d7049b6448. * Added type annotation. * Update fermat_little_theorem.py Used other syntax. * Update fermat_little_theorem.py * Update maths/fermat_little_theorem.py --------- Co-authored-by: Tianyi Zheng --- maths/fermat_little_theorem.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maths/fermat_little_theorem.py b/maths/fermat_little_theorem.py index eea03be245cb..4a3ecd05ce91 100644 --- a/maths/fermat_little_theorem.py +++ b/maths/fermat_little_theorem.py @@ -5,7 +5,7 @@ # Wikipedia reference: https://en.wikipedia.org/wiki/Fermat%27s_little_theorem -def binary_exponentiation(a, n, mod): +def binary_exponentiation(a: int, n: float, mod: int) -> int: if n == 0: return 1 From 1b6c5cc2713743b8a74fd9c92e0a1b6442d63a7f Mon Sep 17 00:00:00 2001 From: Kamil <32775019+quant12345@users.noreply.github.com> Date: Thu, 5 Oct 2023 17:30:43 +0500 Subject: [PATCH 265/757] Karatsuba type annotation (#9800) * Replacing the generator with numpy vector operations from lu_decomposition. * Revert "Replacing the generator with numpy vector operations from lu_decomposition." This reverts commit ad217c66165898d62b76cc89ba09c2d7049b6448. * Added type annotation. --- maths/karatsuba.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maths/karatsuba.py b/maths/karatsuba.py index 4bf4aecdc068..3d29e31d2107 100644 --- a/maths/karatsuba.py +++ b/maths/karatsuba.py @@ -1,7 +1,7 @@ """ Multiply two numbers using Karatsuba algorithm """ -def karatsuba(a, b): +def karatsuba(a: int, b: int) -> int: """ >>> karatsuba(15463, 23489) == 15463 * 23489 True From f159a3350650843e0b3e856e612cda56eabb4237 Mon Sep 17 00:00:00 2001 From: Abul Hasan <33129246+haxkd@users.noreply.github.com> Date: Thu, 5 Oct 2023 18:09:14 +0530 Subject: [PATCH 266/757] convert to the base minus 2 of a number (#9748) * Fix: Issue 9588 * Fix: Issue 9588 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix: Issue 9588 * Fix: Issue #9588 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix: Issue #9588 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix: Issue #9588 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix: issue #9793 * fix: issue #9793 * fix: issue #9588 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- maths/base_neg2_conversion.py | 37 +++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 maths/base_neg2_conversion.py diff --git a/maths/base_neg2_conversion.py b/maths/base_neg2_conversion.py new file mode 100644 index 000000000000..81d40d37e79d --- /dev/null +++ b/maths/base_neg2_conversion.py @@ -0,0 +1,37 @@ +def decimal_to_negative_base_2(num: int) -> int: + """ + This function returns the number negative base 2 + of the decimal number of the input data. + + Args: + int: The decimal number to convert. + + Returns: + int: The negative base 2 number. + + Examples: + >>> decimal_to_negative_base_2(0) + 0 + >>> decimal_to_negative_base_2(-19) + 111101 + >>> decimal_to_negative_base_2(4) + 100 + >>> decimal_to_negative_base_2(7) + 11011 + """ + if num == 0: + return 0 + ans = "" + while num != 0: + num, rem = divmod(num, -2) + if rem < 0: + rem += 2 + num += 1 + ans = str(rem) + ans + return int(ans) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 9bfc314e878e36a5f5d8974ec188ad7f0db8c5a1 Mon Sep 17 00:00:00 2001 From: Kamil <32775019+quant12345@users.noreply.github.com> Date: Thu, 5 Oct 2023 17:39:29 +0500 Subject: [PATCH 267/757] hardy_ramanujanalgo type annotation (#9799) * Replacing the generator with numpy vector operations from lu_decomposition. * Revert "Replacing the generator with numpy vector operations from lu_decomposition." This reverts commit ad217c66165898d62b76cc89ba09c2d7049b6448. * Added type annotation. --- maths/hardy_ramanujanalgo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maths/hardy_ramanujanalgo.py b/maths/hardy_ramanujanalgo.py index 6929533fc389..31ec76fbe10b 100644 --- a/maths/hardy_ramanujanalgo.py +++ b/maths/hardy_ramanujanalgo.py @@ -4,7 +4,7 @@ import math -def exact_prime_factor_count(n): +def exact_prime_factor_count(n: int) -> int: """ >>> exact_prime_factor_count(51242183) 3 From 6643c955376174c307c982b1d5cc39778c40bea1 Mon Sep 17 00:00:00 2001 From: Adebisi Ahmed Date: Thu, 5 Oct 2023 14:18:54 +0100 Subject: [PATCH 268/757] add gas station (#9446) * feat: add gas station * make code more readable make code more readable * update test * Update gas_station.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * tuple[GasStation, ...] * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: Christian Clauss Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- greedy_methods/gas_station.py | 97 +++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 greedy_methods/gas_station.py diff --git a/greedy_methods/gas_station.py b/greedy_methods/gas_station.py new file mode 100644 index 000000000000..2427375d2664 --- /dev/null +++ b/greedy_methods/gas_station.py @@ -0,0 +1,97 @@ +""" +Task: +There are n gas stations along a circular route, where the amount of gas +at the ith station is gas_quantities[i]. + +You have a car with an unlimited gas tank and it costs costs[i] of gas +to travel from the ith station to its next (i + 1)th station. +You begin the journey with an empty tank at one of the gas stations. + +Given two integer arrays gas_quantities and costs, return the starting +gas station's index if you can travel around the circuit once +in the clockwise direction otherwise, return -1. +If there exists a solution, it is guaranteed to be unique + +Reference: https://leetcode.com/problems/gas-station/description + +Implementation notes: +First, check whether the total gas is enough to complete the journey. If not, return -1. +However, if there is enough gas, it is guaranteed that there is a valid +starting index to reach the end of the journey. +Greedily calculate the net gain (gas_quantity - cost) at each station. +If the net gain ever goes below 0 while iterating through the stations, +start checking from the next station. + +""" +from dataclasses import dataclass + + +@dataclass +class GasStation: + gas_quantity: int + cost: int + + +def get_gas_stations( + gas_quantities: list[int], costs: list[int] +) -> tuple[GasStation, ...]: + """ + This function returns a tuple of gas stations. + + Args: + gas_quantities: Amount of gas available at each station + costs: The cost of gas required to move from one station to the next + + Returns: + A tuple of gas stations + + >>> gas_stations = get_gas_stations([1, 2, 3, 4, 5], [3, 4, 5, 1, 2]) + >>> len(gas_stations) + 5 + >>> gas_stations[0] + GasStation(gas_quantity=1, cost=3) + >>> gas_stations[-1] + GasStation(gas_quantity=5, cost=2) + """ + return tuple( + GasStation(quantity, cost) for quantity, cost in zip(gas_quantities, costs) + ) + + +def can_complete_journey(gas_stations: tuple[GasStation, ...]) -> int: + """ + This function returns the index from which to start the journey + in order to reach the end. + + Args: + gas_quantities [list]: Amount of gas available at each station + cost [list]: The cost of gas required to move from one station to the next + + Returns: + start [int]: start index needed to complete the journey + + Examples: + >>> can_complete_journey(get_gas_stations([1, 2, 3, 4, 5], [3, 4, 5, 1, 2])) + 3 + >>> can_complete_journey(get_gas_stations([2, 3, 4], [3, 4, 3])) + -1 + """ + total_gas = sum(gas_station.gas_quantity for gas_station in gas_stations) + total_cost = sum(gas_station.cost for gas_station in gas_stations) + if total_gas < total_cost: + return -1 + + start = 0 + net = 0 + for i, gas_station in enumerate(gas_stations): + net += gas_station.gas_quantity - gas_station.cost + if net < 0: + start = i + 1 + net = 0 + return start + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 55ee273419ae76ddeda250374921644615b88393 Mon Sep 17 00:00:00 2001 From: Wei Jiang <42140605+Jiang15@users.noreply.github.com> Date: Thu, 5 Oct 2023 16:00:48 +0200 Subject: [PATCH 269/757] [bug fixing] Edge case of the double ended queue (#9823) * fix the edge case of the double ended queue pop the last element * refactoring doc --------- Co-authored-by: Jiang15 --- data_structures/queue/double_ended_queue.py | 62 +++++++++++++++------ 1 file changed, 45 insertions(+), 17 deletions(-) diff --git a/data_structures/queue/double_ended_queue.py b/data_structures/queue/double_ended_queue.py index 44dc863b9a4e..17a23038d288 100644 --- a/data_structures/queue/double_ended_queue.py +++ b/data_structures/queue/double_ended_queue.py @@ -242,12 +242,20 @@ def pop(self) -> Any: Removes the last element of the deque and returns it. Time complexity: O(1) @returns topop.val: the value of the node to pop. - >>> our_deque = Deque([1, 2, 3, 15182]) - >>> our_popped = our_deque.pop() - >>> our_popped + >>> our_deque1 = Deque([1]) + >>> our_popped1 = our_deque1.pop() + >>> our_popped1 + 1 + >>> our_deque1 + [] + + >>> our_deque2 = Deque([1, 2, 3, 15182]) + >>> our_popped2 = our_deque2.pop() + >>> our_popped2 15182 - >>> our_deque + >>> our_deque2 [1, 2, 3] + >>> from collections import deque >>> deque_collections = deque([1, 2, 3, 15182]) >>> collections_popped = deque_collections.pop() @@ -255,18 +263,24 @@ def pop(self) -> Any: 15182 >>> deque_collections deque([1, 2, 3]) - >>> list(our_deque) == list(deque_collections) + >>> list(our_deque2) == list(deque_collections) True - >>> our_popped == collections_popped + >>> our_popped2 == collections_popped True """ # make sure the deque has elements to pop assert not self.is_empty(), "Deque is empty." topop = self._back - self._back = self._back.prev_node # set new back - # drop the last node - python will deallocate memory automatically - self._back.next_node = None + # if only one element in the queue: point the front and back to None + # else remove one element from back + if self._front == self._back: + self._front = None + self._back = None + else: + self._back = self._back.prev_node # set new back + # drop the last node, python will deallocate memory automatically + self._back.next_node = None self._len -= 1 @@ -277,11 +291,17 @@ def popleft(self) -> Any: Removes the first element of the deque and returns it. Time complexity: O(1) @returns topop.val: the value of the node to pop. - >>> our_deque = Deque([15182, 1, 2, 3]) - >>> our_popped = our_deque.popleft() - >>> our_popped + >>> our_deque1 = Deque([1]) + >>> our_popped1 = our_deque1.pop() + >>> our_popped1 + 1 + >>> our_deque1 + [] + >>> our_deque2 = Deque([15182, 1, 2, 3]) + >>> our_popped2 = our_deque2.popleft() + >>> our_popped2 15182 - >>> our_deque + >>> our_deque2 [1, 2, 3] >>> from collections import deque >>> deque_collections = deque([15182, 1, 2, 3]) @@ -290,17 +310,23 @@ def popleft(self) -> Any: 15182 >>> deque_collections deque([1, 2, 3]) - >>> list(our_deque) == list(deque_collections) + >>> list(our_deque2) == list(deque_collections) True - >>> our_popped == collections_popped + >>> our_popped2 == collections_popped True """ # make sure the deque has elements to pop assert not self.is_empty(), "Deque is empty." topop = self._front - self._front = self._front.next_node # set new front and drop the first node - self._front.prev_node = None + # if only one element in the queue: point the front and back to None + # else remove one element from front + if self._front == self._back: + self._front = None + self._back = None + else: + self._front = self._front.next_node # set new front and drop the first node + self._front.prev_node = None self._len -= 1 @@ -432,3 +458,5 @@ def __repr__(self) -> str: import doctest doctest.testmod() + dq = Deque([3]) + dq.pop() From deb0480b3a07e50b93f88d4351d1fce000574d05 Mon Sep 17 00:00:00 2001 From: Aasheesh <126905285+AasheeshLikePanner@users.noreply.github.com> Date: Thu, 5 Oct 2023 19:37:44 +0530 Subject: [PATCH 270/757] Changing the directory of sigmoid_linear_unit.py (#9824) * Changing the directory of sigmoid_linear_unit.py * Delete neural_network/activation_functions/__init__.py --------- Co-authored-by: Tianyi Zheng --- .../activation_functions}/sigmoid_linear_unit.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {maths => neural_network/activation_functions}/sigmoid_linear_unit.py (100%) diff --git a/maths/sigmoid_linear_unit.py b/neural_network/activation_functions/sigmoid_linear_unit.py similarity index 100% rename from maths/sigmoid_linear_unit.py rename to neural_network/activation_functions/sigmoid_linear_unit.py From 87494f1fa1022368d154477bdc035fd01f9e4382 Mon Sep 17 00:00:00 2001 From: Parth <100679824+pa-kh039@users.noreply.github.com> Date: Thu, 5 Oct 2023 21:51:28 +0530 Subject: [PATCH 271/757] largest divisible subset (#9825) * largest divisible subset * minor tweaks * adding more test cases Co-authored-by: Christian Clauss * improving code for better readability Co-authored-by: Christian Clauss * update Co-authored-by: Christian Clauss * update Co-authored-by: Christian Clauss * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * suggested changes done, and further modfications * final update * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update largest_divisible_subset.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update largest_divisible_subset.py --------- Co-authored-by: Christian Clauss Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .../largest_divisible_subset.py | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 dynamic_programming/largest_divisible_subset.py diff --git a/dynamic_programming/largest_divisible_subset.py b/dynamic_programming/largest_divisible_subset.py new file mode 100644 index 000000000000..db38636e29db --- /dev/null +++ b/dynamic_programming/largest_divisible_subset.py @@ -0,0 +1,74 @@ +from __future__ import annotations + + +def largest_divisible_subset(items: list[int]) -> list[int]: + """ + Algorithm to find the biggest subset in the given array such that for any 2 elements + x and y in the subset, either x divides y or y divides x. + >>> largest_divisible_subset([1, 16, 7, 8, 4]) + [16, 8, 4, 1] + >>> largest_divisible_subset([1, 2, 3]) + [2, 1] + >>> largest_divisible_subset([-1, -2, -3]) + [-3] + >>> largest_divisible_subset([1, 2, 4, 8]) + [8, 4, 2, 1] + >>> largest_divisible_subset((1, 2, 4, 8)) + [8, 4, 2, 1] + >>> largest_divisible_subset([1, 1, 1]) + [1, 1, 1] + >>> largest_divisible_subset([0, 0, 0]) + [0, 0, 0] + >>> largest_divisible_subset([-1, -1, -1]) + [-1, -1, -1] + >>> largest_divisible_subset([]) + [] + """ + # Sort the array in ascending order as the sequence does not matter we only have to + # pick up a subset. + items = sorted(items) + + number_of_items = len(items) + + # Initialize memo with 1s and hash with increasing numbers + memo = [1] * number_of_items + hash_array = list(range(number_of_items)) + + # Iterate through the array + for i, item in enumerate(items): + for prev_index in range(i): + if ((items[prev_index] != 0 and item % items[prev_index]) == 0) and ( + (1 + memo[prev_index]) > memo[i] + ): + memo[i] = 1 + memo[prev_index] + hash_array[i] = prev_index + + ans = -1 + last_index = -1 + + # Find the maximum length and its corresponding index + for i, memo_item in enumerate(memo): + if memo_item > ans: + ans = memo_item + last_index = i + + # Reconstruct the divisible subset + if last_index == -1: + return [] + result = [items[last_index]] + while hash_array[last_index] != last_index: + last_index = hash_array[last_index] + result.append(items[last_index]) + + return result + + +if __name__ == "__main__": + from doctest import testmod + + testmod() + + items = [1, 16, 7, 8, 4] + print( + f"The longest divisible subset of {items} is {largest_divisible_subset(items)}." + ) From b76115e8d184fbad1d6c400fcdd964e821f09e9b Mon Sep 17 00:00:00 2001 From: Pronay Debnath Date: Thu, 5 Oct 2023 23:03:05 +0530 Subject: [PATCH 272/757] Updated check_bipartite_graph_dfs.py (#9525) * Create dijkstra_algorithm.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update dijkstra_algorithm.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update dijkstra_algorithm.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update dijkstra_algorithm.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Delete greedy_methods/dijkstra_algorithm.py * Update check_bipartite_graph_dfs.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update check_bipartite_graph_dfs.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update graphs/check_bipartite_graph_dfs.py Co-authored-by: Christian Clauss * Update graphs/check_bipartite_graph_dfs.py Co-authored-by: Christian Clauss * Update check_bipartite_graph_dfs.py * Update check_bipartite_graph_dfs.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update check_bipartite_graph_dfs.py * Update check_bipartite_graph_dfs.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update check_bipartite_graph_dfs.py * Update check_bipartite_graph_dfs.py * Update check_bipartite_graph_dfs.py * Let's use self-documenting variable names This is complex code so let's use self-documenting function and variable names to help readers to understand. We should not shorten names to simplify the code formatting but use understandable name and leave to code formatting to psf/black. I am not sure if `nbor` was supposed to be `neighbour`. ;-) * Update check_bipartite_graph_dfs.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- graphs/check_bipartite_graph_dfs.py | 73 +++++++++++++++++++---------- 1 file changed, 47 insertions(+), 26 deletions(-) diff --git a/graphs/check_bipartite_graph_dfs.py b/graphs/check_bipartite_graph_dfs.py index fd644230449c..b13a9eb95afb 100644 --- a/graphs/check_bipartite_graph_dfs.py +++ b/graphs/check_bipartite_graph_dfs.py @@ -1,34 +1,55 @@ -# Check whether Graph is Bipartite or Not using DFS +from collections import defaultdict -# A Bipartite Graph is a graph whose vertices can be divided into two independent sets, -# U and V such that every edge (u, v) either connects a vertex from U to V or a vertex -# from V to U. In other words, for every edge (u, v), either u belongs to U and v to V, -# or u belongs to V and v to U. We can also say that there is no edge that connects -# vertices of same set. -def check_bipartite_dfs(graph): - visited = [False] * len(graph) - color = [-1] * len(graph) +def is_bipartite(graph: defaultdict[int, list[int]]) -> bool: + """ + Check whether a graph is Bipartite or not using Depth-First Search (DFS). - def dfs(v, c): - visited[v] = True - color[v] = c - for u in graph[v]: - if not visited[u]: - dfs(u, 1 - c) + A Bipartite Graph is a graph whose vertices can be divided into two independent + sets, U and V such that every edge (u, v) either connects a vertex from + U to V or a vertex from V to U. In other words, for every edge (u, v), + either u belongs to U and v to V, or u belongs to V and v to U. There is + no edge that connects vertices of the same set. - for i in range(len(graph)): - if not visited[i]: - dfs(i, 0) + Args: + graph: An adjacency list representing the graph. - for i in range(len(graph)): - for j in graph[i]: - if color[i] == color[j]: - return False + Returns: + True if there's no edge that connects vertices of the same set, False otherwise. - return True + Examples: + >>> is_bipartite( + ... defaultdict(list, {0: [1, 2], 1: [0, 3], 2: [0, 4], 3: [1], 4: [2]}) + ... ) + False + >>> is_bipartite(defaultdict(list, {0: [1, 2], 1: [0, 2], 2: [0, 1]})) + True + """ + def depth_first_search(node: int, color: int) -> bool: + visited[node] = color + return any( + visited[neighbour] == color + or ( + visited[neighbour] == -1 + and not depth_first_search(neighbour, 1 - color) + ) + for neighbour in graph[node] + ) -# Adjacency list of graph -graph = {0: [1, 3], 1: [0, 2], 2: [1, 3], 3: [0, 2], 4: []} -print(check_bipartite_dfs(graph)) + visited: defaultdict[int, int] = defaultdict(lambda: -1) + + return all( + not (visited[node] == -1 and not depth_first_search(node, 0)) for node in graph + ) + + +if __name__ == "__main__": + import doctest + + result = doctest.testmod() + + if result.failed: + print(f"{result.failed} test(s) failed.") + else: + print("All tests passed!") From cffdf99c55dcda89a5ce0fb2bf3cb685d168d136 Mon Sep 17 00:00:00 2001 From: Muhammad Umer Farooq <115654418+Muhammadummerr@users.noreply.github.com> Date: Thu, 5 Oct 2023 23:44:55 +0500 Subject: [PATCH 273/757] Updated prime_numbers.py testcases. (#9851) * Updated prime_numbers.py testcases. * revert __main__ code. --- maths/prime_numbers.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/maths/prime_numbers.py b/maths/prime_numbers.py index c5297ed9264c..38cc6670385d 100644 --- a/maths/prime_numbers.py +++ b/maths/prime_numbers.py @@ -17,8 +17,8 @@ def slow_primes(max_n: int) -> Generator[int, None, None]: [2, 3, 5, 7, 11] >>> list(slow_primes(33)) [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31] - >>> list(slow_primes(10000))[-1] - 9973 + >>> list(slow_primes(1000))[-1] + 997 """ numbers: Generator = (i for i in range(1, (max_n + 1))) for i in (n for n in numbers if n > 1): @@ -44,8 +44,8 @@ def primes(max_n: int) -> Generator[int, None, None]: [2, 3, 5, 7, 11] >>> list(primes(33)) [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31] - >>> list(primes(10000))[-1] - 9973 + >>> list(primes(1000))[-1] + 997 """ numbers: Generator = (i for i in range(1, (max_n + 1))) for i in (n for n in numbers if n > 1): @@ -73,8 +73,8 @@ def fast_primes(max_n: int) -> Generator[int, None, None]: [2, 3, 5, 7, 11] >>> list(fast_primes(33)) [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31] - >>> list(fast_primes(10000))[-1] - 9973 + >>> list(fast_primes(1000))[-1] + 997 """ numbers: Generator = (i for i in range(1, (max_n + 1), 2)) # It's useless to test even numbers as they will not be prime From 5869fda74245b55a3bda4ccc5ac62a84ab40766f Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Thu, 5 Oct 2023 23:55:13 +0200 Subject: [PATCH 274/757] print reverse: A LinkedList with a tail pointer (#9875) * print reverse: A LinkedList with a tail pointer * updating DIRECTORY.md --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 7 +- data_structures/linked_list/print_reverse.py | 134 +++++++++++++------ 2 files changed, 101 insertions(+), 40 deletions(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index b0ba3c3852da..c199a4329202 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -50,6 +50,7 @@ * [Index Of Rightmost Set Bit](bit_manipulation/index_of_rightmost_set_bit.py) * [Is Even](bit_manipulation/is_even.py) * [Is Power Of Two](bit_manipulation/is_power_of_two.py) + * [Largest Pow Of Two Le Num](bit_manipulation/largest_pow_of_two_le_num.py) * [Missing Number](bit_manipulation/missing_number.py) * [Numbers Different Signs](bit_manipulation/numbers_different_signs.py) * [Reverse Bits](bit_manipulation/reverse_bits.py) @@ -322,6 +323,7 @@ * [Integer Partition](dynamic_programming/integer_partition.py) * [Iterating Through Submasks](dynamic_programming/iterating_through_submasks.py) * [Knapsack](dynamic_programming/knapsack.py) + * [Largest Divisible Subset](dynamic_programming/largest_divisible_subset.py) * [Longest Common Subsequence](dynamic_programming/longest_common_subsequence.py) * [Longest Common Substring](dynamic_programming/longest_common_substring.py) * [Longest Increasing Subsequence](dynamic_programming/longest_increasing_subsequence.py) @@ -460,6 +462,7 @@ ## Greedy Methods * [Fractional Knapsack](greedy_methods/fractional_knapsack.py) * [Fractional Knapsack 2](greedy_methods/fractional_knapsack_2.py) + * [Gas Station](greedy_methods/gas_station.py) * [Minimum Waiting Time](greedy_methods/minimum_waiting_time.py) * [Optimal Merge Pattern](greedy_methods/optimal_merge_pattern.py) @@ -542,6 +545,7 @@ * [Average Median](maths/average_median.py) * [Average Mode](maths/average_mode.py) * [Bailey Borwein Plouffe](maths/bailey_borwein_plouffe.py) + * [Base Neg2 Conversion](maths/base_neg2_conversion.py) * [Basic Maths](maths/basic_maths.py) * [Bell Numbers](maths/bell_numbers.py) * [Binary Exp Mod](maths/binary_exp_mod.py) @@ -657,7 +661,6 @@ * [P Series](maths/series/p_series.py) * [Sieve Of Eratosthenes](maths/sieve_of_eratosthenes.py) * [Sigmoid](maths/sigmoid.py) - * [Sigmoid Linear Unit](maths/sigmoid_linear_unit.py) * [Signum](maths/signum.py) * [Simpson Rule](maths/simpson_rule.py) * [Simultaneous Linear Equation Solver](maths/simultaneous_linear_equation_solver.py) @@ -716,6 +719,7 @@ * [Leaky Rectified Linear Unit](neural_network/activation_functions/leaky_rectified_linear_unit.py) * [Rectified Linear Unit](neural_network/activation_functions/rectified_linear_unit.py) * [Scaled Exponential Linear Unit](neural_network/activation_functions/scaled_exponential_linear_unit.py) + * [Sigmoid Linear Unit](neural_network/activation_functions/sigmoid_linear_unit.py) * [Back Propagation Neural Network](neural_network/back_propagation_neural_network.py) * [Convolution Neural Network](neural_network/convolution_neural_network.py) * [Perceptron](neural_network/perceptron.py) @@ -1180,6 +1184,7 @@ * [Naive String Search](strings/naive_string_search.py) * [Ngram](strings/ngram.py) * [Palindrome](strings/palindrome.py) + * [Pig Latin](strings/pig_latin.py) * [Prefix Function](strings/prefix_function.py) * [Rabin Karp](strings/rabin_karp.py) * [Remove Duplicate](strings/remove_duplicate.py) diff --git a/data_structures/linked_list/print_reverse.py b/data_structures/linked_list/print_reverse.py index f83d5607ffdd..a023745dee69 100644 --- a/data_structures/linked_list/print_reverse.py +++ b/data_structures/linked_list/print_reverse.py @@ -1,22 +1,91 @@ from __future__ import annotations +from collections.abc import Iterable, Iterator +from dataclasses import dataclass + +@dataclass class Node: - def __init__(self, data=None): - self.data = data - self.next = None + data: int + next_node: Node | None = None + + +class LinkedList: + """A class to represent a Linked List. + Use a tail pointer to speed up the append() operation. + """ + + def __init__(self) -> None: + """Initialize a LinkedList with the head node set to None. + >>> linked_list = LinkedList() + >>> (linked_list.head, linked_list.tail) + (None, None) + """ + self.head: Node | None = None + self.tail: Node | None = None # Speeds up the append() operation + + def __iter__(self) -> Iterator[int]: + """Iterate the LinkedList yielding each Node's data. + >>> linked_list = LinkedList() + >>> items = (1, 2, 3, 4, 5) + >>> linked_list.extend(items) + >>> tuple(linked_list) == items + True + """ + node = self.head + while node: + yield node.data + node = node.next_node + + def __repr__(self) -> str: + """Returns a string representation of the LinkedList. + >>> linked_list = LinkedList() + >>> str(linked_list) + '' + >>> linked_list.append(1) + >>> str(linked_list) + '1' + >>> linked_list.extend([2, 3, 4, 5]) + >>> str(linked_list) + '1 -> 2 -> 3 -> 4 -> 5' + """ + return " -> ".join([str(data) for data in self]) - def __repr__(self): - """Returns a visual representation of the node and all its following nodes.""" - string_rep = [] - temp = self - while temp: - string_rep.append(f"{temp.data}") - temp = temp.next - return "->".join(string_rep) + def append(self, data: int) -> None: + """Appends a new node with the given data to the end of the LinkedList. + >>> linked_list = LinkedList() + >>> str(linked_list) + '' + >>> linked_list.append(1) + >>> str(linked_list) + '1' + >>> linked_list.append(2) + >>> str(linked_list) + '1 -> 2' + """ + if self.tail: + self.tail.next_node = self.tail = Node(data) + else: + self.head = self.tail = Node(data) + def extend(self, items: Iterable[int]) -> None: + """Appends each item to the end of the LinkedList. + >>> linked_list = LinkedList() + >>> linked_list.extend([]) + >>> str(linked_list) + '' + >>> linked_list.extend([1, 2]) + >>> str(linked_list) + '1 -> 2' + >>> linked_list.extend([3,4]) + >>> str(linked_list) + '1 -> 2 -> 3 -> 4' + """ + for item in items: + self.append(item) -def make_linked_list(elements_list: list): + +def make_linked_list(elements_list: Iterable[int]) -> LinkedList: """Creates a Linked List from the elements of the given sequence (list/tuple) and returns the head of the Linked List. >>> make_linked_list([]) @@ -28,43 +97,30 @@ def make_linked_list(elements_list: list): >>> make_linked_list(['abc']) abc >>> make_linked_list([7, 25]) - 7->25 + 7 -> 25 """ if not elements_list: raise Exception("The Elements List is empty") - current = head = Node(elements_list[0]) - for i in range(1, len(elements_list)): - current.next = Node(elements_list[i]) - current = current.next - return head + linked_list = LinkedList() + linked_list.extend(elements_list) + return linked_list -def print_reverse(head_node: Node) -> None: +def in_reverse(linked_list: LinkedList) -> str: """Prints the elements of the given Linked List in reverse order - >>> print_reverse([]) - >>> linked_list = make_linked_list([69, 88, 73]) - >>> print_reverse(linked_list) - 73 - 88 - 69 + >>> in_reverse(LinkedList()) + '' + >>> in_reverse(make_linked_list([69, 88, 73])) + '73 <- 88 <- 69' """ - if head_node is not None and isinstance(head_node, Node): - print_reverse(head_node.next) - print(head_node.data) + return " <- ".join(str(line) for line in reversed(tuple(linked_list))) -def main(): +if __name__ == "__main__": from doctest import testmod testmod() - - linked_list = make_linked_list([14, 52, 14, 12, 43]) - print("Linked List:") - print(linked_list) - print("Elements in Reverse:") - print_reverse(linked_list) - - -if __name__ == "__main__": - main() + linked_list = make_linked_list((14, 52, 14, 12, 43)) + print(f"Linked List: {linked_list}") + print(f"Reverse List: {in_reverse(linked_list)}") From 7f94a73eec45edfd215e8f07148c9c657b4e4b89 Mon Sep 17 00:00:00 2001 From: Marek Mazij <112333347+Mrk-Mzj@users.noreply.github.com> Date: Fri, 6 Oct 2023 00:05:23 +0200 Subject: [PATCH 275/757] camelCase to snake_case conversion - Fixes #9726 (#9727) * First commit camel case to snake case conversion algorithm, including numbers * code modified to not use regex --- strings/camel_case_to_snake_case.py | 60 +++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 strings/camel_case_to_snake_case.py diff --git a/strings/camel_case_to_snake_case.py b/strings/camel_case_to_snake_case.py new file mode 100644 index 000000000000..582907be2edb --- /dev/null +++ b/strings/camel_case_to_snake_case.py @@ -0,0 +1,60 @@ +def camel_to_snake_case(input_str: str) -> str: + """ + Transforms a camelCase (or PascalCase) string to snake_case + + >>> camel_to_snake_case("someRandomString") + 'some_random_string' + + >>> camel_to_snake_case("SomeRandomStr#ng") + 'some_random_str_ng' + + >>> camel_to_snake_case("123someRandom123String123") + '123_some_random_123_string_123' + + >>> camel_to_snake_case("123SomeRandom123String123") + '123_some_random_123_string_123' + + >>> camel_to_snake_case(123) + Traceback (most recent call last): + ... + ValueError: Expected string as input, found + + """ + + # check for invalid input type + if not isinstance(input_str, str): + msg = f"Expected string as input, found {type(input_str)}" + raise ValueError(msg) + + snake_str = "" + + for index, char in enumerate(input_str): + if char.isupper(): + snake_str += "_" + char.lower() + + # if char is lowercase but proceeded by a digit: + elif input_str[index - 1].isdigit() and char.islower(): + snake_str += "_" + char + + # if char is a digit proceeded by a letter: + elif input_str[index - 1].isalpha() and char.isnumeric(): + snake_str += "_" + char.lower() + + # if char is not alphanumeric: + elif not char.isalnum(): + snake_str += "_" + + else: + snake_str += char + + # remove leading underscore + if snake_str[0] == "_": + snake_str = snake_str[1:] + + return snake_str + + +if __name__ == "__main__": + from doctest import testmod + + testmod() From 13317e4f7f260f59e6e53595f802c9d12ec0db4a Mon Sep 17 00:00:00 2001 From: Akshay B Shetty <107768228+NinjaSoulPirate@users.noreply.github.com> Date: Fri, 6 Oct 2023 03:57:13 +0530 Subject: [PATCH 276/757] feat: :sparkles: calculating the resitance of resistor using color codes (#9874) --- electronics/resistor_color_code.py | 373 +++++++++++++++++++++++++++++ 1 file changed, 373 insertions(+) create mode 100644 electronics/resistor_color_code.py diff --git a/electronics/resistor_color_code.py b/electronics/resistor_color_code.py new file mode 100644 index 000000000000..b0534b813def --- /dev/null +++ b/electronics/resistor_color_code.py @@ -0,0 +1,373 @@ +""" +Title : Calculating the resistance of a n band resistor using the color codes + +Description : + Resistors resist the flow of electrical current.Each one has a value that tells how + strongly it resists current flow.This value's unit is the ohm, often noted with the + Greek letter omega: Ω. + + The colored bands on a resistor can tell you everything you need to know about its + value and tolerance, as long as you understand how to read them. The order in which + the colors are arranged is very important, and each value of resistor has its own + unique combination. + + The color coding for resistors is an international standard that is defined in IEC + 60062. + + The number of bands present in a resistor varies from three to six. These represent + significant figures, multiplier, tolerance, reliability, and temperature coefficient + Each color used for a type of band has a value assigned to it. It is read from left + to right. + All resistors will have significant figures and multiplier bands. In a three band + resistor first two bands from the left represent significant figures and the third + represents the multiplier band. + + Significant figures - The number of significant figures band in a resistor can vary + from two to three. + Colors and values associated with significant figure bands - + (Black = 0, Brown = 1, Red = 2, Orange = 3, Yellow = 4, Green = 5, Blue = 6, + Violet = 7, Grey = 8, White = 9) + + Multiplier - There will be one multiplier band in a resistor. It is multiplied with + the significant figures obtained from previous bands. + Colors and values associated with multiplier band - + (Black = 100, Brown = 10^1, Red = 10^2, Orange = 10^3, Yellow = 10^4, Green = 10^5, + Blue = 10^6, Violet = 10^7, Grey = 10^8, White = 10^9, Gold = 10^-1, Silver = 10^-2) + Note that multiplier bands use Gold and Silver which are not used for significant + figure bands. + + Tolerance - The tolerance band is not always present. It can be seen in four band + resistors and above. This is a percentage by which the resistor value can vary. + Colors and values associated with tolerance band - + (Brown = 1%, Red = 2%, Orange = 0.05%, Yellow = 0.02%, Green = 0.5%,Blue = 0.25%, + Violet = 0.1%, Grey = 0.01%, Gold = 5%, Silver = 10%) + If no color is mentioned then by default tolerance is 20% + Note that tolerance band does not use Black and White colors. + + Temperature Coeffecient - Indicates the change in resistance of the component as + a function of ambient temperature in terms of ppm/K. + It is present in six band resistors. + Colors and values associated with Temperature coeffecient - + (Black = 250 ppm/K, Brown = 100 ppm/K, Red = 50 ppm/K, Orange = 15 ppm/K, + Yellow = 25 ppm/K, Green = 20 ppm/K, Blue = 10 ppm/K, Violet = 5 ppm/K, + Grey = 1 ppm/K) + Note that temperature coeffecient band does not use White, Gold, Silver colors. + +Sources : + https://www.calculator.net/resistor-calculator.html + https://learn.parallax.com/support/reference/resistor-color-codes + https://byjus.com/physics/resistor-colour-codes/ +""" +valid_colors: list = [ + "Black", + "Brown", + "Red", + "Orange", + "Yellow", + "Green", + "Blue", + "Violet", + "Grey", + "White", + "Gold", + "Silver", +] + +significant_figures_color_values: dict[str, int] = { + "Black": 0, + "Brown": 1, + "Red": 2, + "Orange": 3, + "Yellow": 4, + "Green": 5, + "Blue": 6, + "Violet": 7, + "Grey": 8, + "White": 9, +} + +multiplier_color_values: dict[str, float] = { + "Black": 10**0, + "Brown": 10**1, + "Red": 10**2, + "Orange": 10**3, + "Yellow": 10**4, + "Green": 10**5, + "Blue": 10**6, + "Violet": 10**7, + "Grey": 10**8, + "White": 10**9, + "Gold": 10**-1, + "Silver": 10**-2, +} + +tolerance_color_values: dict[str, float] = { + "Brown": 1, + "Red": 2, + "Orange": 0.05, + "Yellow": 0.02, + "Green": 0.5, + "Blue": 0.25, + "Violet": 0.1, + "Grey": 0.01, + "Gold": 5, + "Silver": 10, +} + +temperature_coeffecient_color_values: dict[str, int] = { + "Black": 250, + "Brown": 100, + "Red": 50, + "Orange": 15, + "Yellow": 25, + "Green": 20, + "Blue": 10, + "Violet": 5, + "Grey": 1, +} + +band_types: dict[int, dict[str, int]] = { + 3: {"significant": 2, "multiplier": 1}, + 4: {"significant": 2, "multiplier": 1, "tolerance": 1}, + 5: {"significant": 3, "multiplier": 1, "tolerance": 1}, + 6: {"significant": 3, "multiplier": 1, "tolerance": 1, "temp_coeffecient": 1}, +} + + +def get_significant_digits(colors: list) -> str: + """ + Function returns the digit associated with the color. Function takes a + list containing colors as input and returns digits as string + + >>> get_significant_digits(['Black','Blue']) + '06' + + >>> get_significant_digits(['Aqua','Blue']) + Traceback (most recent call last): + ... + ValueError: Aqua is not a valid color for significant figure bands + + """ + digit = "" + for color in colors: + if color not in significant_figures_color_values: + msg = f"{color} is not a valid color for significant figure bands" + raise ValueError(msg) + digit = digit + str(significant_figures_color_values[color]) + return str(digit) + + +def get_multiplier(color: str) -> float: + """ + Function returns the multiplier value associated with the color. + Function takes color as input and returns multiplier value + + >>> get_multiplier('Gold') + 0.1 + + >>> get_multiplier('Ivory') + Traceback (most recent call last): + ... + ValueError: Ivory is not a valid color for multiplier band + + """ + if color not in multiplier_color_values: + msg = f"{color} is not a valid color for multiplier band" + raise ValueError(msg) + return multiplier_color_values[color] + + +def get_tolerance(color: str) -> float: + """ + Function returns the tolerance value associated with the color. + Function takes color as input and returns tolerance value. + + >>> get_tolerance('Green') + 0.5 + + >>> get_tolerance('Indigo') + Traceback (most recent call last): + ... + ValueError: Indigo is not a valid color for tolerance band + + """ + if color not in tolerance_color_values: + msg = f"{color} is not a valid color for tolerance band" + raise ValueError(msg) + return tolerance_color_values[color] + + +def get_temperature_coeffecient(color: str) -> int: + """ + Function returns the temperature coeffecient value associated with the color. + Function takes color as input and returns temperature coeffecient value. + + >>> get_temperature_coeffecient('Yellow') + 25 + + >>> get_temperature_coeffecient('Cyan') + Traceback (most recent call last): + ... + ValueError: Cyan is not a valid color for temperature coeffecient band + + """ + if color not in temperature_coeffecient_color_values: + msg = f"{color} is not a valid color for temperature coeffecient band" + raise ValueError(msg) + return temperature_coeffecient_color_values[color] + + +def get_band_type_count(total_number_of_bands: int, type_of_band: str) -> int: + """ + Function returns the number of bands of a given type in a resistor with n bands + Function takes total_number_of_bands and type_of_band as input and returns + number of bands belonging to that type in the given resistor + + >>> get_band_type_count(3,'significant') + 2 + + >>> get_band_type_count(2,'significant') + Traceback (most recent call last): + ... + ValueError: 2 is not a valid number of bands + + >>> get_band_type_count(3,'sign') + Traceback (most recent call last): + ... + ValueError: sign is not valid for a 3 band resistor + + >>> get_band_type_count(3,'tolerance') + Traceback (most recent call last): + ... + ValueError: tolerance is not valid for a 3 band resistor + + >>> get_band_type_count(5,'temp_coeffecient') + Traceback (most recent call last): + ... + ValueError: temp_coeffecient is not valid for a 5 band resistor + + """ + if total_number_of_bands not in band_types: + msg = f"{total_number_of_bands} is not a valid number of bands" + raise ValueError(msg) + if type_of_band not in band_types[total_number_of_bands]: + msg = f"{type_of_band} is not valid for a {total_number_of_bands} band resistor" + raise ValueError(msg) + return band_types[total_number_of_bands][type_of_band] + + +def check_validity(number_of_bands: int, colors: list) -> bool: + """ + Function checks if the input provided is valid or not. + Function takes number_of_bands and colors as input and returns + True if it is valid + + >>> check_validity(3, ["Black","Blue","Orange"]) + True + + >>> check_validity(4, ["Black","Blue","Orange"]) + Traceback (most recent call last): + ... + ValueError: Expecting 4 colors, provided 3 colors + + >>> check_validity(3, ["Cyan","Red","Yellow"]) + Traceback (most recent call last): + ... + ValueError: Cyan is not a valid color + + """ + if number_of_bands >= 3 and number_of_bands <= 6: + if number_of_bands == len(colors): + for color in colors: + if color not in valid_colors: + msg = f"{color} is not a valid color" + raise ValueError(msg) + return True + else: + msg = f"Expecting {number_of_bands} colors, provided {len(colors)} colors" + raise ValueError(msg) + else: + msg = "Invalid number of bands. Resistor bands must be 3 to 6" + raise ValueError(msg) + + +def calculate_resistance(number_of_bands: int, color_code_list: list) -> dict: + """ + Function calculates the total resistance of the resistor using the color codes. + Function takes number_of_bands, color_code_list as input and returns + resistance + + >>> calculate_resistance(3, ["Black","Blue","Orange"]) + {'resistance': '6000Ω ±20% '} + + >>> calculate_resistance(4, ["Orange","Green","Blue","Gold"]) + {'resistance': '35000000Ω ±5% '} + + >>> calculate_resistance(5, ["Violet","Brown","Grey","Silver","Green"]) + {'resistance': '7.18Ω ±0.5% '} + + >>> calculate_resistance(6, ["Red","Green","Blue","Yellow","Orange","Grey"]) + {'resistance': '2560000Ω ±0.05% 1 ppm/K'} + + >>> calculate_resistance(0, ["Violet","Brown","Grey","Silver","Green"]) + Traceback (most recent call last): + ... + ValueError: Invalid number of bands. Resistor bands must be 3 to 6 + + >>> calculate_resistance(4, ["Violet","Brown","Grey","Silver","Green"]) + Traceback (most recent call last): + ... + ValueError: Expecting 4 colors, provided 5 colors + + >>> calculate_resistance(4, ["Violet","Silver","Brown","Grey"]) + Traceback (most recent call last): + ... + ValueError: Silver is not a valid color for significant figure bands + + >>> calculate_resistance(4, ["Violet","Blue","Lime","Grey"]) + Traceback (most recent call last): + ... + ValueError: Lime is not a valid color + + """ + is_valid = check_validity(number_of_bands, color_code_list) + if is_valid: + number_of_significant_bands = get_band_type_count( + number_of_bands, "significant" + ) + significant_colors = color_code_list[:number_of_significant_bands] + significant_digits = int(get_significant_digits(significant_colors)) + multiplier_color = color_code_list[number_of_significant_bands] + multiplier = get_multiplier(multiplier_color) + if number_of_bands == 3: + tolerance_color = None + else: + tolerance_color = color_code_list[number_of_significant_bands + 1] + tolerance = ( + 20 if tolerance_color is None else get_tolerance(str(tolerance_color)) + ) + if number_of_bands != 6: + temperature_coeffecient_color = None + else: + temperature_coeffecient_color = color_code_list[ + number_of_significant_bands + 2 + ] + temperature_coeffecient = ( + 0 + if temperature_coeffecient_color is None + else get_temperature_coeffecient(str(temperature_coeffecient_color)) + ) + resisitance = significant_digits * multiplier + if temperature_coeffecient == 0: + answer = f"{resisitance}Ω ±{tolerance}% " + else: + answer = f"{resisitance}Ω ±{tolerance}% {temperature_coeffecient} ppm/K" + return {"resistance": answer} + else: + raise ValueError("Input is invalid") + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From b316a9612826905b963a465f0f02febaed761ccc Mon Sep 17 00:00:00 2001 From: Abul Hasan <33129246+haxkd@users.noreply.github.com> Date: Fri, 6 Oct 2023 04:15:10 +0530 Subject: [PATCH 277/757] Match a pattern and String using backtracking (#9861) * Fix: Issue 9588 * Fix: Issue 9588 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix: Issue 9588 * Fix: Issue #9588 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix: Issue #9588 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix: Issue #9588 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix: issue #9793 * fix: issue #9793 * fix: issue #9588 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix: issue #9844 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix: issue #9844 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix: issue #9844 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix: issue #9844 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- backtracking/match_word_pattern.py | 61 ++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 backtracking/match_word_pattern.py diff --git a/backtracking/match_word_pattern.py b/backtracking/match_word_pattern.py new file mode 100644 index 000000000000..bfa9b1354d51 --- /dev/null +++ b/backtracking/match_word_pattern.py @@ -0,0 +1,61 @@ +def match_word_pattern(pattern: str, input_string: str) -> bool: + """ + Determine if a given pattern matches a string using backtracking. + + pattern: The pattern to match. + input_string: The string to match against the pattern. + return: True if the pattern matches the string, False otherwise. + + >>> match_word_pattern("aba", "GraphTreesGraph") + True + + >>> match_word_pattern("xyx", "PythonRubyPython") + True + + >>> match_word_pattern("GG", "PythonJavaPython") + False + """ + + def backtrack(pattern_index: int, str_index: int) -> bool: + """ + >>> backtrack(0, 0) + True + + >>> backtrack(0, 1) + True + + >>> backtrack(0, 4) + False + """ + if pattern_index == len(pattern) and str_index == len(input_string): + return True + if pattern_index == len(pattern) or str_index == len(input_string): + return False + char = pattern[pattern_index] + if char in pattern_map: + mapped_str = pattern_map[char] + if input_string.startswith(mapped_str, str_index): + return backtrack(pattern_index + 1, str_index + len(mapped_str)) + else: + return False + for end in range(str_index + 1, len(input_string) + 1): + substr = input_string[str_index:end] + if substr in str_map: + continue + pattern_map[char] = substr + str_map[substr] = char + if backtrack(pattern_index + 1, end): + return True + del pattern_map[char] + del str_map[substr] + return False + + pattern_map: dict[str, str] = {} + str_map: dict[str, str] = {} + return backtrack(0, 0) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From cd684fd94762c4df5529d19d1ede6fc927428815 Mon Sep 17 00:00:00 2001 From: Dean Bring Date: Thu, 5 Oct 2023 15:45:40 -0700 Subject: [PATCH 278/757] Added algorithm to deeply clone a graph (#9765) * Added algorithm to deeply clone a graph * Fixed file name and removed a function call * Removed nested function and fixed class parameter types * Fixed doctests * bug fix * Added class decorator * Updated doctests and fixed precommit errors * Cleaned up code * Simplified doctest * Added doctests * Code simplification --- graphs/deep_clone_graph.py | 77 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 graphs/deep_clone_graph.py diff --git a/graphs/deep_clone_graph.py b/graphs/deep_clone_graph.py new file mode 100644 index 000000000000..55678b4c01ec --- /dev/null +++ b/graphs/deep_clone_graph.py @@ -0,0 +1,77 @@ +""" +LeetCode 133. Clone Graph +https://leetcode.com/problems/clone-graph/ + +Given a reference of a node in a connected undirected graph. + +Return a deep copy (clone) of the graph. + +Each node in the graph contains a value (int) and a list (List[Node]) of its +neighbors. +""" +from dataclasses import dataclass + + +@dataclass +class Node: + value: int = 0 + neighbors: list["Node"] | None = None + + def __post_init__(self) -> None: + """ + >>> Node(3).neighbors + [] + """ + self.neighbors = self.neighbors or [] + + def __hash__(self) -> int: + """ + >>> hash(Node(3)) != 0 + True + """ + return id(self) + + +def clone_graph(node: Node | None) -> Node | None: + """ + This function returns a clone of a connected undirected graph. + >>> clone_graph(Node(1)) + Node(value=1, neighbors=[]) + >>> clone_graph(Node(1, [Node(2)])) + Node(value=1, neighbors=[Node(value=2, neighbors=[])]) + >>> clone_graph(None) is None + True + """ + if not node: + return None + + originals_to_clones = {} # map nodes to clones + + stack = [node] + + while stack: + original = stack.pop() + + if original in originals_to_clones: + continue + + originals_to_clones[original] = Node(original.value) + + stack.extend(original.neighbors or []) + + for original, clone in originals_to_clones.items(): + for neighbor in original.neighbors or []: + cloned_neighbor = originals_to_clones[neighbor] + + if not clone.neighbors: + clone.neighbors = [] + + clone.neighbors.append(cloned_neighbor) + + return originals_to_clones[node] + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 9200c64464492117bff792f1f43b19050070af4a Mon Sep 17 00:00:00 2001 From: Aroson <74296409+Aroson1@users.noreply.github.com> Date: Fri, 6 Oct 2023 04:46:51 +0530 Subject: [PATCH 279/757] Added Wheatstone Bridge Algorithm (#9872) * Add files via upload * Update wheatstone_bridge.py * Update wheatstone_bridge.py --- electronics/wheatstone_bridge.py | 41 ++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 electronics/wheatstone_bridge.py diff --git a/electronics/wheatstone_bridge.py b/electronics/wheatstone_bridge.py new file mode 100644 index 000000000000..3529a09339c4 --- /dev/null +++ b/electronics/wheatstone_bridge.py @@ -0,0 +1,41 @@ +# https://en.wikipedia.org/wiki/Wheatstone_bridge +from __future__ import annotations + + +def wheatstone_solver( + resistance_1: float, resistance_2: float, resistance_3: float +) -> float: + """ + This function can calculate the unknown resistance in an wheatstone network, + given that the three other resistances in the network are known. + The formula to calculate the same is: + + --------------- + |Rx=(R2/R1)*R3| + --------------- + + Usage examples: + >>> wheatstone_solver(resistance_1=2, resistance_2=4, resistance_3=5) + 10.0 + >>> wheatstone_solver(resistance_1=356, resistance_2=234, resistance_3=976) + 641.5280898876405 + >>> wheatstone_solver(resistance_1=2, resistance_2=-1, resistance_3=2) + Traceback (most recent call last): + ... + ValueError: All resistance values must be positive + >>> wheatstone_solver(resistance_1=0, resistance_2=0, resistance_3=2) + Traceback (most recent call last): + ... + ValueError: All resistance values must be positive + """ + + if resistance_1 <= 0 or resistance_2 <= 0 or resistance_3 <= 0: + raise ValueError("All resistance values must be positive") + else: + return float((resistance_2 / resistance_1) * resistance_3) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 19fc788197474f75c56cc3755582cc583be9e52f Mon Sep 17 00:00:00 2001 From: ojas wani <52542740+ojas-wani@users.noreply.github.com> Date: Thu, 5 Oct 2023 16:43:45 -0700 Subject: [PATCH 280/757] added laplacian_filter file (#9783) * added laplacian_filter file * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * updated laplacian.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * updated laplacian_py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * updated laplacian_filter.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * updated laplacian_filter.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * updated laplacian_filter.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * required changes to laplacian file * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * changed laplacian_filter.py * changed laplacian_filter.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * changed laplacian_filter.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * changed laplacian_filter.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * updated laplacian_filter.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * update laplacian_filter.py * update laplacian_filter.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * update laplacian_filter.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * changed laplacian_filter.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * changed laplacian_filter.py * changed laplacian_filter.py * changed laplacian_filter.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update laplacian_filter.py * Add a test --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- .../filters/laplacian_filter.py | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 digital_image_processing/filters/laplacian_filter.py diff --git a/digital_image_processing/filters/laplacian_filter.py b/digital_image_processing/filters/laplacian_filter.py new file mode 100644 index 000000000000..69b9616e4d30 --- /dev/null +++ b/digital_image_processing/filters/laplacian_filter.py @@ -0,0 +1,81 @@ +# @Author : ojas-wani +# @File : laplacian_filter.py +# @Date : 10/04/2023 + +import numpy as np +from cv2 import ( + BORDER_DEFAULT, + COLOR_BGR2GRAY, + CV_64F, + cvtColor, + filter2D, + imread, + imshow, + waitKey, +) + +from digital_image_processing.filters.gaussian_filter import gaussian_filter + + +def my_laplacian(src: np.ndarray, ksize: int) -> np.ndarray: + """ + :param src: the source image, which should be a grayscale or color image. + :param ksize: the size of the kernel used to compute the Laplacian filter, + which can be 1, 3, 5, or 7. + + >>> my_laplacian(src=np.array([]), ksize=0) + Traceback (most recent call last): + ... + ValueError: ksize must be in (1, 3, 5, 7) + """ + kernels = { + 1: np.array([[0, -1, 0], [-1, 4, -1], [0, -1, 0]]), + 3: np.array([[0, 1, 0], [1, -4, 1], [0, 1, 0]]), + 5: np.array( + [ + [0, 0, -1, 0, 0], + [0, -1, -2, -1, 0], + [-1, -2, 16, -2, -1], + [0, -1, -2, -1, 0], + [0, 0, -1, 0, 0], + ] + ), + 7: np.array( + [ + [0, 0, 0, -1, 0, 0, 0], + [0, 0, -2, -3, -2, 0, 0], + [0, -2, -7, -10, -7, -2, 0], + [-1, -3, -10, 68, -10, -3, -1], + [0, -2, -7, -10, -7, -2, 0], + [0, 0, -2, -3, -2, 0, 0], + [0, 0, 0, -1, 0, 0, 0], + ] + ), + } + if ksize not in kernels: + msg = f"ksize must be in {tuple(kernels)}" + raise ValueError(msg) + + # Apply the Laplacian kernel using convolution + return filter2D( + src, CV_64F, kernels[ksize], 0, borderType=BORDER_DEFAULT, anchor=(0, 0) + ) + + +if __name__ == "__main__": + # read original image + img = imread(r"../image_data/lena.jpg") + + # turn image in gray scale value + gray = cvtColor(img, COLOR_BGR2GRAY) + + # Applying gaussian filter + blur_image = gaussian_filter(gray, 3, sigma=1) + + # Apply multiple Kernel to detect edges + laplacian_image = my_laplacian(ksize=3, src=blur_image) + + imshow("Original image", img) + imshow("Detected edges using laplacian filter", laplacian_image) + + waitKey(0) From 17af6444497a64dbe803904e2ef27d0e2a280f8c Mon Sep 17 00:00:00 2001 From: JeevaRamanathan <64531160+JeevaRamanathan@users.noreply.github.com> Date: Fri, 6 Oct 2023 05:30:58 +0530 Subject: [PATCH 281/757] Symmetric tree (#9871) * symmectric tree * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * removed trailing spaces * escape sequence fix * added return type * added class * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * wordings fix * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * added static method * added type * added static method * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * wordings fix * testcase added * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * testcase added for mirror function * testcase added for mirror function * made the requested changes * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * made the requested changes * doc test added for symmetric, asymmetric * Update symmetric_tree.py --------- Co-authored-by: jeevaramanthan.m Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- data_structures/binary_tree/symmetric_tree.py | 101 ++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 data_structures/binary_tree/symmetric_tree.py diff --git a/data_structures/binary_tree/symmetric_tree.py b/data_structures/binary_tree/symmetric_tree.py new file mode 100644 index 000000000000..331a25849c1c --- /dev/null +++ b/data_structures/binary_tree/symmetric_tree.py @@ -0,0 +1,101 @@ +""" +Given the root of a binary tree, check whether it is a mirror of itself +(i.e., symmetric around its center). + +Leetcode reference: https://leetcode.com/problems/symmetric-tree/ +""" +from __future__ import annotations + +from dataclasses import dataclass + + +@dataclass +class Node: + """ + A Node has data variable and pointers to Nodes to its left and right. + """ + + data: int + left: Node | None = None + right: Node | None = None + + +def make_symmetric_tree() -> Node: + r""" + Create a symmetric tree for testing. + The tree looks like this: + 1 + / \ + 2 2 + / \ / \ + 3 4 4 3 + """ + root = Node(1) + root.left = Node(2) + root.right = Node(2) + root.left.left = Node(3) + root.left.right = Node(4) + root.right.left = Node(4) + root.right.right = Node(3) + return root + + +def make_asymmetric_tree() -> Node: + r""" + Create a asymmetric tree for testing. + The tree looks like this: + 1 + / \ + 2 2 + / \ / \ + 3 4 3 4 + """ + root = Node(1) + root.left = Node(2) + root.right = Node(2) + root.left.left = Node(3) + root.left.right = Node(4) + root.right.left = Node(3) + root.right.right = Node(4) + return root + + +def is_symmetric_tree(tree: Node) -> bool: + """ + Test cases for is_symmetric_tree function + >>> is_symmetric_tree(make_symmetric_tree()) + True + >>> is_symmetric_tree(make_asymmetric_tree()) + False + """ + if tree: + return is_mirror(tree.left, tree.right) + return True # An empty tree is considered symmetric. + + +def is_mirror(left: Node | None, right: Node | None) -> bool: + """ + >>> tree1 = make_symmetric_tree() + >>> tree1.right.right = Node(3) + >>> is_mirror(tree1.left, tree1.right) + True + >>> tree2 = make_asymmetric_tree() + >>> is_mirror(tree2.left, tree2.right) + False + """ + if left is None and right is None: + # Both sides are empty, which is symmetric. + return True + if left is None or right is None: + # One side is empty while the other is not, which is not symmetric. + return False + if left.data == right.data: + # The values match, so check the subtree + return is_mirror(left.left, right.right) and is_mirror(left.right, right.left) + return False + + +if __name__ == "__main__": + from doctest import testmod + + testmod() From d0c54acd75cedf14cff353869482a0487fea1697 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Fri, 6 Oct 2023 04:31:11 +0200 Subject: [PATCH 282/757] Use dataclasses in singly_linked_list.py (#9886) --- DIRECTORY.md | 7 + .../linked_list/singly_linked_list.py | 151 ++++++++++-------- 2 files changed, 93 insertions(+), 65 deletions(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index c199a4329202..a975b9264be0 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -25,6 +25,7 @@ * [Combination Sum](backtracking/combination_sum.py) * [Hamiltonian Cycle](backtracking/hamiltonian_cycle.py) * [Knight Tour](backtracking/knight_tour.py) + * [Match Word Pattern](backtracking/match_word_pattern.py) * [Minimax](backtracking/minimax.py) * [N Queens](backtracking/n_queens.py) * [N Queens Math](backtracking/n_queens_math.py) @@ -199,6 +200,7 @@ * [Red Black Tree](data_structures/binary_tree/red_black_tree.py) * [Segment Tree](data_structures/binary_tree/segment_tree.py) * [Segment Tree Other](data_structures/binary_tree/segment_tree_other.py) + * [Symmetric Tree](data_structures/binary_tree/symmetric_tree.py) * [Treap](data_structures/binary_tree/treap.py) * [Wavelet Tree](data_structures/binary_tree/wavelet_tree.py) * Disjoint Set @@ -277,6 +279,7 @@ * [Convolve](digital_image_processing/filters/convolve.py) * [Gabor Filter](digital_image_processing/filters/gabor_filter.py) * [Gaussian Filter](digital_image_processing/filters/gaussian_filter.py) + * [Laplacian Filter](digital_image_processing/filters/laplacian_filter.py) * [Local Binary Pattern](digital_image_processing/filters/local_binary_pattern.py) * [Median Filter](digital_image_processing/filters/median_filter.py) * [Sobel Filter](digital_image_processing/filters/sobel_filter.py) @@ -365,8 +368,10 @@ * [Ind Reactance](electronics/ind_reactance.py) * [Ohms Law](electronics/ohms_law.py) * [Real And Reactive Power](electronics/real_and_reactive_power.py) + * [Resistor Color Code](electronics/resistor_color_code.py) * [Resistor Equivalence](electronics/resistor_equivalence.py) * [Resonant Frequency](electronics/resonant_frequency.py) + * [Wheatstone Bridge](electronics/wheatstone_bridge.py) ## File Transfer * [Receive File](file_transfer/receive_file.py) @@ -415,6 +420,7 @@ * [Check Bipartite Graph Dfs](graphs/check_bipartite_graph_dfs.py) * [Check Cycle](graphs/check_cycle.py) * [Connected Components](graphs/connected_components.py) + * [Deep Clone Graph](graphs/deep_clone_graph.py) * [Depth First Search](graphs/depth_first_search.py) * [Depth First Search 2](graphs/depth_first_search_2.py) * [Dijkstra](graphs/dijkstra.py) @@ -1159,6 +1165,7 @@ * [Autocomplete Using Trie](strings/autocomplete_using_trie.py) * [Barcode Validator](strings/barcode_validator.py) * [Boyer Moore Search](strings/boyer_moore_search.py) + * [Camel Case To Snake Case](strings/camel_case_to_snake_case.py) * [Can String Be Rearranged As Palindrome](strings/can_string_be_rearranged_as_palindrome.py) * [Capitalize](strings/capitalize.py) * [Check Anagrams](strings/check_anagrams.py) diff --git a/data_structures/linked_list/singly_linked_list.py b/data_structures/linked_list/singly_linked_list.py index f4b2ddce12d7..2c6713a47ad9 100644 --- a/data_structures/linked_list/singly_linked_list.py +++ b/data_structures/linked_list/singly_linked_list.py @@ -1,27 +1,38 @@ +from __future__ import annotations + +from collections.abc import Iterator +from dataclasses import dataclass from typing import Any +@dataclass class Node: - def __init__(self, data: Any): - """ - Create and initialize Node class instance. - >>> Node(20) - Node(20) - >>> Node("Hello, world!") - Node(Hello, world!) - >>> Node(None) - Node(None) - >>> Node(True) - Node(True) - """ - self.data = data - self.next = None + """ + Create and initialize Node class instance. + >>> Node(20) + Node(20) + >>> Node("Hello, world!") + Node(Hello, world!) + >>> Node(None) + Node(None) + >>> Node(True) + Node(True) + """ + + data: Any + next_node: Node | None = None def __repr__(self) -> str: """ Get the string representation of this node. >>> Node(10).__repr__() 'Node(10)' + >>> repr(Node(10)) + 'Node(10)' + >>> str(Node(10)) + 'Node(10)' + >>> Node(10) + Node(10) """ return f"Node({self.data})" @@ -31,10 +42,12 @@ def __init__(self): """ Create and initialize LinkedList class instance. >>> linked_list = LinkedList() + >>> linked_list.head is None + True """ self.head = None - def __iter__(self) -> Any: + def __iter__(self) -> Iterator[Any]: """ This function is intended for iterators to access and iterate through data inside linked list. @@ -51,7 +64,7 @@ def __iter__(self) -> Any: node = self.head while node: yield node.data - node = node.next + node = node.next_node def __len__(self) -> int: """ @@ -81,9 +94,16 @@ def __repr__(self) -> str: >>> linked_list.insert_tail(1) >>> linked_list.insert_tail(3) >>> linked_list.__repr__() - '1->3' + '1 -> 3' + >>> repr(linked_list) + '1 -> 3' + >>> str(linked_list) + '1 -> 3' + >>> linked_list.insert_tail(5) + >>> f"{linked_list}" + '1 -> 3 -> 5' """ - return "->".join([str(item) for item in self]) + return " -> ".join([str(item) for item in self]) def __getitem__(self, index: int) -> Any: """ @@ -134,7 +154,7 @@ def __setitem__(self, index: int, data: Any) -> None: raise ValueError("list index out of range.") current = self.head for _ in range(index): - current = current.next + current = current.next_node current.data = data def insert_tail(self, data: Any) -> None: @@ -146,10 +166,10 @@ def insert_tail(self, data: Any) -> None: tail >>> linked_list.insert_tail("tail_2") >>> linked_list - tail->tail_2 + tail -> tail_2 >>> linked_list.insert_tail("tail_3") >>> linked_list - tail->tail_2->tail_3 + tail -> tail_2 -> tail_3 """ self.insert_nth(len(self), data) @@ -162,10 +182,10 @@ def insert_head(self, data: Any) -> None: head >>> linked_list.insert_head("head_2") >>> linked_list - head_2->head + head_2 -> head >>> linked_list.insert_head("head_3") >>> linked_list - head_3->head_2->head + head_3 -> head_2 -> head """ self.insert_nth(0, data) @@ -177,13 +197,13 @@ def insert_nth(self, index: int, data: Any) -> None: >>> linked_list.insert_tail("second") >>> linked_list.insert_tail("third") >>> linked_list - first->second->third + first -> second -> third >>> linked_list.insert_nth(1, "fourth") >>> linked_list - first->fourth->second->third + first -> fourth -> second -> third >>> linked_list.insert_nth(3, "fifth") >>> linked_list - first->fourth->second->fifth->third + first -> fourth -> second -> fifth -> third """ if not 0 <= index <= len(self): raise IndexError("list index out of range") @@ -191,14 +211,14 @@ def insert_nth(self, index: int, data: Any) -> None: if self.head is None: self.head = new_node elif index == 0: - new_node.next = self.head # link new_node to head + new_node.next_node = self.head # link new_node to head self.head = new_node else: temp = self.head for _ in range(index - 1): - temp = temp.next - new_node.next = temp.next - temp.next = new_node + temp = temp.next_node + new_node.next_node = temp.next_node + temp.next_node = new_node def print_list(self) -> None: # print every node data """ @@ -208,7 +228,7 @@ def print_list(self) -> None: # print every node data >>> linked_list.insert_tail("second") >>> linked_list.insert_tail("third") >>> linked_list - first->second->third + first -> second -> third """ print(self) @@ -221,11 +241,11 @@ def delete_head(self) -> Any: >>> linked_list.insert_tail("second") >>> linked_list.insert_tail("third") >>> linked_list - first->second->third + first -> second -> third >>> linked_list.delete_head() 'first' >>> linked_list - second->third + second -> third >>> linked_list.delete_head() 'second' >>> linked_list @@ -248,11 +268,11 @@ def delete_tail(self) -> Any: # delete from tail >>> linked_list.insert_tail("second") >>> linked_list.insert_tail("third") >>> linked_list - first->second->third + first -> second -> third >>> linked_list.delete_tail() 'third' >>> linked_list - first->second + first -> second >>> linked_list.delete_tail() 'second' >>> linked_list @@ -275,11 +295,11 @@ def delete_nth(self, index: int = 0) -> Any: >>> linked_list.insert_tail("second") >>> linked_list.insert_tail("third") >>> linked_list - first->second->third + first -> second -> third >>> linked_list.delete_nth(1) # delete middle 'second' >>> linked_list - first->third + first -> third >>> linked_list.delete_nth(5) # this raises error Traceback (most recent call last): ... @@ -293,13 +313,13 @@ def delete_nth(self, index: int = 0) -> Any: raise IndexError("List index out of range.") delete_node = self.head # default first node if index == 0: - self.head = self.head.next + self.head = self.head.next_node else: temp = self.head for _ in range(index - 1): - temp = temp.next - delete_node = temp.next - temp.next = temp.next.next + temp = temp.next_node + delete_node = temp.next_node + temp.next_node = temp.next_node.next_node return delete_node.data def is_empty(self) -> bool: @@ -322,22 +342,22 @@ def reverse(self) -> None: >>> linked_list.insert_tail("second") >>> linked_list.insert_tail("third") >>> linked_list - first->second->third + first -> second -> third >>> linked_list.reverse() >>> linked_list - third->second->first + third -> second -> first """ prev = None current = self.head while current: # Store the current node's next node. - next_node = current.next - # Make the current node's next point backwards - current.next = prev + next_node = current.next_node + # Make the current node's next_node point backwards + current.next_node = prev # Make the previous node be the current node prev = current - # Make the current node the next node (to progress iteration) + # Make the current node the next_node node (to progress iteration) current = next_node # Return prev in order to put the head at the end self.head = prev @@ -366,17 +386,17 @@ def test_singly_linked_list() -> None: 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)) + assert str(linked_list) == " -> ".join(str(i) for i in range(1, 11)) linked_list.insert_head(0) linked_list.insert_tail(11) - assert str(linked_list) == "->".join(str(i) for i in range(12)) + assert str(linked_list) == " -> ".join(str(i) for i in range(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 str(linked_list) == " -> ".join(str(i) for i in range(1, 10)) assert all(linked_list[i] == i + 1 for i in range(9)) is True @@ -385,7 +405,7 @@ def test_singly_linked_list() -> None: assert all(linked_list[i] == -i for i in range(9)) is True linked_list.reverse() - assert str(linked_list) == "->".join(str(i) for i in range(-8, 1)) + assert str(linked_list) == " -> ".join(str(i) for i in range(-8, 1)) def test_singly_linked_list_2() -> None: @@ -417,56 +437,57 @@ def test_singly_linked_list_2() -> None: # Check if it's empty or not assert linked_list.is_empty() is False assert ( - str(linked_list) == "-9->100->Node(77345112)->dlrow olleH->7->5555->0->" - "-192.55555->Hello, world!->77.9->Node(10)->None->None->12.2" + str(linked_list) + == "-9 -> 100 -> Node(77345112) -> dlrow olleH -> 7 -> 5555 -> " + "0 -> -192.55555 -> Hello, world! -> 77.9 -> Node(10) -> None -> None -> 12.2" ) # Delete the head result = linked_list.delete_head() assert result == -9 assert ( - str(linked_list) == "100->Node(77345112)->dlrow olleH->7->5555->0->-192.55555->" - "Hello, world!->77.9->Node(10)->None->None->12.2" + str(linked_list) == "100 -> Node(77345112) -> dlrow olleH -> 7 -> 5555 -> 0 -> " + "-192.55555 -> Hello, world! -> 77.9 -> Node(10) -> None -> None -> 12.2" ) # Delete the tail result = linked_list.delete_tail() assert result == 12.2 assert ( - str(linked_list) == "100->Node(77345112)->dlrow olleH->7->5555->0->-192.55555->" - "Hello, world!->77.9->Node(10)->None->None" + str(linked_list) == "100 -> Node(77345112) -> dlrow olleH -> 7 -> 5555 -> 0 -> " + "-192.55555 -> Hello, world! -> 77.9 -> Node(10) -> None -> None" ) # Delete a node in specific location in linked list result = linked_list.delete_nth(10) assert result is None assert ( - str(linked_list) == "100->Node(77345112)->dlrow olleH->7->5555->0->-192.55555->" - "Hello, world!->77.9->Node(10)->None" + str(linked_list) == "100 -> Node(77345112) -> dlrow olleH -> 7 -> 5555 -> 0 -> " + "-192.55555 -> Hello, world! -> 77.9 -> Node(10) -> None" ) # Add a Node instance to its head linked_list.insert_head(Node("Hello again, world!")) assert ( str(linked_list) - == "Node(Hello again, world!)->100->Node(77345112)->dlrow olleH->" - "7->5555->0->-192.55555->Hello, world!->77.9->Node(10)->None" + == "Node(Hello again, world!) -> 100 -> Node(77345112) -> dlrow olleH -> " + "7 -> 5555 -> 0 -> -192.55555 -> Hello, world! -> 77.9 -> Node(10) -> None" ) # Add None to its tail linked_list.insert_tail(None) assert ( str(linked_list) - == "Node(Hello again, world!)->100->Node(77345112)->dlrow olleH->" - "7->5555->0->-192.55555->Hello, world!->77.9->Node(10)->None->None" + == "Node(Hello again, world!) -> 100 -> Node(77345112) -> dlrow olleH -> 7 -> " + "5555 -> 0 -> -192.55555 -> Hello, world! -> 77.9 -> Node(10) -> None -> None" ) # Reverse the linked list linked_list.reverse() assert ( str(linked_list) - == "None->None->Node(10)->77.9->Hello, world!->-192.55555->0->5555->" - "7->dlrow olleH->Node(77345112)->100->Node(Hello again, world!)" + == "None -> None -> Node(10) -> 77.9 -> Hello, world! -> -192.55555 -> 0 -> " + "5555 -> 7 -> dlrow olleH -> Node(77345112) -> 100 -> Node(Hello again, world!)" ) From 795e97e87f6760a693769097613ace56a6addc8d Mon Sep 17 00:00:00 2001 From: Sarvjeet Singh <63469455+aazad20@users.noreply.github.com> Date: Fri, 6 Oct 2023 19:19:34 +0530 Subject: [PATCH 283/757] Added Majority Voting Algorithm (#9866) * Create MajorityVoteAlgorithm.py * Update and rename MajorityVoteAlgorithm.py to majorityvotealgorithm.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update and rename majorityvotealgorithm.py to majority_vote_algorithm.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update majority_vote_algorithm.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update majority_vote_algorithm.py * Update majority_vote_algorithm.py * Update other/majority_vote_algorithm.py Co-authored-by: Christian Clauss * renaming variables majority_vote_algorithm.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update majority_vote_algorithm.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update majority_vote_algorithm.py * Update majority_vote_algorithm.py * Update majority_vote_algorithm.py * Update majority_vote_algorithm.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update other/majority_vote_algorithm.py Co-authored-by: Christian Clauss * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update other/majority_vote_algorithm.py Co-authored-by: Christian Clauss * adding more testcases majority_vote_algorithm.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update majority_vote_algorithm.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update majority_vote_algorithm.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- other/majority_vote_algorithm.py | 37 ++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 other/majority_vote_algorithm.py diff --git a/other/majority_vote_algorithm.py b/other/majority_vote_algorithm.py new file mode 100644 index 000000000000..ab8b386dd2e5 --- /dev/null +++ b/other/majority_vote_algorithm.py @@ -0,0 +1,37 @@ +""" +This is Booyer-Moore Majority Vote Algorithm. The problem statement goes like this: +Given an integer array of size n, find all elements that appear more than ⌊ n/k ⌋ times. +We have to solve in O(n) time and O(1) Space. +URL : https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_majority_vote_algorithm +""" +from collections import Counter + + +def majority_vote(votes: list[int], votes_needed_to_win: int) -> list[int]: + """ + >>> majority_vote([1, 2, 2, 3, 1, 3, 2], 3) + [2] + >>> majority_vote([1, 2, 2, 3, 1, 3, 2], 2) + [] + >>> majority_vote([1, 2, 2, 3, 1, 3, 2], 4) + [1, 2, 3] + """ + majority_candidate_counter: Counter[int] = Counter() + for vote in votes: + majority_candidate_counter[vote] += 1 + if len(majority_candidate_counter) == votes_needed_to_win: + majority_candidate_counter -= Counter(set(majority_candidate_counter)) + majority_candidate_counter = Counter( + vote for vote in votes if vote in majority_candidate_counter + ) + return [ + vote + for vote in majority_candidate_counter + if majority_candidate_counter[vote] > len(votes) / votes_needed_to_win + ] + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 995c5533c645250c120b11f0eddc53909fc3d012 Mon Sep 17 00:00:00 2001 From: fxdup <47389903+fxdup@users.noreply.github.com> Date: Fri, 6 Oct 2023 14:46:58 -0400 Subject: [PATCH 284/757] Consolidate gamma (#9769) * refactor(gamma): Append _iterative to func name * refactor(gamma): Consolidate implementations * refactor(gamma): Redundant test function removal * Update maths/gamma.py --------- Co-authored-by: Tianyi Zheng --- maths/gamma.py | 91 ++++++++++++++++++++++++++++++++++------ maths/gamma_recursive.py | 77 ---------------------------------- 2 files changed, 79 insertions(+), 89 deletions(-) delete mode 100644 maths/gamma_recursive.py diff --git a/maths/gamma.py b/maths/gamma.py index d5debc58764b..822bbc74456f 100644 --- a/maths/gamma.py +++ b/maths/gamma.py @@ -1,35 +1,43 @@ +""" +Gamma function is a very useful tool in math and physics. +It helps calculating complex integral in a convenient way. +for more info: https://en.wikipedia.org/wiki/Gamma_function +In mathematics, the gamma function is one commonly +used extension of the factorial function to complex numbers. +The gamma function is defined for all complex numbers except +the non-positive integers +Python's Standard Library math.gamma() function overflows around gamma(171.624). +""" import math from numpy import inf from scipy.integrate import quad -def gamma(num: float) -> float: +def gamma_iterative(num: float) -> float: """ - https://en.wikipedia.org/wiki/Gamma_function - In mathematics, the gamma function is one commonly - used extension of the factorial function to complex numbers. - The gamma function is defined for all complex numbers except the non-positive - integers - >>> gamma(-1) + Calculates the value of Gamma function of num + where num is either an integer (1, 2, 3..) or a half-integer (0.5, 1.5, 2.5 ...). + + >>> gamma_iterative(-1) Traceback (most recent call last): ... ValueError: math domain error - >>> gamma(0) + >>> gamma_iterative(0) Traceback (most recent call last): ... ValueError: math domain error - >>> gamma(9) + >>> gamma_iterative(9) 40320.0 >>> from math import gamma as math_gamma - >>> all(.99999999 < gamma(i) / math_gamma(i) <= 1.000000001 + >>> all(.99999999 < gamma_iterative(i) / math_gamma(i) <= 1.000000001 ... for i in range(1, 50)) True - >>> gamma(-1)/math_gamma(-1) <= 1.000000001 + >>> gamma_iterative(-1)/math_gamma(-1) <= 1.000000001 Traceback (most recent call last): ... ValueError: math domain error - >>> gamma(3.3) - math_gamma(3.3) <= 0.00000001 + >>> gamma_iterative(3.3) - math_gamma(3.3) <= 0.00000001 True """ if num <= 0: @@ -42,7 +50,66 @@ def integrand(x: float, z: float) -> float: return math.pow(x, z - 1) * math.exp(-x) +def gamma_recursive(num: float) -> float: + """ + Calculates the value of Gamma function of num + where num is either an integer (1, 2, 3..) or a half-integer (0.5, 1.5, 2.5 ...). + Implemented using recursion + Examples: + >>> from math import isclose, gamma as math_gamma + >>> gamma_recursive(0.5) + 1.7724538509055159 + >>> gamma_recursive(1) + 1.0 + >>> gamma_recursive(2) + 1.0 + >>> gamma_recursive(3.5) + 3.3233509704478426 + >>> gamma_recursive(171.5) + 9.483367566824795e+307 + >>> all(isclose(gamma_recursive(num), math_gamma(num)) + ... for num in (0.5, 2, 3.5, 171.5)) + True + >>> gamma_recursive(0) + Traceback (most recent call last): + ... + ValueError: math domain error + >>> gamma_recursive(-1.1) + Traceback (most recent call last): + ... + ValueError: math domain error + >>> gamma_recursive(-4) + Traceback (most recent call last): + ... + ValueError: math domain error + >>> gamma_recursive(172) + Traceback (most recent call last): + ... + OverflowError: math range error + >>> gamma_recursive(1.1) + Traceback (most recent call last): + ... + NotImplementedError: num must be an integer or a half-integer + """ + if num <= 0: + raise ValueError("math domain error") + if num > 171.5: + raise OverflowError("math range error") + elif num - int(num) not in (0, 0.5): + raise NotImplementedError("num must be an integer or a half-integer") + elif num == 0.5: + return math.sqrt(math.pi) + else: + return 1.0 if num == 1 else (num - 1) * gamma_recursive(num - 1) + + if __name__ == "__main__": from doctest import testmod testmod() + num = 1.0 + while num: + num = float(input("Gamma of: ")) + print(f"gamma_iterative({num}) = {gamma_iterative(num)}") + print(f"gamma_recursive({num}) = {gamma_recursive(num)}") + print("\nEnter 0 to exit...") diff --git a/maths/gamma_recursive.py b/maths/gamma_recursive.py deleted file mode 100644 index 3d6b8c5e8138..000000000000 --- a/maths/gamma_recursive.py +++ /dev/null @@ -1,77 +0,0 @@ -""" -Gamma function is a very useful tool in math and physics. -It helps calculating complex integral in a convenient way. -for more info: https://en.wikipedia.org/wiki/Gamma_function -Python's Standard Library math.gamma() function overflows around gamma(171.624). -""" -from math import pi, sqrt - - -def gamma(num: float) -> float: - """ - Calculates the value of Gamma function of num - where num is either an integer (1, 2, 3..) or a half-integer (0.5, 1.5, 2.5 ...). - Implemented using recursion - Examples: - >>> from math import isclose, gamma as math_gamma - >>> gamma(0.5) - 1.7724538509055159 - >>> gamma(2) - 1.0 - >>> gamma(3.5) - 3.3233509704478426 - >>> gamma(171.5) - 9.483367566824795e+307 - >>> all(isclose(gamma(num), math_gamma(num)) for num in (0.5, 2, 3.5, 171.5)) - True - >>> gamma(0) - Traceback (most recent call last): - ... - ValueError: math domain error - >>> gamma(-1.1) - Traceback (most recent call last): - ... - ValueError: math domain error - >>> gamma(-4) - Traceback (most recent call last): - ... - ValueError: math domain error - >>> gamma(172) - Traceback (most recent call last): - ... - OverflowError: math range error - >>> gamma(1.1) - Traceback (most recent call last): - ... - NotImplementedError: num must be an integer or a half-integer - """ - if num <= 0: - raise ValueError("math domain error") - if num > 171.5: - raise OverflowError("math range error") - elif num - int(num) not in (0, 0.5): - raise NotImplementedError("num must be an integer or a half-integer") - elif num == 0.5: - return sqrt(pi) - else: - return 1.0 if num == 1 else (num - 1) * gamma(num - 1) - - -def test_gamma() -> None: - """ - >>> test_gamma() - """ - assert gamma(0.5) == sqrt(pi) - assert gamma(1) == 1.0 - assert gamma(2) == 1.0 - - -if __name__ == "__main__": - from doctest import testmod - - testmod() - num = 1.0 - while num: - num = float(input("Gamma of: ")) - print(f"gamma({num}) = {gamma(num)}") - print("\nEnter 0 to exit...") From c6ec99d57140cbf8b54077d379dfffeb6c7ad280 Mon Sep 17 00:00:00 2001 From: Kausthub Kannan Date: Sat, 7 Oct 2023 00:53:05 +0530 Subject: [PATCH 285/757] Added Mish Activation Function (#9942) * Added Mish Activation Function * Apply suggestions from code review --------- Co-authored-by: Tianyi Zheng --- neural_network/activation_functions/mish.py | 39 +++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 neural_network/activation_functions/mish.py diff --git a/neural_network/activation_functions/mish.py b/neural_network/activation_functions/mish.py new file mode 100644 index 000000000000..e4f98307f2ba --- /dev/null +++ b/neural_network/activation_functions/mish.py @@ -0,0 +1,39 @@ +""" +Mish Activation Function + +Use Case: Improved version of the ReLU activation function used in Computer Vision. +For more detailed information, you can refer to the following link: +https://en.wikipedia.org/wiki/Rectifier_(neural_networks)#Mish +""" + +import numpy as np + + +def mish(vector: np.ndarray) -> np.ndarray: + """ + Implements the Mish activation function. + + Parameters: + vector (np.ndarray): The input array for Mish activation. + + Returns: + np.ndarray: The input array after applying the Mish activation. + + Formula: + f(x) = x * tanh(softplus(x)) = x * tanh(ln(1 + e^x)) + + Examples: + >>> mish(vector=np.array([2.3,0.6,-2,-3.8])) + array([ 2.26211893, 0.46613649, -0.25250148, -0.08405831]) + + >>> mish(np.array([-9.2, -0.3, 0.45, -4.56])) + array([-0.00092952, -0.15113318, 0.33152014, -0.04745745]) + + """ + return vector * np.tanh(np.log(1 + np.exp(vector))) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 80a2087e0aa349b81fb6bbc5d73dae920f560e75 Mon Sep 17 00:00:00 2001 From: Kausthub Kannan Date: Sat, 7 Oct 2023 01:56:09 +0530 Subject: [PATCH 286/757] Added Softplus activation function (#9944) --- .../activation_functions/softplus.py | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 neural_network/activation_functions/softplus.py diff --git a/neural_network/activation_functions/softplus.py b/neural_network/activation_functions/softplus.py new file mode 100644 index 000000000000..35fdf41afc96 --- /dev/null +++ b/neural_network/activation_functions/softplus.py @@ -0,0 +1,37 @@ +""" +Softplus Activation Function + +Use Case: The Softplus function is a smooth approximation of the ReLU function. +For more detailed information, you can refer to the following link: +https://en.wikipedia.org/wiki/Rectifier_(neural_networks)#Softplus +""" + +import numpy as np + + +def softplus(vector: np.ndarray) -> np.ndarray: + """ + Implements the Softplus activation function. + + Parameters: + vector (np.ndarray): The input array for the Softplus activation. + + Returns: + np.ndarray: The input array after applying the Softplus activation. + + Formula: f(x) = ln(1 + e^x) + + Examples: + >>> softplus(np.array([2.3, 0.6, -2, -3.8])) + array([2.39554546, 1.03748795, 0.12692801, 0.02212422]) + + >>> softplus(np.array([-9.2, -0.3, 0.45, -4.56])) + array([1.01034298e-04, 5.54355244e-01, 9.43248946e-01, 1.04077103e-02]) + """ + return np.log(1 + np.exp(vector)) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 2122474e41f2b85500e1f9347d98c9efc15aba4e Mon Sep 17 00:00:00 2001 From: Kamil <32775019+quant12345@users.noreply.github.com> Date: Sat, 7 Oct 2023 14:09:39 +0500 Subject: [PATCH 287/757] Segmented sieve - doctests (#9945) * Replacing the generator with numpy vector operations from lu_decomposition. * Revert "Replacing the generator with numpy vector operations from lu_decomposition." This reverts commit ad217c66165898d62b76cc89ba09c2d7049b6448. * Added doctests. * Update segmented_sieve.py Removed unnecessary check. * Update segmented_sieve.py Added checks for 0 and negative numbers. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update segmented_sieve.py * Update segmented_sieve.py Added float number check. * Update segmented_sieve.py * Update segmented_sieve.py simplified verification * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update segmented_sieve.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update segmented_sieve.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * ValueError: Number 22.2 must instead be a positive integer --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- maths/segmented_sieve.py | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/maths/segmented_sieve.py b/maths/segmented_sieve.py index e950a83b752a..125390edc588 100644 --- a/maths/segmented_sieve.py +++ b/maths/segmented_sieve.py @@ -4,7 +4,36 @@ def sieve(n: int) -> list[int]: - """Segmented Sieve.""" + """ + Segmented Sieve. + + Examples: + >>> sieve(8) + [2, 3, 5, 7] + + >>> sieve(27) + [2, 3, 5, 7, 11, 13, 17, 19, 23] + + >>> sieve(0) + Traceback (most recent call last): + ... + ValueError: Number 0 must instead be a positive integer + + >>> sieve(-1) + Traceback (most recent call last): + ... + ValueError: Number -1 must instead be a positive integer + + >>> sieve(22.2) + Traceback (most recent call last): + ... + ValueError: Number 22.2 must instead be a positive integer + """ + + if n <= 0 or isinstance(n, float): + msg = f"Number {n} must instead be a positive integer" + raise ValueError(msg) + in_prime = [] start = 2 end = int(math.sqrt(n)) # Size of every segment @@ -42,4 +71,9 @@ def sieve(n: int) -> list[int]: return prime -print(sieve(10**6)) +if __name__ == "__main__": + import doctest + + doctest.testmod() + + print(f"{sieve(10**6) = }") From 678e0aa8cfdaae1d17536fdcf489bebe1e12cfc6 Mon Sep 17 00:00:00 2001 From: Saahil Mahato <115351000+saahil-mahato@users.noreply.github.com> Date: Sat, 7 Oct 2023 15:20:23 +0545 Subject: [PATCH 288/757] Mention square matrices in strassen docs and make it more clear (#9839) * refactor: fix strassen matrix multiplication docs * refactor: make docs more clear --- divide_and_conquer/strassen_matrix_multiplication.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/divide_and_conquer/strassen_matrix_multiplication.py b/divide_and_conquer/strassen_matrix_multiplication.py index 1d03950ef9fe..f529a255d2ef 100644 --- a/divide_and_conquer/strassen_matrix_multiplication.py +++ b/divide_and_conquer/strassen_matrix_multiplication.py @@ -74,7 +74,7 @@ def print_matrix(matrix: list) -> None: 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. + Algorithm. It only supports square matrices of any size that is a power of 2. """ if matrix_dimensions(matrix_a) == (2, 2): return default_matrix_multiplication(matrix_a, matrix_b) @@ -129,8 +129,8 @@ def strassen(matrix1: list, matrix2: list) -> list: new_matrix1 = matrix1 new_matrix2 = matrix2 - # Adding zeros to the matrices so that the arrays dimensions are the same and also - # power of 2 + # Adding zeros to the matrices to convert them both into square matrices of equal + # dimensions that are a power of 2 for i in range(maxim): if i < dimension1[0]: for _ in range(dimension1[1], maxim): From 78af0c43c623332029c9ad1d240d81577aac5d72 Mon Sep 17 00:00:00 2001 From: Pronay Debnath Date: Sat, 7 Oct 2023 21:21:30 +0530 Subject: [PATCH 289/757] Create fractional_cover_problem.py (#9973) * Create fractional_cover_problem.py * Update fractional_cover_problem.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update fractional_cover_problem.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update fractional_cover_problem.py * Update fractional_cover_problem.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update fractional_cover_problem.py * Update fractional_cover_problem.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update fractional_cover_problem.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Lose __eq__() * Update fractional_cover_problem.py * Define Item property ratio --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- greedy_methods/fractional_cover_problem.py | 102 +++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 greedy_methods/fractional_cover_problem.py diff --git a/greedy_methods/fractional_cover_problem.py b/greedy_methods/fractional_cover_problem.py new file mode 100644 index 000000000000..e37c363f1db9 --- /dev/null +++ b/greedy_methods/fractional_cover_problem.py @@ -0,0 +1,102 @@ +# https://en.wikipedia.org/wiki/Set_cover_problem + +from dataclasses import dataclass +from operator import attrgetter + + +@dataclass +class Item: + weight: int + value: int + + @property + def ratio(self) -> float: + """ + Return the value-to-weight ratio for the item. + + Returns: + float: The value-to-weight ratio for the item. + + Examples: + >>> Item(10, 65).ratio + 6.5 + + >>> Item(20, 100).ratio + 5.0 + + >>> Item(30, 120).ratio + 4.0 + """ + return self.value / self.weight + + +def fractional_cover(items: list[Item], capacity: int) -> float: + """ + Solve the Fractional Cover Problem. + + Args: + items: A list of items, where each item has weight and value attributes. + capacity: The maximum weight capacity of the knapsack. + + Returns: + The maximum value that can be obtained by selecting fractions of items to cover + the knapsack's capacity. + + Raises: + ValueError: If capacity is negative. + + Examples: + >>> fractional_cover((Item(10, 60), Item(20, 100), Item(30, 120)), capacity=50) + 240.0 + + >>> fractional_cover([Item(20, 100), Item(30, 120), Item(10, 60)], capacity=25) + 135.0 + + >>> fractional_cover([Item(10, 60), Item(20, 100), Item(30, 120)], capacity=60) + 280.0 + + >>> fractional_cover(items=[Item(5, 30), Item(10, 60), Item(15, 90)], capacity=30) + 180.0 + + >>> fractional_cover(items=[], capacity=50) + 0.0 + + >>> fractional_cover(items=[Item(10, 60)], capacity=5) + 30.0 + + >>> fractional_cover(items=[Item(10, 60)], capacity=1) + 6.0 + + >>> fractional_cover(items=[Item(10, 60)], capacity=0) + 0.0 + + >>> fractional_cover(items=[Item(10, 60)], capacity=-1) + Traceback (most recent call last): + ... + ValueError: Capacity cannot be negative + """ + if capacity < 0: + raise ValueError("Capacity cannot be negative") + + total_value = 0.0 + remaining_capacity = capacity + + # Sort the items by their value-to-weight ratio in descending order + for item in sorted(items, key=attrgetter("ratio"), reverse=True): + if remaining_capacity == 0: + break + + weight_taken = min(item.weight, remaining_capacity) + total_value += weight_taken * item.ratio + remaining_capacity -= weight_taken + + return total_value + + +if __name__ == "__main__": + import doctest + + if result := doctest.testmod().failed: + print(f"{result} test(s) failed") + else: + print("All tests passed") From 112daddc4de91d60bbdd3201fc9a6a4afc60f57a Mon Sep 17 00:00:00 2001 From: dhruvtrigotra <72982592+dhruvtrigotra@users.noreply.github.com> Date: Sun, 8 Oct 2023 00:34:24 +0530 Subject: [PATCH 290/757] charging_capacitor (#10016) * charging_capacitor * charging_capacitor * Final edits --------- Co-authored-by: Christian Clauss --- electronics/charging_capacitor.py | 71 +++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 electronics/charging_capacitor.py diff --git a/electronics/charging_capacitor.py b/electronics/charging_capacitor.py new file mode 100644 index 000000000000..4029b0ecf267 --- /dev/null +++ b/electronics/charging_capacitor.py @@ -0,0 +1,71 @@ +# source - The ARRL Handbook for Radio Communications +# https://en.wikipedia.org/wiki/RC_time_constant + +""" +Description +----------- +When a capacitor is connected with a potential source (AC or DC). It starts to charge +at a general speed but when a resistor is connected in the circuit with in series to +a capacitor then the capacitor charges slowly means it will take more time than usual. +while the capacitor is being charged, the voltage is in exponential function with time. + +'resistance(ohms) * capacitance(farads)' is called RC-timeconstant which may also be +represented as τ (tau). By using this RC-timeconstant we can find the voltage at any +time 't' from the initiation of charging a capacitor with the help of the exponential +function containing RC. Both at charging and discharging of a capacitor. +""" +from math import exp # value of exp = 2.718281828459… + + +def charging_capacitor( + source_voltage: float, # voltage in volts. + resistance: float, # resistance in ohms. + capacitance: float, # capacitance in farads. + time_sec: float, # time in seconds after charging initiation of capacitor. +) -> float: + """ + Find capacitor voltage at any nth second after initiating its charging. + + Examples + -------- + >>> charging_capacitor(source_voltage=.2,resistance=.9,capacitance=8.4,time_sec=.5) + 0.013 + + >>> charging_capacitor(source_voltage=2.2,resistance=3.5,capacitance=2.4,time_sec=9) + 1.446 + + >>> charging_capacitor(source_voltage=15,resistance=200,capacitance=20,time_sec=2) + 0.007 + + >>> charging_capacitor(20, 2000, 30*pow(10,-5), 4) + 19.975 + + >>> charging_capacitor(source_voltage=0,resistance=10.0,capacitance=.30,time_sec=3) + Traceback (most recent call last): + ... + ValueError: Source voltage must be positive. + + >>> charging_capacitor(source_voltage=20,resistance=-2000,capacitance=30,time_sec=4) + Traceback (most recent call last): + ... + ValueError: Resistance must be positive. + + >>> charging_capacitor(source_voltage=30,resistance=1500,capacitance=0,time_sec=4) + Traceback (most recent call last): + ... + ValueError: Capacitance must be positive. + """ + + if source_voltage <= 0: + raise ValueError("Source voltage must be positive.") + if resistance <= 0: + raise ValueError("Resistance must be positive.") + if capacitance <= 0: + raise ValueError("Capacitance must be positive.") + return round(source_voltage * (1 - exp(-time_sec / (resistance * capacitance))), 3) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 60291738d2552999545c414bb8a8e90f86c69678 Mon Sep 17 00:00:00 2001 From: Kosuri L Indu <118645569+kosuri-indu@users.noreply.github.com> Date: Sun, 8 Oct 2023 00:38:38 +0530 Subject: [PATCH 291/757] add : trapped water program under dynamic programming (#10027) * to add the trapped water program * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * to make changes for error : B006 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * to make changes for error : B006 * to make changes for error : B006 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * to make changes in doctest * to make changes in doctest * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update dynamic_programming/trapped_water.py Co-authored-by: Christian Clauss * Update dynamic_programming/trapped_water.py Co-authored-by: Christian Clauss * to make changes in parameters * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * to make changes in parameters * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update dynamic_programming/trapped_water.py Co-authored-by: Christian Clauss * to make changes in parameters * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * for negative heights * Update dynamic_programming/trapped_water.py Co-authored-by: Christian Clauss * to remove falsy * Final edits * tuple[int, ...] --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- dynamic_programming/trapped_water.py | 60 ++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 dynamic_programming/trapped_water.py diff --git a/dynamic_programming/trapped_water.py b/dynamic_programming/trapped_water.py new file mode 100644 index 000000000000..8bec9fac5fef --- /dev/null +++ b/dynamic_programming/trapped_water.py @@ -0,0 +1,60 @@ +""" +Given an array of non-negative integers representing an elevation map where the width +of each bar is 1, this program calculates how much rainwater can be trapped. + +Example - height = (0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1) +Output: 6 +This problem can be solved using the concept of "DYNAMIC PROGRAMMING". + +We calculate the maximum height of bars on the left and right of every bar in array. +Then iterate over the width of structure and at each index. +The amount of water that will be stored is equal to minimum of maximum height of bars +on both sides minus height of bar at current position. +""" + + +def trapped_rainwater(heights: tuple[int, ...]) -> int: + """ + The trapped_rainwater function calculates the total amount of rainwater that can be + trapped given an array of bar heights. + It uses a dynamic programming approach, determining the maximum height of bars on + both sides for each bar, and then computing the trapped water above each bar. + The function returns the total trapped water. + + >>> trapped_rainwater((0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1)) + 6 + >>> trapped_rainwater((7, 1, 5, 3, 6, 4)) + 9 + >>> trapped_rainwater((7, 1, 5, 3, 6, -1)) + Traceback (most recent call last): + ... + ValueError: No height can be negative + """ + if not heights: + return 0 + if any(h < 0 for h in heights): + raise ValueError("No height can be negative") + length = len(heights) + + left_max = [0] * length + left_max[0] = heights[0] + for i, height in enumerate(heights[1:], start=1): + left_max[i] = max(height, left_max[i - 1]) + + right_max = [0] * length + right_max[-1] = heights[-1] + for i in range(length - 2, -1, -1): + right_max[i] = max(heights[i], right_max[i + 1]) + + return sum( + min(left, right) - height + for left, right, height in zip(left_max, right_max, heights) + ) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + print(f"{trapped_rainwater((0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1)) = }") + print(f"{trapped_rainwater((7, 1, 5, 3, 6, 4)) = }") From 895dffb412d80f29c65a062bf6d91fd2a70d8818 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 7 Oct 2023 21:32:28 +0200 Subject: [PATCH 292/757] [pre-commit.ci] pre-commit autoupdate (#9543) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/astral-sh/ruff-pre-commit: v0.0.291 → v0.0.292](https://github.com/astral-sh/ruff-pre-commit/compare/v0.0.291...v0.0.292) - [github.com/codespell-project/codespell: v2.2.5 → v2.2.6](https://github.com/codespell-project/codespell/compare/v2.2.5...v2.2.6) - [github.com/tox-dev/pyproject-fmt: 1.1.0 → 1.2.0](https://github.com/tox-dev/pyproject-fmt/compare/1.1.0...1.2.0) * updating DIRECTORY.md * Fix typos in test_min_spanning_tree_prim.py * Fix typos * codespell --ignore-words-list=manuel --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: Tianyi Zheng Co-authored-by: Christian Clauss --- .pre-commit-config.yaml | 2 +- .../cnn_classification.py.DISABLED.txt | 4 +-- computer_vision/mosaic_augmentation.py | 2 +- dynamic_programming/min_distance_up_bottom.py | 11 +++--- graphs/tests/test_min_spanning_tree_prim.py | 8 ++--- hashes/sha1.py | 36 ++++++++++--------- maths/pi_generator.py | 31 +++++++--------- maths/radians.py | 4 +-- maths/square_root.py | 7 ++-- neural_network/convolution_neural_network.py | 8 ++--- neural_network/gan.py_tf | 2 +- other/graham_scan.py | 8 ++--- other/linear_congruential_generator.py | 4 +-- other/password.py | 12 +++---- physics/speed_of_sound.py | 30 +++++++--------- project_euler/problem_035/sol1.py | 12 +++---- project_euler/problem_135/sol1.py | 30 +++++++--------- project_euler/problem_493/sol1.py | 2 +- pyproject.toml | 2 +- 19 files changed, 97 insertions(+), 118 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index dbf7ff341243..8a88dcc07622 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -26,7 +26,7 @@ repos: - id: black - repo: https://github.com/codespell-project/codespell - rev: v2.2.5 + rev: v2.2.6 hooks: - id: codespell additional_dependencies: diff --git a/computer_vision/cnn_classification.py.DISABLED.txt b/computer_vision/cnn_classification.py.DISABLED.txt index 9b5f8c95eebf..b813b71033f3 100644 --- a/computer_vision/cnn_classification.py.DISABLED.txt +++ b/computer_vision/cnn_classification.py.DISABLED.txt @@ -11,10 +11,10 @@ Download dataset from : https://lhncbc.nlm.nih.gov/LHC-publications/pubs/TuberculosisChestXrayImageDataSets.html 1. Download the dataset folder and create two folder training set and test set -in the parent dataste folder +in the parent dataset folder 2. Move 30-40 image from both TB positive and TB Negative folder in the test set folder -3. The labels of the iamges will be extracted from the folder name +3. The labels of the images will be extracted from the folder name the image is present in. """ diff --git a/computer_vision/mosaic_augmentation.py b/computer_vision/mosaic_augmentation.py index c150126d6bfb..cd923dfe095f 100644 --- a/computer_vision/mosaic_augmentation.py +++ b/computer_vision/mosaic_augmentation.py @@ -8,7 +8,7 @@ import cv2 import numpy as np -# Parrameters +# Parameters OUTPUT_SIZE = (720, 1280) # Height, Width SCALE_RANGE = (0.4, 0.6) # if height or width lower than this scale, drop it. FILTER_TINY_SCALE = 1 / 100 diff --git a/dynamic_programming/min_distance_up_bottom.py b/dynamic_programming/min_distance_up_bottom.py index 4870c7ef4499..6b38a41a1c0a 100644 --- a/dynamic_programming/min_distance_up_bottom.py +++ b/dynamic_programming/min_distance_up_bottom.py @@ -1,11 +1,8 @@ """ Author : Alexander Pantyukhin Date : October 14, 2022 -This is implementation Dynamic Programming up bottom approach -to find edit distance. -The aim is to demonstate up bottom approach for solving the task. -The implementation was tested on the -leetcode: https://leetcode.com/problems/edit-distance/ +This is an implementation of the up-bottom approach to find edit distance. +The implementation was tested on Leetcode: https://leetcode.com/problems/edit-distance/ Levinstein distance Dynamic Programming: up -> down. @@ -30,10 +27,10 @@ def min_distance_up_bottom(word1: str, word2: str) -> int: @functools.cache def min_distance(index1: int, index2: int) -> int: - # if first word index is overflow - delete all from the second word + # if first word index overflows - delete all from the second word if index1 >= len_word1: return len_word2 - index2 - # if second word index is overflow - delete all from the first word + # if second word index overflows - delete all from the first word if index2 >= len_word2: return len_word1 - index1 diff = int(word1[index1] != word2[index2]) # current letters not identical diff --git a/graphs/tests/test_min_spanning_tree_prim.py b/graphs/tests/test_min_spanning_tree_prim.py index 91feab28fc81..66e5706dadb1 100644 --- a/graphs/tests/test_min_spanning_tree_prim.py +++ b/graphs/tests/test_min_spanning_tree_prim.py @@ -22,12 +22,12 @@ def test_prim_successful_result(): [1, 7, 11], ] - adjancency = defaultdict(list) + adjacency = defaultdict(list) for node1, node2, cost in edges: - adjancency[node1].append([node2, cost]) - adjancency[node2].append([node1, cost]) + adjacency[node1].append([node2, cost]) + adjacency[node2].append([node1, cost]) - result = mst(adjancency) + result = mst(adjacency) expected = [ [7, 6, 1], diff --git a/hashes/sha1.py b/hashes/sha1.py index 8a03673f3c9f..a0fa688f863e 100644 --- a/hashes/sha1.py +++ b/hashes/sha1.py @@ -1,26 +1,28 @@ """ -Demonstrates implementation of SHA1 Hash function in a Python class and gives utilities -to find hash of string or hash of text from a file. +Implementation of the SHA1 hash function and gives utilities to find hash of string or +hash of text from a file. Also contains a Test class to verify that the generated hash +matches what is returned by the hashlib library + Usage: python sha1.py --string "Hello World!!" python sha1.py --file "hello_world.txt" When run without any arguments, it prints the hash of the string "Hello World!! Welcome to Cryptography" -Also contains a Test class to verify that the generated Hash is same as that -returned by the hashlib library -SHA1 hash or SHA1 sum of a string is a cryptographic function which means it is easy +SHA1 hash or SHA1 sum of a string is a cryptographic function, which means it is easy to calculate forwards but extremely difficult to calculate backwards. What this means -is, you can easily calculate the hash of a string, but it is extremely difficult to -know the original string if you have its hash. This property is useful to communicate -securely, send encrypted messages and is very useful in payment systems, blockchain -and cryptocurrency etc. -The Algorithm as described in the reference: +is you can easily calculate the hash of a string, but it is extremely difficult to know +the original string if you have its hash. This property is useful for communicating +securely, send encrypted messages and is very useful in payment systems, blockchain and +cryptocurrency etc. + +The algorithm as described in the reference: First we start with a message. The message is padded and the length of the message is added to the end. It is then split into blocks of 512 bits or 64 bytes. The blocks are then processed one at a time. Each block must be expanded and compressed. -The value after each compression is added to a 160bit buffer called the current hash -state. After the last block is processed the current hash state is returned as +The value after each compression is added to a 160-bit buffer called the current hash +state. After the last block is processed, the current hash state is returned as the final hash. + Reference: https://deadhacker.com/2006/02/21/sha-1-illustrated/ """ import argparse @@ -30,18 +32,18 @@ class SHA1Hash: """ - Class to contain the entire pipeline for SHA1 Hashing Algorithm + Class to contain the entire pipeline for SHA1 hashing algorithm >>> SHA1Hash(bytes('Allan', 'utf-8')).final_hash() '872af2d8ac3d8695387e7c804bf0e02c18df9e6e' """ def __init__(self, data): """ - Inititates the variables data and h. h is a list of 5 8-digit Hexadecimal + Initiates the variables data and h. h is a list of 5 8-digit hexadecimal numbers corresponding to (1732584193, 4023233417, 2562383102, 271733878, 3285377520) respectively. We will start with this as a message digest. 0x is how you write - Hexadecimal numbers in Python + hexadecimal numbers in Python """ self.data = data self.h = [0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0] @@ -90,7 +92,7 @@ def final_hash(self): For each block, the variable h that was initialized is copied to a,b,c,d,e and these 5 variables a,b,c,d,e undergo several changes. After all the blocks are processed, these 5 variables are pairwise added to h ie a to h[0], b to h[1] - and so on. This h becomes our final hash which is returned. + and so on. This h becomes our final hash which is returned. """ self.padded_data = self.padding() self.blocks = self.split_blocks() @@ -135,7 +137,7 @@ def test_sha1_hash(): def main(): """ Provides option 'string' or 'file' to take input and prints the calculated SHA1 - hash. unittest.main() has been commented because we probably don't want to run + hash. unittest.main() has been commented out because we probably don't want to run the test each time. """ # unittest.main() diff --git a/maths/pi_generator.py b/maths/pi_generator.py index dcd218aae309..addd921747ba 100644 --- a/maths/pi_generator.py +++ b/maths/pi_generator.py @@ -3,60 +3,53 @@ def calculate_pi(limit: int) -> str: https://en.wikipedia.org/wiki/Leibniz_formula_for_%CF%80 Leibniz Formula for Pi - The Leibniz formula is the special case arctan 1 = 1/4 Pi . + The Leibniz formula is the special case arctan(1) = pi / 4. Leibniz's formula converges extremely slowly: it exhibits sublinear convergence. Convergence (https://en.wikipedia.org/wiki/Leibniz_formula_for_%CF%80#Convergence) We cannot try to prove against an interrupted, uncompleted generation. https://en.wikipedia.org/wiki/Leibniz_formula_for_%CF%80#Unusual_behaviour - The errors can in fact be predicted; - but those calculations also approach infinity for accuracy. + The errors can in fact be predicted, but those calculations also approach infinity + for accuracy. - Our output will always be a string since we can defintely store all digits in there. - For simplicity' sake, let's just compare against known values and since our outpit - is a string, we need to convert to float. + Our output will be a string so that we can definitely store all digits. >>> import math >>> float(calculate_pi(15)) == math.pi True - Since we cannot predict errors or interrupt any infinite alternating - series generation since they approach infinity, - or interrupt any alternating series, we are going to need math.isclose() + Since we cannot predict errors or interrupt any infinite alternating series + generation since they approach infinity, or interrupt any alternating series, we'll + need math.isclose() >>> math.isclose(float(calculate_pi(50)), math.pi) True - >>> math.isclose(float(calculate_pi(100)), math.pi) True - Since math.pi-constant contains only 16 digits, here some test with preknown values: + Since math.pi contains only 16 digits, here are some tests with known values: >>> calculate_pi(50) '3.14159265358979323846264338327950288419716939937510' >>> calculate_pi(80) '3.14159265358979323846264338327950288419716939937510582097494459230781640628620899' - - To apply the Leibniz formula for calculating pi, - the variables q, r, t, k, n, and l are used for the iteration process. """ + # Variables used for the iteration process q = 1 r = 0 t = 1 k = 1 n = 3 l = 3 + decimal = limit counter = 0 result = "" - """ - We will avoid using yield since we otherwise get a Generator-Object, - which we can't just compare against anything. We would have to make a list out of it - after the generation, so we will just stick to plain return logic: - """ + # We can't compare against anything if we make a generator, + # so we'll stick with plain return logic while counter != decimal + 1: if 4 * q + r - t < n * t: result += str(n) diff --git a/maths/radians.py b/maths/radians.py index 465467a3ba08..b8ac61cb135c 100644 --- a/maths/radians.py +++ b/maths/radians.py @@ -3,7 +3,7 @@ def radians(degree: float) -> float: """ - Coverts the given angle from degrees to radians + Converts the given angle from degrees to radians https://en.wikipedia.org/wiki/Radian >>> radians(180) @@ -16,7 +16,7 @@ def radians(degree: float) -> float: 1.9167205845401725 >>> from math import radians as math_radians - >>> all(abs(radians(i)-math_radians(i)) <= 0.00000001 for i in range(-2, 361)) + >>> all(abs(radians(i) - math_radians(i)) <= 1e-8 for i in range(-2, 361)) True """ diff --git a/maths/square_root.py b/maths/square_root.py index 2cbf14beae18..4462ccb75261 100644 --- a/maths/square_root.py +++ b/maths/square_root.py @@ -19,14 +19,13 @@ def get_initial_point(a: float) -> float: def square_root_iterative( - a: float, max_iter: int = 9999, tolerance: float = 0.00000000000001 + a: float, max_iter: int = 9999, tolerance: float = 1e-14 ) -> float: """ - Square root is aproximated using Newtons method. + Square root approximated using Newton's method. https://en.wikipedia.org/wiki/Newton%27s_method - >>> all(abs(square_root_iterative(i)-math.sqrt(i)) <= .00000000000001 - ... for i in range(500)) + >>> all(abs(square_root_iterative(i) - math.sqrt(i)) <= 1e-14 for i in range(500)) True >>> square_root_iterative(-1) diff --git a/neural_network/convolution_neural_network.py b/neural_network/convolution_neural_network.py index f5ec156f3593..f2e88fe7bd88 100644 --- a/neural_network/convolution_neural_network.py +++ b/neural_network/convolution_neural_network.py @@ -2,7 +2,7 @@ - - - - - -- - - - - - - - - - - - - - - - - - - - - - - Name - - CNN - Convolution Neural Network For Photo Recognizing Goal - - Recognize Handing Writing Word Photo - Detail:Total 5 layers neural network + Detail: Total 5 layers neural network * Convolution layer * Pooling layer * Input layer layer of BP @@ -24,7 +24,7 @@ def __init__( self, conv1_get, size_p1, bp_num1, bp_num2, bp_num3, rate_w=0.2, rate_t=0.2 ): """ - :param conv1_get: [a,c,d],size, number, step of convolution kernel + :param conv1_get: [a,c,d], size, number, step of convolution kernel :param size_p1: pooling size :param bp_num1: units number of flatten layer :param bp_num2: units number of hidden layer @@ -71,7 +71,7 @@ def save_model(self, save_path): with open(save_path, "wb") as f: pickle.dump(model_dic, f) - print(f"Model saved: {save_path}") + print(f"Model saved: {save_path}") @classmethod def read_model(cls, model_path): @@ -210,7 +210,7 @@ def _calculate_gradient_from_pool( def train( self, patterns, datas_train, datas_teach, n_repeat, error_accuracy, draw_e=bool ): - # model traning + # model training print("----------------------Start Training-------------------------") print((" - - Shape: Train_Data ", np.shape(datas_train))) print((" - - Shape: Teach_Data ", np.shape(datas_teach))) diff --git a/neural_network/gan.py_tf b/neural_network/gan.py_tf index deb062c48dc7..9c6e1c05b8b4 100644 --- a/neural_network/gan.py_tf +++ b/neural_network/gan.py_tf @@ -158,7 +158,7 @@ if __name__ == "__main__": # G_b2 = np.random.normal(size=(784),scale=(1. / np.sqrt(784 / 2.))) *0.002 G_b7 = np.zeros(784) - # 3. For Adam Optimzier + # 3. For Adam Optimizer v1, m1 = 0, 0 v2, m2 = 0, 0 v3, m3 = 0, 0 diff --git a/other/graham_scan.py b/other/graham_scan.py index 2eadb4e56668..3f11d40f141c 100644 --- a/other/graham_scan.py +++ b/other/graham_scan.py @@ -1,5 +1,5 @@ """ -This is a pure Python implementation of the merge-insertion sort algorithm +This is a pure Python implementation of the Graham scan algorithm Source: https://en.wikipedia.org/wiki/Graham_scan For doctests run following command: @@ -142,8 +142,8 @@ def graham_scan(points: list[tuple[int, int]]) -> list[tuple[int, int]]: stack.append(sorted_points[0]) stack.append(sorted_points[1]) stack.append(sorted_points[2]) - # In any ways, the first 3 points line are towards left. - # Because we sort them the angle from minx, miny. + # The first 3 points lines are towards the left because we sort them by their angle + # from minx, miny. current_direction = Direction.left for i in range(3, len(sorted_points)): @@ -164,7 +164,7 @@ def graham_scan(points: list[tuple[int, int]]) -> list[tuple[int, int]]: break elif current_direction == Direction.right: # If the straight line is towards right, - # every previous points on those straigh line is not convex hull. + # every previous points on that straight line is not convex hull. stack.pop() if next_direction == Direction.right: stack.pop() diff --git a/other/linear_congruential_generator.py b/other/linear_congruential_generator.py index c016310f9cfa..c7de15b94bbd 100644 --- a/other/linear_congruential_generator.py +++ b/other/linear_congruential_generator.py @@ -8,9 +8,9 @@ class LinearCongruentialGenerator: A pseudorandom number generator. """ - # The default value for **seed** is the result of a function call which is not + # The default value for **seed** is the result of a function call, which is not # normally recommended and causes ruff to raise a B008 error. However, in this case, - # it is accptable because `LinearCongruentialGenerator.__init__()` will only be + # it is acceptable because `LinearCongruentialGenerator.__init__()` will only be # called once per instance and it ensures that each instance will generate a unique # sequence of numbers. diff --git a/other/password.py b/other/password.py index 9a6161af87d7..1ce0d52316e6 100644 --- a/other/password.py +++ b/other/password.py @@ -63,11 +63,12 @@ def random_characters(chars_incl, i): pass # Put your code here... -# This Will Check Whether A Given Password Is Strong Or Not -# It Follows The Rule that Length Of Password Should Be At Least 8 Characters -# And At Least 1 Lower, 1 Upper, 1 Number And 1 Special Character def is_strong_password(password: str, min_length: int = 8) -> bool: """ + This will check whether a given password is strong or not. The password must be at + least as long as the provided minimum length, and it must contain at least 1 + lowercase letter, 1 uppercase letter, 1 number and 1 special character. + >>> is_strong_password('Hwea7$2!') True >>> is_strong_password('Sh0r1') @@ -81,7 +82,6 @@ def is_strong_password(password: str, min_length: int = 8) -> bool: """ if len(password) < min_length: - # Your Password must be at least 8 characters long return False upper = any(char in ascii_uppercase for char in password) @@ -90,8 +90,6 @@ def is_strong_password(password: str, min_length: int = 8) -> bool: spec_char = any(char in punctuation for char in password) return upper and lower and num and spec_char - # Passwords should contain UPPERCASE, lowerase - # numbers, and special characters def main(): @@ -104,7 +102,7 @@ def main(): "Alternative Password generated:", alternative_password_generator(chars_incl, length), ) - print("[If you are thinking of using this passsword, You better save it.]") + print("[If you are thinking of using this password, You better save it.]") if __name__ == "__main__": diff --git a/physics/speed_of_sound.py b/physics/speed_of_sound.py index a4658366a36c..3fa952cdb411 100644 --- a/physics/speed_of_sound.py +++ b/physics/speed_of_sound.py @@ -2,39 +2,35 @@ Title : Calculating the speed of sound Description : - The speed of sound (c) is the speed that a sound wave travels - per unit time (m/s). During propagation, the sound wave propagates - through an elastic medium. Its SI unit is meter per second (m/s). + The speed of sound (c) is the speed that a sound wave travels per unit time (m/s). + During propagation, the sound wave propagates through an elastic medium. - Only longitudinal waves can propagate in liquids and gas other then - solid where they also travel in transverse wave. The following Algo- - rithem calculates the speed of sound in fluid depanding on the bulk - module and the density of the fluid. + Sound propagates as longitudinal waves in liquids and gases and as transverse waves + in solids. This file calculates the speed of sound in a fluid based on its bulk + module and density. - Equation for calculating speed od sound in fluid: - c_fluid = (K_s*p)**0.5 + Equation for the speed of sound in a fluid: + c_fluid = sqrt(K_s / p) c_fluid: speed of sound in fluid K_s: isentropic bulk modulus p: density of fluid - - Source : https://en.wikipedia.org/wiki/Speed_of_sound """ def speed_of_sound_in_a_fluid(density: float, bulk_modulus: float) -> float: """ - This method calculates the speed of sound in fluid - - This is calculated from the other two provided values + Calculates the speed of sound in a fluid from its density and bulk modulus + Examples: - Example 1 --> Water 20°C: bulk_moduls= 2.15MPa, density=998kg/m³ - Example 2 --> Murcery 20°: bulk_moduls= 28.5MPa, density=13600kg/m³ + Example 1 --> Water 20°C: bulk_modulus= 2.15MPa, density=998kg/m³ + Example 2 --> Mercury 20°C: bulk_modulus= 28.5MPa, density=13600kg/m³ - >>> speed_of_sound_in_a_fluid(bulk_modulus=2.15*10**9, density=998) + >>> speed_of_sound_in_a_fluid(bulk_modulus=2.15e9, density=998) 1467.7563207952705 - >>> speed_of_sound_in_a_fluid(bulk_modulus=28.5*10**9, density=13600) + >>> speed_of_sound_in_a_fluid(bulk_modulus=28.5e9, density=13600) 1447.614670861731 """ diff --git a/project_euler/problem_035/sol1.py b/project_euler/problem_035/sol1.py index 17a4e9088ae2..644c992ed8a5 100644 --- a/project_euler/problem_035/sol1.py +++ b/project_euler/problem_035/sol1.py @@ -11,18 +11,18 @@ How many circular primes are there below one million? To solve this problem in an efficient manner, we will first mark all the primes -below 1 million using the Seive of Eratosthenes. Then, out of all these primes, -we will rule out the numbers which contain an even digit. After this we will +below 1 million using the Sieve of Eratosthenes. Then, out of all these primes, +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 __future__ import annotations -seive = [True] * 1000001 +sieve = [True] * 1000001 i = 2 while i * i <= 1000000: - if seive[i]: + if sieve[i]: for j in range(i * i, 1000001, i): - seive[j] = False + sieve[j] = False i += 1 @@ -36,7 +36,7 @@ def is_prime(n: int) -> bool: >>> is_prime(25363) False """ - return seive[n] + return sieve[n] def contains_an_even_digit(n: int) -> bool: diff --git a/project_euler/problem_135/sol1.py b/project_euler/problem_135/sol1.py index d71a0439c7e9..ac91fa4e2b9d 100644 --- a/project_euler/problem_135/sol1.py +++ b/project_euler/problem_135/sol1.py @@ -1,28 +1,22 @@ """ Project Euler Problem 135: https://projecteuler.net/problem=135 -Given the positive integers, x, y, and z, -are consecutive terms of an arithmetic progression, -the least value of the positive integer, n, -for which the equation, +Given the positive integers, x, y, and z, are consecutive terms of an arithmetic +progression, the least value of the positive integer, n, for which the equation, x2 − y2 − z2 = n, has exactly two solutions is n = 27: 342 − 272 − 202 = 122 − 92 − 62 = 27 -It turns out that n = 1155 is the least value -which has exactly ten solutions. +It turns out that n = 1155 is the least value which has exactly ten solutions. -How many values of n less than one million -have exactly ten distinct solutions? +How many values of n less than one million have exactly ten distinct solutions? -Taking x,y,z of the form a+d,a,a-d respectively, -the given equation reduces to a*(4d-a)=n. -Calculating no of solutions for every n till 1 million by fixing a -,and n must be multiple of a. -Total no of steps=n*(1/1+1/2+1/3+1/4..+1/n) -,so roughly O(nlogn) time complexity. - +Taking x, y, z of the form a + d, a, a - d respectively, the given equation reduces to +a * (4d - a) = n. +Calculating no of solutions for every n till 1 million by fixing a, and n must be a +multiple of a. Total no of steps = n * (1/1 + 1/2 + 1/3 + 1/4 + ... + 1/n), so roughly +O(nlogn) time complexity. """ @@ -42,15 +36,15 @@ def solution(limit: int = 1000000) -> int: for first_term in range(1, limit): for n in range(first_term, limit, first_term): common_difference = first_term + n / first_term - if common_difference % 4: # d must be divisble by 4 + if common_difference % 4: # d must be divisible by 4 continue else: common_difference /= 4 if ( first_term > common_difference and first_term < 4 * common_difference - ): # since x,y,z are positive integers - frequency[n] += 1 # so z>0 and a>d ,also 4d 0, a > d and 4d < a count = sum(1 for x in frequency[1:limit] if x == 10) diff --git a/project_euler/problem_493/sol1.py b/project_euler/problem_493/sol1.py index c9879a528230..4d96c6c3207e 100644 --- a/project_euler/problem_493/sol1.py +++ b/project_euler/problem_493/sol1.py @@ -9,7 +9,7 @@ This combinatorial problem can be solved by decomposing the problem into the following steps: -1. Calculate the total number of possible picking cominations +1. Calculate the total number of possible picking combinations [combinations := binom_coeff(70, 20)] 2. Calculate the number of combinations with one colour missing [missing := binom_coeff(60, 20)] diff --git a/pyproject.toml b/pyproject.toml index f9091fb8578d..75da7a04513e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -130,5 +130,5 @@ omit = [".env/*"] sort = "Cover" [tool.codespell] -ignore-words-list = "3rt,ans,crate,damon,fo,followings,hist,iff,kwanza,mater,secant,som,sur,tim,zar" +ignore-words-list = "3rt,ans,crate,damon,fo,followings,hist,iff,kwanza,manuel,mater,secant,som,sur,tim,zar" skip = "./.*,*.json,ciphers/prehistoric_men.txt,project_euler/problem_022/p022_names.txt,pyproject.toml,strings/dictionary.txt,strings/words.txt" From fa077e6703758afcae4f19347a4388b9230d568f Mon Sep 17 00:00:00 2001 From: hollowcrust <72879387+hollowcrust@users.noreply.github.com> Date: Sun, 8 Oct 2023 16:58:48 +0800 Subject: [PATCH 293/757] Add doctests, type hints; fix bug for dynamic_programming/minimum_partition.py (#10012) * Add doctests, type hints; fix bug * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- dynamic_programming/minimum_partition.py | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/dynamic_programming/minimum_partition.py b/dynamic_programming/minimum_partition.py index 3daa9767fde4..e6188cb33b3a 100644 --- a/dynamic_programming/minimum_partition.py +++ b/dynamic_programming/minimum_partition.py @@ -3,13 +3,25 @@ """ -def find_min(arr): +def find_min(arr: list[int]) -> int: + """ + >>> find_min([1, 2, 3, 4, 5]) + 1 + >>> find_min([5, 5, 5, 5, 5]) + 5 + >>> find_min([5, 5, 5, 5]) + 0 + >>> find_min([3]) + 3 + >>> find_min([]) + 0 + """ n = len(arr) s = sum(arr) dp = [[False for x in range(s + 1)] for y in range(n + 1)] - for i in range(1, n + 1): + for i in range(n + 1): dp[i][0] = True for i in range(1, s + 1): @@ -17,7 +29,7 @@ def find_min(arr): for i in range(1, n + 1): for j in range(1, s + 1): - dp[i][j] = dp[i][j - 1] + dp[i][j] = dp[i - 1][j] if arr[i - 1] <= j: dp[i][j] = dp[i][j] or dp[i - 1][j - arr[i - 1]] @@ -28,3 +40,9 @@ def find_min(arr): break return diff + + +if __name__ == "__main__": + from doctest import testmod + + testmod() From 937ce83b150f0a217c7fa63c75a095534ae8bfeb Mon Sep 17 00:00:00 2001 From: Om Alve Date: Sun, 8 Oct 2023 16:35:01 +0530 Subject: [PATCH 294/757] Added fractionated_morse_cipher (#9442) * Added fractionated_morse_cipher * Added return type hint for main function * Added doctest for main * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Replaced main function * changed the references section Co-authored-by: Christian Clauss * removed repetitive datatype hint in the docstring Co-authored-by: Christian Clauss * changed dictionary comprehension variable names to something more compact Co-authored-by: Christian Clauss * Update fractionated_morse_cipher.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- ciphers/fractionated_morse_cipher.py | 167 +++++++++++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100644 ciphers/fractionated_morse_cipher.py diff --git a/ciphers/fractionated_morse_cipher.py b/ciphers/fractionated_morse_cipher.py new file mode 100644 index 000000000000..c1d5dc6d50aa --- /dev/null +++ b/ciphers/fractionated_morse_cipher.py @@ -0,0 +1,167 @@ +""" +Python program for the Fractionated Morse Cipher. + +The Fractionated Morse cipher first converts the plaintext to Morse code, +then enciphers fixed-size blocks of Morse code back to letters. +This procedure means plaintext letters are mixed into the ciphertext letters, +making it more secure than substitution ciphers. + +http://practicalcryptography.com/ciphers/fractionated-morse-cipher/ +""" +import string + +MORSE_CODE_DICT = { + "A": ".-", + "B": "-...", + "C": "-.-.", + "D": "-..", + "E": ".", + "F": "..-.", + "G": "--.", + "H": "....", + "I": "..", + "J": ".---", + "K": "-.-", + "L": ".-..", + "M": "--", + "N": "-.", + "O": "---", + "P": ".--.", + "Q": "--.-", + "R": ".-.", + "S": "...", + "T": "-", + "U": "..-", + "V": "...-", + "W": ".--", + "X": "-..-", + "Y": "-.--", + "Z": "--..", + " ": "", +} + +# Define possible trigrams of Morse code +MORSE_COMBINATIONS = [ + "...", + "..-", + "..x", + ".-.", + ".--", + ".-x", + ".x.", + ".x-", + ".xx", + "-..", + "-.-", + "-.x", + "--.", + "---", + "--x", + "-x.", + "-x-", + "-xx", + "x..", + "x.-", + "x.x", + "x-.", + "x--", + "x-x", + "xx.", + "xx-", + "xxx", +] + +# Create a reverse dictionary for Morse code +REVERSE_DICT = {value: key for key, value in MORSE_CODE_DICT.items()} + + +def encode_to_morse(plaintext: str) -> str: + """Encode a plaintext message into Morse code. + + Args: + plaintext: The plaintext message to encode. + + Returns: + The Morse code representation of the plaintext message. + + Example: + >>> encode_to_morse("defend the east") + '-..x.x..-.x.x-.x-..xx-x....x.xx.x.-x...x-' + """ + return "x".join([MORSE_CODE_DICT.get(letter.upper(), "") for letter in plaintext]) + + +def encrypt_fractionated_morse(plaintext: str, key: str) -> str: + """Encrypt a plaintext message using Fractionated Morse Cipher. + + Args: + plaintext: The plaintext message to encrypt. + key: The encryption key. + + Returns: + The encrypted ciphertext. + + Example: + >>> encrypt_fractionated_morse("defend the east","Roundtable") + 'ESOAVVLJRSSTRX' + + """ + morse_code = encode_to_morse(plaintext) + key = key.upper() + string.ascii_uppercase + key = "".join(sorted(set(key), key=key.find)) + + # Ensure morse_code length is a multiple of 3 + padding_length = 3 - (len(morse_code) % 3) + morse_code += "x" * padding_length + + fractionated_morse_dict = {v: k for k, v in zip(key, MORSE_COMBINATIONS)} + fractionated_morse_dict["xxx"] = "" + encrypted_text = "".join( + [ + fractionated_morse_dict[morse_code[i : i + 3]] + for i in range(0, len(morse_code), 3) + ] + ) + return encrypted_text + + +def decrypt_fractionated_morse(ciphertext: str, key: str) -> str: + """Decrypt a ciphertext message encrypted with Fractionated Morse Cipher. + + Args: + ciphertext: The ciphertext message to decrypt. + key: The decryption key. + + Returns: + The decrypted plaintext message. + + Example: + >>> decrypt_fractionated_morse("ESOAVVLJRSSTRX","Roundtable") + 'DEFEND THE EAST' + """ + key = key.upper() + string.ascii_uppercase + key = "".join(sorted(set(key), key=key.find)) + + inverse_fractionated_morse_dict = dict(zip(key, MORSE_COMBINATIONS)) + morse_code = "".join( + [inverse_fractionated_morse_dict.get(letter, "") for letter in ciphertext] + ) + decrypted_text = "".join( + [REVERSE_DICT[code] for code in morse_code.split("x")] + ).strip() + return decrypted_text + + +if __name__ == "__main__": + """ + Example usage of Fractionated Morse Cipher. + """ + plaintext = "defend the east" + print("Plain Text:", plaintext) + key = "ROUNDTABLE" + + ciphertext = encrypt_fractionated_morse(plaintext, key) + print("Encrypted:", ciphertext) + + decrypted_text = decrypt_fractionated_morse(ciphertext, key) + print("Decrypted:", decrypted_text) From 08d394126c9d46fc9d227a0dc1e343ad1fa70679 Mon Sep 17 00:00:00 2001 From: Kausthub Kannan Date: Sun, 8 Oct 2023 21:18:22 +0530 Subject: [PATCH 295/757] Changed Mish Activation Function to use Softplus (#10111) --- neural_network/activation_functions/mish.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/neural_network/activation_functions/mish.py b/neural_network/activation_functions/mish.py index e4f98307f2ba..e51655df8a3f 100644 --- a/neural_network/activation_functions/mish.py +++ b/neural_network/activation_functions/mish.py @@ -7,6 +7,7 @@ """ import numpy as np +from softplus import softplus def mish(vector: np.ndarray) -> np.ndarray: @@ -30,7 +31,7 @@ def mish(vector: np.ndarray) -> np.ndarray: array([-0.00092952, -0.15113318, 0.33152014, -0.04745745]) """ - return vector * np.tanh(np.log(1 + np.exp(vector))) + return vector * np.tanh(softplus(vector)) if __name__ == "__main__": From 6860daea60a512b202481bd5dd00d6534e162b77 Mon Sep 17 00:00:00 2001 From: Aarya Balwadkar <142713127+AaryaBalwadkar@users.noreply.github.com> Date: Sun, 8 Oct 2023 21:23:38 +0530 Subject: [PATCH 296/757] Made Changes shifted CRT, modular division to maths directory (#10084) --- {blockchain => maths}/chinese_remainder_theorem.py | 0 {blockchain => maths}/modular_division.py | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename {blockchain => maths}/chinese_remainder_theorem.py (100%) rename {blockchain => maths}/modular_division.py (100%) diff --git a/blockchain/chinese_remainder_theorem.py b/maths/chinese_remainder_theorem.py similarity index 100% rename from blockchain/chinese_remainder_theorem.py rename to maths/chinese_remainder_theorem.py diff --git a/blockchain/modular_division.py b/maths/modular_division.py similarity index 100% rename from blockchain/modular_division.py rename to maths/modular_division.py From 81b29066d206217cb689fe2c9c8d530a1aa66cbe Mon Sep 17 00:00:00 2001 From: Arnav Kohli <95236897+THEGAMECHANGER416@users.noreply.github.com> Date: Sun, 8 Oct 2023 21:34:43 +0530 Subject: [PATCH 297/757] Created folder for losses in Machine_Learning (#9969) * Created folder for losses in Machine_Learning * Update binary_cross_entropy.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update mean_squared_error.py * Update binary_cross_entropy.py * Update mean_squared_error.py * Update binary_cross_entropy.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update mean_squared_error.py * Update binary_cross_entropy.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update mean_squared_error.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update binary_cross_entropy.py * Update mean_squared_error.py * Update binary_cross_entropy.py * Update mean_squared_error.py * Update machine_learning/losses/binary_cross_entropy.py Co-authored-by: Christian Clauss * Update machine_learning/losses/mean_squared_error.py Co-authored-by: Christian Clauss * Update machine_learning/losses/binary_cross_entropy.py Co-authored-by: Christian Clauss * Update mean_squared_error.py * Update machine_learning/losses/mean_squared_error.py Co-authored-by: Tianyi Zheng * Update binary_cross_entropy.py * Update mean_squared_error.py * Update binary_cross_entropy.py * Update mean_squared_error.py * Update mean_squared_error.py * Update binary_cross_entropy.py * renamed: losses -> loss_functions * updated 2 files * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update mean_squared_error.py * Update mean_squared_error.py * Update binary_cross_entropy.py * Update mean_squared_error.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss Co-authored-by: Tianyi Zheng --- .../loss_functions/binary_cross_entropy.py | 59 +++++++++++++++++++ .../loss_functions/mean_squared_error.py | 51 ++++++++++++++++ 2 files changed, 110 insertions(+) create mode 100644 machine_learning/loss_functions/binary_cross_entropy.py create mode 100644 machine_learning/loss_functions/mean_squared_error.py diff --git a/machine_learning/loss_functions/binary_cross_entropy.py b/machine_learning/loss_functions/binary_cross_entropy.py new file mode 100644 index 000000000000..4ebca7f21757 --- /dev/null +++ b/machine_learning/loss_functions/binary_cross_entropy.py @@ -0,0 +1,59 @@ +""" +Binary Cross-Entropy (BCE) Loss Function + +Description: +Quantifies dissimilarity between true labels (0 or 1) and predicted probabilities. +It's widely used in binary classification tasks. + +Formula: +BCE = -Σ(y_true * log(y_pred) + (1 - y_true) * log(1 - y_pred)) + +Source: +[Wikipedia - Cross entropy](https://en.wikipedia.org/wiki/Cross_entropy) +""" + +import numpy as np + + +def binary_cross_entropy( + y_true: np.ndarray, y_pred: np.ndarray, epsilon: float = 1e-15 +) -> float: + """ + Calculate the BCE Loss between true labels and predicted probabilities. + + Parameters: + - y_true: True binary labels (0 or 1). + - y_pred: Predicted probabilities for class 1. + - epsilon: Small constant to avoid numerical instability. + + Returns: + - bce_loss: Binary Cross-Entropy Loss. + + Example Usage: + >>> true_labels = np.array([0, 1, 1, 0, 1]) + >>> predicted_probs = np.array([0.2, 0.7, 0.9, 0.3, 0.8]) + >>> binary_cross_entropy(true_labels, predicted_probs) + 0.2529995012327421 + >>> true_labels = np.array([0, 1, 1, 0, 1]) + >>> predicted_probs = np.array([0.3, 0.8, 0.9, 0.2]) + >>> binary_cross_entropy(true_labels, predicted_probs) + Traceback (most recent call last): + ... + ValueError: Input arrays must have the same length. + """ + if len(y_true) != len(y_pred): + raise ValueError("Input arrays must have the same length.") + # Clip predicted probabilities to avoid log(0) and log(1) + y_pred = np.clip(y_pred, epsilon, 1 - epsilon) + + # Calculate binary cross-entropy loss + bce_loss = -(y_true * np.log(y_pred) + (1 - y_true) * np.log(1 - y_pred)) + + # Take the mean over all samples + return np.mean(bce_loss) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() diff --git a/machine_learning/loss_functions/mean_squared_error.py b/machine_learning/loss_functions/mean_squared_error.py new file mode 100644 index 000000000000..d2b0e1e158ba --- /dev/null +++ b/machine_learning/loss_functions/mean_squared_error.py @@ -0,0 +1,51 @@ +""" +Mean Squared Error (MSE) Loss Function + +Description: +MSE measures the mean squared difference between true values and predicted values. +It serves as a measure of the model's accuracy in regression tasks. + +Formula: +MSE = (1/n) * Σ(y_true - y_pred)^2 + +Source: +[Wikipedia - Mean squared error](https://en.wikipedia.org/wiki/Mean_squared_error) +""" + +import numpy as np + + +def mean_squared_error(y_true: np.ndarray, y_pred: np.ndarray) -> float: + """ + Calculate the Mean Squared Error (MSE) between two arrays. + + Parameters: + - y_true: The true values (ground truth). + - y_pred: The predicted values. + + Returns: + - mse: The Mean Squared Error between y_true and y_pred. + + Example usage: + >>> true_values = np.array([1.0, 2.0, 3.0, 4.0, 5.0]) + >>> predicted_values = np.array([0.8, 2.1, 2.9, 4.2, 5.2]) + >>> mean_squared_error(true_values, predicted_values) + 0.028000000000000032 + >>> true_labels = np.array([1.0, 2.0, 3.0, 4.0, 5.0]) + >>> predicted_probs = np.array([0.3, 0.8, 0.9, 0.2]) + >>> mean_squared_error(true_labels, predicted_probs) + Traceback (most recent call last): + ... + ValueError: Input arrays must have the same length. + """ + if len(y_true) != len(y_pred): + raise ValueError("Input arrays must have the same length.") + + squared_errors = (y_true - y_pred) ** 2 + return np.mean(squared_errors) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From a12b07f352d51af1cb86c14f865cf2b18aba3ea1 Mon Sep 17 00:00:00 2001 From: Kausthub Kannan Date: Sun, 8 Oct 2023 21:38:37 +0530 Subject: [PATCH 298/757] Added Squareplus Activation Function (#9977) * Added Squareplus Activation Function * Added parameter beta to the function * Fixed Squareplus Function * Update neural_network/activation_functions/squareplus.py --------- Co-authored-by: Tianyi Zheng --- .../activation_functions/squareplus.py | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 neural_network/activation_functions/squareplus.py diff --git a/neural_network/activation_functions/squareplus.py b/neural_network/activation_functions/squareplus.py new file mode 100644 index 000000000000..40fa800d6b4a --- /dev/null +++ b/neural_network/activation_functions/squareplus.py @@ -0,0 +1,38 @@ +""" +Squareplus Activation Function + +Use Case: Squareplus designed to enhance positive values and suppress negative values. +For more detailed information, you can refer to the following link: +https://en.wikipedia.org/wiki/Rectifier_(neural_networks)#Squareplus +""" + +import numpy as np + + +def squareplus(vector: np.ndarray, beta: float) -> np.ndarray: + """ + Implements the SquarePlus activation function. + + Parameters: + vector (np.ndarray): The input array for the SquarePlus activation. + beta (float): size of the curved region + + Returns: + np.ndarray: The input array after applying the SquarePlus activation. + + Formula: f(x) = ( x + sqrt(x^2 + b) ) / 2 + + Examples: + >>> squareplus(np.array([2.3, 0.6, -2, -3.8]), beta=2) + array([2.5 , 1.06811457, 0.22474487, 0.12731349]) + + >>> squareplus(np.array([-9.2, -0.3, 0.45, -4.56]), beta=3) + array([0.0808119 , 0.72891979, 1.11977651, 0.15893419]) + """ + return (vector + np.sqrt(vector**2 + beta)) / 2 + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From e89ae55d8e157cb7c6c3f855188a0fde29083c35 Mon Sep 17 00:00:00 2001 From: Saurabh Mahapatra <98408932+its-100rabh@users.noreply.github.com> Date: Sun, 8 Oct 2023 21:40:41 +0530 Subject: [PATCH 299/757] Create strip.py (#10011) * Create strip.py * Update strip.py --- strings/strip.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 strings/strip.py diff --git a/strings/strip.py b/strings/strip.py new file mode 100644 index 000000000000..d4f901f0c7ea --- /dev/null +++ b/strings/strip.py @@ -0,0 +1,33 @@ +def strip(user_string: str, characters: str = " \t\n\r") -> str: + """ + Remove leading and trailing characters (whitespace by default) from a string. + + Args: + user_string (str): The input string to be stripped. + characters (str, optional): Optional characters to be removed + (default is whitespace). + + Returns: + str: The stripped string. + + Examples: + >>> strip(" hello ") + 'hello' + >>> strip("...world...", ".") + 'world' + >>> strip("123hello123", "123") + 'hello' + >>> strip("") + '' + """ + + start = 0 + end = len(user_string) + + while start < end and user_string[start] in characters: + start += 1 + + while end > start and user_string[end - 1] in characters: + end -= 1 + + return user_string[start:end] From 982bc2735872592d036c20389859071f36b13469 Mon Sep 17 00:00:00 2001 From: Kosuri L Indu <118645569+kosuri-indu@users.noreply.github.com> Date: Sun, 8 Oct 2023 22:37:02 +0530 Subject: [PATCH 300/757] add : Best time to buy and sell stock program under GREEDY methods (#10114) * to add best_time_stock program * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update best_time_to_buy_and_sell_stock.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- .../best_time_to_buy_and_sell_stock.py | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 greedy_methods/best_time_to_buy_and_sell_stock.py diff --git a/greedy_methods/best_time_to_buy_and_sell_stock.py b/greedy_methods/best_time_to_buy_and_sell_stock.py new file mode 100644 index 000000000000..4aea19172ece --- /dev/null +++ b/greedy_methods/best_time_to_buy_and_sell_stock.py @@ -0,0 +1,42 @@ +""" +Given a list of stock prices calculate the maximum profit that can be made from a +single buy and sell of one share of stock. We only allowed to complete one buy +transaction and one sell transaction but must buy before we sell. + +Example : prices = [7, 1, 5, 3, 6, 4] +max_profit will return 5 - which is by buying at price 1 and selling at price 6. + +This problem can be solved using the concept of "GREEDY ALGORITHM". + +We iterate over the price array once, keeping track of the lowest price point +(buy) and the maximum profit we can get at each point. The greedy choice at each point +is to either buy at the current price if it's less than our current buying price, or +sell at the current price if the profit is more than our current maximum profit. +""" + + +def max_profit(prices: list[int]) -> int: + """ + >>> max_profit([7, 1, 5, 3, 6, 4]) + 5 + >>> max_profit([7, 6, 4, 3, 1]) + 0 + """ + if not prices: + return 0 + + min_price = prices[0] + max_profit: int = 0 + + for price in prices: + min_price = min(price, min_price) + max_profit = max(price - min_price, max_profit) + + return max_profit + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + print(max_profit([7, 1, 5, 3, 6, 4])) From e7a59bfff5b182fb01530aa4b1a29b804efb1425 Mon Sep 17 00:00:00 2001 From: SubhranShu2332 <124662904+SubhranShu2332@users.noreply.github.com> Date: Mon, 9 Oct 2023 00:47:02 +0530 Subject: [PATCH 301/757] In place of calculating the factorial several times we can run a loop k times to calculate the combination (#10051) * In place of calculating the factorial several times we can run a loop k times to calculate the combination for example: 5 C 3 = 5! / (3! * (5-3)! ) = (5*4*3*2*1)/[(3*2*1)*(2*1)] =(5*4*3)/(3*2*1) so running a loop k times will reduce the time complexity to O(k) * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update maths/combinations.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Tianyi Zheng --- maths/combinations.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/maths/combinations.py b/maths/combinations.py index a2324012c01f..6e9e1a807067 100644 --- a/maths/combinations.py +++ b/maths/combinations.py @@ -1,7 +1,6 @@ """ https://en.wikipedia.org/wiki/Combination """ -from math import factorial def combinations(n: int, k: int) -> int: @@ -35,7 +34,11 @@ def combinations(n: int, k: int) -> int: # to calculate a factorial of a negative number, which is not possible if n < k or k < 0: raise ValueError("Please enter positive integers for n and k where n >= k") - return factorial(n) // (factorial(k) * factorial(n - k)) + res = 1 + for i in range(k): + res *= n - i + res //= i + 1 + return res if __name__ == "__main__": From c8f6f79f8038ef090a396725c80fa77d9186fb4b Mon Sep 17 00:00:00 2001 From: Siddharth Warrier <117698635+siddwarr@users.noreply.github.com> Date: Mon, 9 Oct 2023 01:10:14 +0530 Subject: [PATCH 302/757] Power of 4 (#9505) * added power_of_4 * updated power_of_4 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * updated power_of_4 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * updated power_of_4 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * updated power_of_4 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * updated power_of_4 * added type check * added tescase --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- bit_manipulation/power_of_4.py | 67 ++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 bit_manipulation/power_of_4.py diff --git a/bit_manipulation/power_of_4.py b/bit_manipulation/power_of_4.py new file mode 100644 index 000000000000..09e6e28621df --- /dev/null +++ b/bit_manipulation/power_of_4.py @@ -0,0 +1,67 @@ +""" + +Task: +Given a positive int number. Return True if this number is power of 4 +or False otherwise. + +Implementation notes: Use bit manipulation. +For example if the number is the power of 2 it's bits representation: +n = 0..100..00 +n - 1 = 0..011..11 + +n & (n - 1) - no intersections = 0 +If the number is a power of 4 then it should be a power of 2 +and the set bit should be at an odd position. +""" + + +def power_of_4(number: int) -> bool: + """ + Return True if this number is power of 4 or False otherwise. + + >>> power_of_4(0) + Traceback (most recent call last): + ... + ValueError: number must be positive + >>> power_of_4(1) + True + >>> power_of_4(2) + False + >>> power_of_4(4) + True + >>> power_of_4(6) + False + >>> power_of_4(8) + False + >>> power_of_4(17) + False + >>> power_of_4(64) + True + >>> power_of_4(-1) + Traceback (most recent call last): + ... + ValueError: number must be positive + >>> power_of_4(1.2) + Traceback (most recent call last): + ... + TypeError: number must be an integer + + """ + if not isinstance(number, int): + raise TypeError("number must be an integer") + if number <= 0: + raise ValueError("number must be positive") + if number & (number - 1) == 0: + c = 0 + while number: + c += 1 + number >>= 1 + return c % 2 == 1 + else: + return False + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 17de908d1ad5a3eb7eb0c850e64394b62e4674c3 Mon Sep 17 00:00:00 2001 From: Achal Jain Date: Mon, 9 Oct 2023 01:11:30 +0530 Subject: [PATCH 303/757] Added Median of Medians Algorithm (#9864) * Added Median of Medians Algorithm * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update median_of_medians.py as per requested changes * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- searches/median_of_medians.py | 107 ++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 searches/median_of_medians.py diff --git a/searches/median_of_medians.py b/searches/median_of_medians.py new file mode 100644 index 000000000000..a8011a34af76 --- /dev/null +++ b/searches/median_of_medians.py @@ -0,0 +1,107 @@ +""" +A Python implementation of the Median of Medians algorithm +to select pivots for quick_select, which is efficient for +calculating the value that would appear in the index of a +list if it would be sorted, even if it is not already +sorted. Search in time complexity O(n) at any rank +deterministically +https://en.wikipedia.org/wiki/Median_of_medians +""" + + +def median_of_five(arr: list) -> int: + """ + Return the median of the input list + :param arr: Array to find median of + :return: median of arr + + >>> median_of_five([2, 4, 5, 7, 899]) + 5 + >>> median_of_five([5, 7, 899, 54, 32]) + 32 + >>> median_of_five([5, 4, 3, 2]) + 4 + >>> median_of_five([3, 5, 7, 10, 2]) + 5 + """ + arr = sorted(arr) + return arr[len(arr) // 2] + + +def median_of_medians(arr: list) -> int: + """ + Return a pivot to partition data on by calculating + Median of medians of input data + :param arr: The data to be checked (a list) + :return: median of medians of input array + + >>> median_of_medians([2, 4, 5, 7, 899, 54, 32]) + 54 + >>> median_of_medians([5, 7, 899, 54, 32]) + 32 + >>> median_of_medians([5, 4, 3, 2]) + 4 + >>> median_of_medians([3, 5, 7, 10, 2, 12]) + 12 + """ + + if len(arr) <= 5: + return median_of_five(arr) + medians = [] + i = 0 + while i < len(arr): + if (i + 4) <= len(arr): + medians.append(median_of_five(arr[i:].copy())) + else: + medians.append(median_of_five(arr[i : i + 5].copy())) + i += 5 + return median_of_medians(medians) + + +def quick_select(arr: list, target: int) -> int: + """ + Two way partition the data into smaller and greater lists, + in relationship to the pivot + :param arr: The data to be searched (a list) + :param target: The rank to be searched + :return: element at rank target + + >>> quick_select([2, 4, 5, 7, 899, 54, 32], 5) + 32 + >>> quick_select([2, 4, 5, 7, 899, 54, 32], 1) + 2 + >>> quick_select([5, 4, 3, 2], 2) + 3 + >>> quick_select([3, 5, 7, 10, 2, 12], 3) + 5 + """ + + # Invalid Input + if target > len(arr): + return -1 + + # x is the estimated pivot by median of medians algorithm + x = median_of_medians(arr) + left = [] + right = [] + check = False + for i in range(len(arr)): + if arr[i] < x: + left.append(arr[i]) + elif arr[i] > x: + right.append(arr[i]) + elif arr[i] == x and not check: + check = True + else: + right.append(arr[i]) + rank_x = len(left) + 1 + if rank_x == target: + answer = x + elif rank_x > target: + answer = quick_select(left, target) + elif rank_x < target: + answer = quick_select(right, target - rank_x) + return answer + + +print(median_of_five([5, 4, 3, 2])) From 8e108ed92ab9a50d5a3e6f647fa33238270e21d1 Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Sun, 8 Oct 2023 15:43:07 -0400 Subject: [PATCH 304/757] Rename maths/binary_exponentiation_3.py (#9656) * updating DIRECTORY.md * Rename binary_exponentiation_3.py Rename binary_exponentiation_3.py to binary_exponentiation_2.py because the original binary_exponentiation_2.py was renamed to binary_multiplication.py in PR #9513 * updating DIRECTORY.md * updating DIRECTORY.md * updating DIRECTORY.md * updating DIRECTORY.md --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 5 +++-- ...binary_exponentiation_3.py => binary_exponentiation_2.py} | 0 2 files changed, 3 insertions(+), 2 deletions(-) rename maths/{binary_exponentiation_3.py => binary_exponentiation_2.py} (100%) diff --git a/DIRECTORY.md b/DIRECTORY.md index a975b9264be0..55b270624438 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -556,7 +556,7 @@ * [Bell Numbers](maths/bell_numbers.py) * [Binary Exp Mod](maths/binary_exp_mod.py) * [Binary Exponentiation](maths/binary_exponentiation.py) - * [Binary Exponentiation 3](maths/binary_exponentiation_3.py) + * [Binary Exponentiation 2](maths/binary_exponentiation_2.py) * [Binary Multiplication](maths/binary_multiplication.py) * [Binomial Coefficient](maths/binomial_coefficient.py) * [Binomial Distribution](maths/binomial_distribution.py) @@ -588,7 +588,6 @@ * [Find Min](maths/find_min.py) * [Floor](maths/floor.py) * [Gamma](maths/gamma.py) - * [Gamma Recursive](maths/gamma_recursive.py) * [Gaussian](maths/gaussian.py) * [Gaussian Error Linear Unit](maths/gaussian_error_linear_unit.py) * [Gcd Of N Numbers](maths/gcd_of_n_numbers.py) @@ -723,6 +722,7 @@ * Activation Functions * [Exponential Linear Unit](neural_network/activation_functions/exponential_linear_unit.py) * [Leaky Rectified Linear Unit](neural_network/activation_functions/leaky_rectified_linear_unit.py) + * [Mish](neural_network/activation_functions/mish.py) * [Rectified Linear Unit](neural_network/activation_functions/rectified_linear_unit.py) * [Scaled Exponential Linear Unit](neural_network/activation_functions/scaled_exponential_linear_unit.py) * [Sigmoid Linear Unit](neural_network/activation_functions/sigmoid_linear_unit.py) @@ -748,6 +748,7 @@ * [Linear Congruential Generator](other/linear_congruential_generator.py) * [Lru Cache](other/lru_cache.py) * [Magicdiamondpattern](other/magicdiamondpattern.py) + * [Majority Vote Algorithm](other/majority_vote_algorithm.py) * [Maximum Subsequence](other/maximum_subsequence.py) * [Nested Brackets](other/nested_brackets.py) * [Number Container System](other/number_container_system.py) diff --git a/maths/binary_exponentiation_3.py b/maths/binary_exponentiation_2.py similarity index 100% rename from maths/binary_exponentiation_3.py rename to maths/binary_exponentiation_2.py From 2d02500332533bb314f91675a3c30ea05bd52b5a Mon Sep 17 00:00:00 2001 From: halfhearted <99018821+Arunsiva003@users.noreply.github.com> Date: Mon, 9 Oct 2023 01:14:49 +0530 Subject: [PATCH 305/757] equilibrium index in an array (#9856) * equilibrium index in an array * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * equilibrium index in an array * equilibrium index in an array * equilibrium index in an array removed type in docstring --------- Co-authored-by: ArunSiva Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .../arrays/equilibrium_index_in_array.py | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 data_structures/arrays/equilibrium_index_in_array.py diff --git a/data_structures/arrays/equilibrium_index_in_array.py b/data_structures/arrays/equilibrium_index_in_array.py new file mode 100644 index 000000000000..4099896d226d --- /dev/null +++ b/data_structures/arrays/equilibrium_index_in_array.py @@ -0,0 +1,59 @@ +""" +Find the Equilibrium Index of an Array. +Reference: https://www.geeksforgeeks.org/equilibrium-index-of-an-array/ + +Python doctests can be run with the following command: +python -m doctest -v equilibrium_index.py + +Given a sequence arr[] of size n, this function returns +an equilibrium index (if any) or -1 if no equilibrium index exists. + +The equilibrium index of an array is an index such that the sum of +elements at lower indexes is equal to the sum of elements at higher indexes. + + + +Example Input: +arr = [-7, 1, 5, 2, -4, 3, 0] +Output: 3 + +""" + + +def equilibrium_index(arr: list[int], size: int) -> int: + """ + Find the equilibrium index of an array. + + Args: + arr : The input array of integers. + size : The size of the array. + + Returns: + int: The equilibrium index or -1 if no equilibrium index exists. + + Examples: + >>> equilibrium_index([-7, 1, 5, 2, -4, 3, 0], 7) + 3 + >>> equilibrium_index([1, 2, 3, 4, 5], 5) + -1 + >>> equilibrium_index([1, 1, 1, 1, 1], 5) + 2 + >>> equilibrium_index([2, 4, 6, 8, 10, 3], 6) + -1 + """ + total_sum = sum(arr) + left_sum = 0 + + for i in range(size): + total_sum -= arr[i] + if left_sum == total_sum: + return i + left_sum += arr[i] + + return -1 + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 66e4ea6a621cccabd6116f1543432899a4411daa Mon Sep 17 00:00:00 2001 From: Anshu Sharma <142900182+AnshuSharma111@users.noreply.github.com> Date: Mon, 9 Oct 2023 01:47:22 +0530 Subject: [PATCH 306/757] Consolidated two scripts reverse_letters.py and reverse_long_words.py into one (#10140) * Conolidated two scripts reverse_letters.py and reverse_long_words.py into one because of similar functionality * Added a new line to accomodate characters without going over 88 char limit * fixed grammar to pass pre-commit * Changed faulty test case entirely to pass pre commit * fixed a test case which was wrong --------- Co-authored-by: Keyboard-1 <142900182+Keyboard-1@users.noreply.github.com> --- DIRECTORY.md | 1 - strings/reverse_letters.py | 27 ++++++++++++++++----------- strings/reverse_long_words.py | 21 --------------------- 3 files changed, 16 insertions(+), 33 deletions(-) delete mode 100644 strings/reverse_long_words.py diff --git a/DIRECTORY.md b/DIRECTORY.md index 55b270624438..b1a23a239b01 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -1197,7 +1197,6 @@ * [Rabin Karp](strings/rabin_karp.py) * [Remove Duplicate](strings/remove_duplicate.py) * [Reverse Letters](strings/reverse_letters.py) - * [Reverse Long Words](strings/reverse_long_words.py) * [Reverse Words](strings/reverse_words.py) * [Snake Case To Camel Pascal Case](strings/snake_case_to_camel_pascal_case.py) * [Split](strings/split.py) diff --git a/strings/reverse_letters.py b/strings/reverse_letters.py index 10b8a6d72a0f..4f73f816b382 100644 --- a/strings/reverse_letters.py +++ b/strings/reverse_letters.py @@ -1,19 +1,24 @@ -def reverse_letters(input_str: str) -> str: +def reverse_letters(sentence: str, length: int = 0) -> str: """ - Reverses letters in a given string without adjusting the position of the words - >>> reverse_letters('The cat in the hat') - 'ehT tac ni eht tah' - >>> reverse_letters('The quick brown fox jumped over the lazy dog.') - 'ehT kciuq nworb xof depmuj revo eht yzal .god' - >>> reverse_letters('Is this true?') - 'sI siht ?eurt' - >>> reverse_letters("I love Python") - 'I evol nohtyP' + Reverse all words that are longer than the given length of characters in a sentence. + If unspecified, length is taken as 0 + + >>> reverse_letters("Hey wollef sroirraw", 3) + 'Hey fellow warriors' + >>> reverse_letters("nohtyP is nohtyP", 2) + 'Python is Python' + >>> reverse_letters("1 12 123 1234 54321 654321", 0) + '1 21 321 4321 12345 123456' + >>> reverse_letters("racecar") + 'racecar' """ - return " ".join([word[::-1] for word in input_str.split()]) + return " ".join( + "".join(word[::-1]) if len(word) > length else word for word in sentence.split() + ) if __name__ == "__main__": import doctest doctest.testmod() + print(reverse_letters("Hey wollef sroirraw")) diff --git a/strings/reverse_long_words.py b/strings/reverse_long_words.py deleted file mode 100644 index 39ef11513f40..000000000000 --- a/strings/reverse_long_words.py +++ /dev/null @@ -1,21 +0,0 @@ -def reverse_long_words(sentence: str) -> str: - """ - Reverse all words that are longer than 4 characters in a sentence. - - >>> reverse_long_words("Hey wollef sroirraw") - 'Hey fellow warriors' - >>> reverse_long_words("nohtyP is nohtyP") - 'Python is Python' - >>> reverse_long_words("1 12 123 1234 54321 654321") - '1 12 123 1234 12345 123456' - """ - return " ".join( - "".join(word[::-1]) if len(word) > 4 else word for word in sentence.split() - ) - - -if __name__ == "__main__": - import doctest - - doctest.testmod() - print(reverse_long_words("Hey wollef sroirraw")) From a2b695dabd6e9c6dd82784bd534c2e7570939be2 Mon Sep 17 00:00:00 2001 From: Megan Payne Date: Sun, 8 Oct 2023 23:33:50 +0200 Subject: [PATCH 307/757] Added Germain primes algorithm to the maths folder (#10120) * Added algorithm for Germain Primes to maths folder * Fixed test errors Germain primes. * Formatting Germain primes after pre-commit * Fixed path to maths * Update maths/germain_primes.py Co-authored-by: Tianyi Zheng * Update maths/germain_primes.py Co-authored-by: Tianyi Zheng * Added function for safe primes * Update maths/germain_primes.py Co-authored-by: Tianyi Zheng * Apply suggestions from code review --------- Co-authored-by: Megan Payne Co-authored-by: Tianyi Zheng --- maths/germain_primes.py | 72 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 maths/germain_primes.py diff --git a/maths/germain_primes.py b/maths/germain_primes.py new file mode 100644 index 000000000000..078d1967f41a --- /dev/null +++ b/maths/germain_primes.py @@ -0,0 +1,72 @@ +""" +A Sophie Germain prime is any prime p, where 2p + 1 is also prime. +The second number, 2p + 1 is called a safe prime. + +Examples of Germain primes include: 2, 3, 5, 11, 23 + +Their corresponding safe primes: 5, 7, 11, 23, 47 +https://en.wikipedia.org/wiki/Safe_and_Sophie_Germain_primes +""" + +from maths.prime_check import is_prime + + +def is_germain_prime(number: int) -> bool: + """Checks if input number and 2*number + 1 are prime. + + >>> is_germain_prime(3) + True + >>> is_germain_prime(11) + True + >>> is_germain_prime(4) + False + >>> is_germain_prime(23) + True + >>> is_germain_prime(13) + False + >>> is_germain_prime(20) + False + >>> is_germain_prime('abc') + Traceback (most recent call last): + ... + TypeError: Input value must be a positive integer. Input value: abc + """ + if not isinstance(number, int) or number < 1: + msg = f"Input value must be a positive integer. Input value: {number}" + raise TypeError(msg) + + return is_prime(number) and is_prime(2 * number + 1) + + +def is_safe_prime(number: int) -> bool: + """Checks if input number and (number - 1)/2 are prime. + The smallest safe prime is 5, with the Germain prime is 2. + + >>> is_safe_prime(5) + True + >>> is_safe_prime(11) + True + >>> is_safe_prime(1) + False + >>> is_safe_prime(2) + False + >>> is_safe_prime(3) + False + >>> is_safe_prime(47) + True + >>> is_safe_prime('abc') + Traceback (most recent call last): + ... + TypeError: Input value must be a positive integer. Input value: abc + """ + if not isinstance(number, int) or number < 1: + msg = f"Input value must be a positive integer. Input value: {number}" + raise TypeError(msg) + + return (number - 1) % 2 == 0 and is_prime(number) and is_prime((number - 1) // 2) + + +if __name__ == "__main__": + from doctest import testmod + + testmod() From 2260961a803ebd037f471ef18fa5032a547d42da Mon Sep 17 00:00:00 2001 From: Saahil Mahato <115351000+saahil-mahato@users.noreply.github.com> Date: Mon, 9 Oct 2023 05:04:28 +0545 Subject: [PATCH 308/757] Add Soboleva Modified Hyberbolic Tangent function (#10043) * Add Sobovela Modified Hyberbolic Tangent function * fix: typo * Update and rename sobovela_modified_hyperbolic_tangent.py to soboleva_modified_hyperbolic_tangent.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix: typo * Apply suggestions from code review --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Tianyi Zheng --- .../soboleva_modified_hyperbolic_tangent.py | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 neural_network/activation_functions/soboleva_modified_hyperbolic_tangent.py diff --git a/neural_network/activation_functions/soboleva_modified_hyperbolic_tangent.py b/neural_network/activation_functions/soboleva_modified_hyperbolic_tangent.py new file mode 100644 index 000000000000..603ac0b7e120 --- /dev/null +++ b/neural_network/activation_functions/soboleva_modified_hyperbolic_tangent.py @@ -0,0 +1,49 @@ +""" +This script implements the Soboleva Modified Hyperbolic Tangent function. + +The function applies the Soboleva Modified Hyperbolic Tangent function +to each element of the vector. + +More details about the activation function can be found on: +https://en.wikipedia.org/wiki/Soboleva_modified_hyperbolic_tangent +""" + + +import numpy as np + + +def soboleva_modified_hyperbolic_tangent( + vector: np.ndarray, a_value: float, b_value: float, c_value: float, d_value: float +) -> np.ndarray: + """ + Implements the Soboleva Modified Hyperbolic Tangent function + + Parameters: + vector (ndarray): A vector that consists of numeric values + a_value (float): parameter a of the equation + b_value (float): parameter b of the equation + c_value (float): parameter c of the equation + d_value (float): parameter d of the equation + + Returns: + vector (ndarray): Input array after applying SMHT function + + >>> vector = np.array([5.4, -2.4, 6.3, -5.23, 3.27, 0.56]) + >>> soboleva_modified_hyperbolic_tangent(vector, 0.2, 0.4, 0.6, 0.8) + array([ 0.11075085, -0.28236685, 0.07861169, -0.1180085 , 0.22999056, + 0.1566043 ]) + """ + + # Separate the numerator and denominator for simplicity + # Calculate the numerator and denominator element-wise + numerator = np.exp(a_value * vector) - np.exp(-b_value * vector) + denominator = np.exp(c_value * vector) + np.exp(-d_value * vector) + + # Calculate and return the final result element-wise + return numerator / denominator + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From ed19b1cf0c3d8284027e17cc025d65b3f924acc0 Mon Sep 17 00:00:00 2001 From: Saahil Mahato <115351000+saahil-mahato@users.noreply.github.com> Date: Mon, 9 Oct 2023 05:19:50 +0545 Subject: [PATCH 309/757] Add binary step activation function (#10030) * Add binary step activation function * fix: ruff line too long error * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * refactor: add link to directory * revert: add link to directory * fix: algorithm bug and docs * Update neural_network/activation_functions/binary_step.py * fix: ruff line too long error --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Tianyi Zheng --- .../activation_functions/binary_step.py | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 neural_network/activation_functions/binary_step.py diff --git a/neural_network/activation_functions/binary_step.py b/neural_network/activation_functions/binary_step.py new file mode 100644 index 000000000000..8f8f4d405fd2 --- /dev/null +++ b/neural_network/activation_functions/binary_step.py @@ -0,0 +1,36 @@ +""" +This script demonstrates the implementation of the Binary Step function. + +It's an activation function in which the neuron is activated if the input is positive +or 0, else it is deactivated + +It's a simple activation function which is mentioned in this wikipedia article: +https://en.wikipedia.org/wiki/Activation_function +""" + + +import numpy as np + + +def binary_step(vector: np.ndarray) -> np.ndarray: + """ + Implements the binary step function + + Parameters: + vector (ndarray): A vector that consists of numeric values + + Returns: + vector (ndarray): Input vector after applying binary step function + + >>> vector = np.array([-1.2, 0, 2, 1.45, -3.7, 0.3]) + >>> binary_step(vector) + array([0, 1, 1, 1, 0, 1]) + """ + + return np.where(vector >= 0, 1, 0) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 12e8e9ca876ed3ae7f1effa1de407ca29a06cb36 Mon Sep 17 00:00:00 2001 From: Sai Harsha Kottapalli Date: Mon, 9 Oct 2023 17:36:16 +0530 Subject: [PATCH 310/757] Add DocTests to is_palindrome.py (#10081) * add doctest ut * test complete * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * format * ruff update * cover line 154 * Update data_structures/linked_list/is_palindrome.py Co-authored-by: Christian Clauss * use dataclass * pre-commit fix * Fix mypy errors * use future annotations --------- Co-authored-by: Harsha Kottapalli Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- data_structures/linked_list/is_palindrome.py | 176 +++++++++++++++---- 1 file changed, 142 insertions(+), 34 deletions(-) diff --git a/data_structures/linked_list/is_palindrome.py b/data_structures/linked_list/is_palindrome.py index d540fb69f36b..7d89f085c67f 100644 --- a/data_structures/linked_list/is_palindrome.py +++ b/data_structures/linked_list/is_palindrome.py @@ -1,65 +1,167 @@ -def is_palindrome(head): +from __future__ import annotations + +from dataclasses import dataclass + + +@dataclass +class ListNode: + val: int = 0 + next_node: ListNode | None = None + + +def is_palindrome(head: ListNode | None) -> bool: + """ + Check if a linked list is a palindrome. + + Args: + head: The head of the linked list. + + Returns: + bool: True if the linked list is a palindrome, False otherwise. + + Examples: + >>> is_palindrome(None) + True + + >>> is_palindrome(ListNode(1)) + True + + >>> is_palindrome(ListNode(1, ListNode(2))) + False + + >>> is_palindrome(ListNode(1, ListNode(2, ListNode(1)))) + True + + >>> is_palindrome(ListNode(1, ListNode(2, ListNode(2, ListNode(1))))) + True + """ if not head: return True # split the list to two parts - fast, slow = head.next, head - while fast and fast.next: - fast = fast.next.next - slow = slow.next - second = slow.next - slow.next = None # Don't forget here! But forget still works! + fast: ListNode | None = head.next_node + slow: ListNode | None = head + while fast and fast.next_node: + fast = fast.next_node.next_node + slow = slow.next_node if slow else None + if slow: + # slow will always be defined, + # adding this check to resolve mypy static check + second = slow.next_node + slow.next_node = None # Don't forget here! But forget still works! # reverse the second part - node = None + node: ListNode | None = None while second: - nxt = second.next - second.next = node + nxt = second.next_node + second.next_node = node node = second second = nxt # compare two parts # second part has the same or one less node - while node: + while node and head: if node.val != head.val: return False - node = node.next - head = head.next + node = node.next_node + head = head.next_node return True -def is_palindrome_stack(head): - if not head or not head.next: +def is_palindrome_stack(head: ListNode | None) -> bool: + """ + Check if a linked list is a palindrome using a stack. + + Args: + head (ListNode): The head of the linked list. + + Returns: + bool: True if the linked list is a palindrome, False otherwise. + + Examples: + >>> is_palindrome_stack(None) + True + + >>> is_palindrome_stack(ListNode(1)) + True + + >>> is_palindrome_stack(ListNode(1, ListNode(2))) + False + + >>> is_palindrome_stack(ListNode(1, ListNode(2, ListNode(1)))) + True + + >>> is_palindrome_stack(ListNode(1, ListNode(2, ListNode(2, ListNode(1))))) + True + """ + if not head or not head.next_node: return True # 1. Get the midpoint (slow) - slow = fast = cur = head - while fast and fast.next: - fast, slow = fast.next.next, slow.next - - # 2. Push the second half into the stack - stack = [slow.val] - while slow.next: - slow = slow.next - stack.append(slow.val) - - # 3. Comparison - while stack: - if stack.pop() != cur.val: - return False - cur = cur.next + slow: ListNode | None = head + fast: ListNode | None = head + while fast and fast.next_node: + fast = fast.next_node.next_node + slow = slow.next_node if slow else None + + # slow will always be defined, + # adding this check to resolve mypy static check + if slow: + stack = [slow.val] + + # 2. Push the second half into the stack + while slow.next_node: + slow = slow.next_node + stack.append(slow.val) + + # 3. Comparison + cur: ListNode | None = head + while stack and cur: + if stack.pop() != cur.val: + return False + cur = cur.next_node return True -def is_palindrome_dict(head): - if not head or not head.next: +def is_palindrome_dict(head: ListNode | None) -> bool: + """ + Check if a linked list is a palindrome using a dictionary. + + Args: + head (ListNode): The head of the linked list. + + Returns: + bool: True if the linked list is a palindrome, False otherwise. + + Examples: + >>> is_palindrome_dict(None) + True + + >>> is_palindrome_dict(ListNode(1)) + True + + >>> is_palindrome_dict(ListNode(1, ListNode(2))) + False + + >>> is_palindrome_dict(ListNode(1, ListNode(2, ListNode(1)))) + True + + >>> is_palindrome_dict(ListNode(1, ListNode(2, ListNode(2, ListNode(1))))) + True + + >>> is_palindrome_dict(\ + ListNode(\ + 1, ListNode(2, ListNode(1, ListNode(3, ListNode(2, ListNode(1))))))) + False + """ + if not head or not head.next_node: return True - d = {} + d: dict[int, list[int]] = {} pos = 0 while head: if head.val in d: d[head.val].append(pos) else: d[head.val] = [pos] - head = head.next + head = head.next_node pos += 1 checksum = pos - 1 middle = 0 @@ -75,3 +177,9 @@ def is_palindrome_dict(head): if middle > 1: return False return True + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 876087be998d5b366d68cbb9394b6b92b7f619f6 Mon Sep 17 00:00:00 2001 From: Sai Harsha Kottapalli Date: Mon, 9 Oct 2023 17:46:43 +0530 Subject: [PATCH 311/757] Add DocTests to magicdiamondpattern.py (#10135) * magicdiamondpattern doctest * remove start part --------- Co-authored-by: Harsha Kottapalli --- other/magicdiamondpattern.py | 76 ++++++++++++++++++++++++------------ 1 file changed, 50 insertions(+), 26 deletions(-) diff --git a/other/magicdiamondpattern.py b/other/magicdiamondpattern.py index 89b973bb41e8..58889280ab17 100644 --- a/other/magicdiamondpattern.py +++ b/other/magicdiamondpattern.py @@ -4,52 +4,76 @@ # Function to print upper half of diamond (pyramid) def floyd(n): """ - Parameters: - n : size of pattern + Print the upper half of a diamond pattern with '*' characters. + + Args: + n (int): Size of the pattern. + + Examples: + >>> floyd(3) + ' * \\n * * \\n* * * \\n' + + >>> floyd(5) + ' * \\n * * \\n * * * \\n * * * * \\n* * * * * \\n' """ + result = "" for i in range(n): for _ in range(n - i - 1): # printing spaces - print(" ", end="") + result += " " for _ in range(i + 1): # printing stars - print("* ", end="") - print() + result += "* " + result += "\n" + return result # Function to print lower half of diamond (pyramid) def reverse_floyd(n): """ - Parameters: - n : size of pattern + Print the lower half of a diamond pattern with '*' characters. + + Args: + n (int): Size of the pattern. + + Examples: + >>> reverse_floyd(3) + '* * * \\n * * \\n * \\n ' + + >>> reverse_floyd(5) + '* * * * * \\n * * * * \\n * * * \\n * * \\n * \\n ' """ + result = "" for i in range(n, 0, -1): for _ in range(i, 0, -1): # printing stars - print("* ", end="") - print() + result += "* " + result += "\n" for _ in range(n - i + 1, 0, -1): # printing spaces - print(" ", end="") + result += " " + return result # Function to print complete diamond pattern of "*" def pretty_print(n): """ - Parameters: - n : size of pattern + Print a complete diamond pattern with '*' characters. + + Args: + n (int): Size of the pattern. + + Examples: + >>> pretty_print(0) + ' ... .... nothing printing :(' + + >>> pretty_print(3) + ' * \\n * * \\n* * * \\n* * * \\n * * \\n * \\n ' """ if n <= 0: - print(" ... .... nothing printing :(") - return - floyd(n) # upper half - reverse_floyd(n) # lower half + return " ... .... nothing printing :(" + upper_half = floyd(n) # upper half + lower_half = reverse_floyd(n) # lower half + return upper_half + lower_half if __name__ == "__main__": - print(r"| /\ | |- | |- |--| |\ /| |-") - print(r"|/ \| |- |_ |_ |__| | \/ | |_") - K = 1 - while K: - user_number = int(input("enter the number and , and see the magic : ")) - print() - pretty_print(user_number) - K = int(input("press 0 to exit... and 1 to continue...")) - - print("Good Bye...") + import doctest + + doctest.testmod() From 583a614fefaa9c932e6d650abfea2eaa75a93b05 Mon Sep 17 00:00:00 2001 From: Siddik Patel <70135775+Siddikpatel@users.noreply.github.com> Date: Mon, 9 Oct 2023 17:49:12 +0530 Subject: [PATCH 312/757] Removed redundant greatest_common_divisor code (#9358) * Deleted greatest_common_divisor def from many files and instead imported the method from Maths folder * Deleted greatest_common_divisor def from many files and instead imported the method from Maths folder, also fixed comments * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Deleted greatest_common_divisor def from many files and instead imported the method from Maths folder, also fixed comments * Imports organized * recursive gcd function implementation rolledback * more gcd duplicates removed * more gcd duplicates removed * Update maths/carmichael_number.py * updated files * moved a file to another location --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Tianyi Zheng --- blockchain/diophantine_equation.py | 32 +++--------------------- ciphers/affine_cipher.py | 6 +++-- ciphers/cryptomath_module.py | 7 ++---- ciphers/hill_cipher.py | 14 +---------- ciphers/rsa_key_generator.py | 4 ++- maths/carmichael_number.py | 11 ++------ maths/least_common_multiple.py | 22 ++-------------- maths/primelib.py | 40 +++--------------------------- project_euler/problem_005/sol2.py | 19 ++------------ 9 files changed, 24 insertions(+), 131 deletions(-) diff --git a/blockchain/diophantine_equation.py b/blockchain/diophantine_equation.py index 22b0cad75c63..7110d90230c9 100644 --- a/blockchain/diophantine_equation.py +++ b/blockchain/diophantine_equation.py @@ -1,11 +1,13 @@ from __future__ import annotations +from maths.greatest_common_divisor import greatest_common_divisor + def diophantine(a: int, b: int, c: int) -> tuple[float, float]: """ Diophantine Equation : Given integers a,b,c ( at least one of a and b != 0), the diophantine equation a*x + b*y = c has a solution (where x and y are integers) - iff gcd(a,b) divides c. + iff greatest_common_divisor(a,b) divides c. GCD ( Greatest Common Divisor ) or HCF ( Highest Common Factor ) @@ -22,7 +24,7 @@ def diophantine(a: int, b: int, c: int) -> tuple[float, float]: assert ( c % greatest_common_divisor(a, b) == 0 - ) # greatest_common_divisor(a,b) function implemented below + ) # greatest_common_divisor(a,b) is in maths directory (d, x, y) = extended_gcd(a, b) # extended_gcd(a,b) function implemented below r = c / d return (r * x, r * y) @@ -69,32 +71,6 @@ def diophantine_all_soln(a: int, b: int, c: int, n: int = 2) -> None: print(x, y) -def greatest_common_divisor(a: int, b: int) -> int: - """ - Euclid's Lemma : d divides a and b, if and only if d divides a-b and b - - Euclid's Algorithm - - >>> greatest_common_divisor(7,5) - 1 - - Note : In number theory, two integers a and b are said to be relatively prime, - mutually prime, or co-prime if the only positive integer (factor) that - divides both of them is 1 i.e., gcd(a,b) = 1. - - >>> greatest_common_divisor(121, 11) - 11 - - """ - if a < b: - a, b = b, a - - while a % b != 0: - a, b = b, a % b - - return b - - def extended_gcd(a: int, b: int) -> tuple[int, int, int]: """ Extended Euclid's Algorithm : If d divides a and b and d = a*x + b*y for integers diff --git a/ciphers/affine_cipher.py b/ciphers/affine_cipher.py index cd1e33b88425..10d16367cced 100644 --- a/ciphers/affine_cipher.py +++ b/ciphers/affine_cipher.py @@ -1,6 +1,8 @@ import random import sys +from maths.greatest_common_divisor import gcd_by_iterative + from . import cryptomath_module as cryptomath SYMBOLS = ( @@ -26,7 +28,7 @@ def check_keys(key_a: int, key_b: int, mode: str) -> None: "Key A must be greater than 0 and key B must " f"be between 0 and {len(SYMBOLS) - 1}." ) - if cryptomath.gcd(key_a, len(SYMBOLS)) != 1: + if gcd_by_iterative(key_a, len(SYMBOLS)) != 1: sys.exit( f"Key A {key_a} and the symbol set size {len(SYMBOLS)} " "are not relatively prime. Choose a different key." @@ -76,7 +78,7 @@ def get_random_key() -> int: while True: key_b = random.randint(2, len(SYMBOLS)) key_b = random.randint(2, len(SYMBOLS)) - if cryptomath.gcd(key_b, len(SYMBOLS)) == 1 and key_b % len(SYMBOLS) != 0: + if gcd_by_iterative(key_b, len(SYMBOLS)) == 1 and key_b % len(SYMBOLS) != 0: return key_b * len(SYMBOLS) + key_b diff --git a/ciphers/cryptomath_module.py b/ciphers/cryptomath_module.py index 6f15f7b733e6..02e94e4b9e92 100644 --- a/ciphers/cryptomath_module.py +++ b/ciphers/cryptomath_module.py @@ -1,11 +1,8 @@ -def gcd(a: int, b: int) -> int: - while a != 0: - a, b = b % a, a - return b +from maths.greatest_common_divisor import gcd_by_iterative def find_mod_inverse(a: int, m: int) -> int: - if gcd(a, m) != 1: + if gcd_by_iterative(a, m) != 1: msg = f"mod inverse of {a!r} and {m!r} does not exist" raise ValueError(msg) u1, u2, u3 = 1, 0, a diff --git a/ciphers/hill_cipher.py b/ciphers/hill_cipher.py index b4424e82298e..1201fda901e5 100644 --- a/ciphers/hill_cipher.py +++ b/ciphers/hill_cipher.py @@ -39,19 +39,7 @@ import numpy - -def greatest_common_divisor(a: int, b: int) -> int: - """ - >>> greatest_common_divisor(4, 8) - 4 - >>> greatest_common_divisor(8, 4) - 4 - >>> greatest_common_divisor(4, 7) - 1 - >>> greatest_common_divisor(0, 10) - 10 - """ - return b if a == 0 else greatest_common_divisor(b % a, a) +from maths.greatest_common_divisor import greatest_common_divisor class HillCipher: diff --git a/ciphers/rsa_key_generator.py b/ciphers/rsa_key_generator.py index eedc7336804a..44970e8cbc15 100644 --- a/ciphers/rsa_key_generator.py +++ b/ciphers/rsa_key_generator.py @@ -2,6 +2,8 @@ import random import sys +from maths.greatest_common_divisor import gcd_by_iterative + from . import cryptomath_module, rabin_miller @@ -27,7 +29,7 @@ def generate_key(key_size: int) -> tuple[tuple[int, int], tuple[int, int]]: # Generate e that is relatively prime to (p - 1) * (q - 1) while True: e = random.randrange(2 ** (key_size - 1), 2 ** (key_size)) - if cryptomath_module.gcd(e, (p - 1) * (q - 1)) == 1: + if gcd_by_iterative(e, (p - 1) * (q - 1)) == 1: break # Calculate d that is mod inverse of e diff --git a/maths/carmichael_number.py b/maths/carmichael_number.py index c9c144759246..81712520ffc7 100644 --- a/maths/carmichael_number.py +++ b/maths/carmichael_number.py @@ -10,14 +10,7 @@ Examples of Carmichael Numbers: 561, 1105, ... https://en.wikipedia.org/wiki/Carmichael_number """ - - -def gcd(a: int, b: int) -> int: - if a < b: - return gcd(b, a) - if a % b == 0: - return b - return gcd(b, a % b) +from maths.greatest_common_divisor import greatest_common_divisor def power(x: int, y: int, mod: int) -> int: @@ -33,7 +26,7 @@ def power(x: int, y: int, mod: int) -> int: def is_carmichael_number(n: int) -> bool: b = 2 while b < n: - if gcd(b, n) == 1 and power(b, n - 1, n) != 1: + if greatest_common_divisor(b, n) == 1 and power(b, n - 1, n) != 1: return False b += 1 return True diff --git a/maths/least_common_multiple.py b/maths/least_common_multiple.py index 10cc63ac7990..4f28da8ab2a7 100644 --- a/maths/least_common_multiple.py +++ b/maths/least_common_multiple.py @@ -1,6 +1,8 @@ import unittest from timeit import timeit +from maths.greatest_common_divisor import greatest_common_divisor + def least_common_multiple_slow(first_num: int, second_num: int) -> int: """ @@ -20,26 +22,6 @@ def least_common_multiple_slow(first_num: int, second_num: int) -> int: return common_mult -def greatest_common_divisor(a: int, b: int) -> int: - """ - Calculate Greatest Common Divisor (GCD). - see greatest_common_divisor.py - >>> greatest_common_divisor(24, 40) - 8 - >>> greatest_common_divisor(1, 1) - 1 - >>> greatest_common_divisor(1, 800) - 1 - >>> greatest_common_divisor(11, 37) - 1 - >>> greatest_common_divisor(3, 5) - 1 - >>> greatest_common_divisor(16, 4) - 4 - """ - return b if a == 0 else greatest_common_divisor(b % a, a) - - def least_common_multiple_fast(first_num: int, second_num: int) -> int: """ Find the least common multiple of two numbers. diff --git a/maths/primelib.py b/maths/primelib.py index 28b5aee9dcc8..cf01750cf912 100644 --- a/maths/primelib.py +++ b/maths/primelib.py @@ -21,7 +21,6 @@ is_even(number) is_odd(number) -gcd(number1, number2) // greatest common divisor kg_v(number1, number2) // least common multiple get_divisors(number) // all divisors of 'number' inclusive 1, number is_perfect_number(number) @@ -40,6 +39,8 @@ from math import sqrt +from maths.greatest_common_divisor import gcd_by_iterative + def is_prime(number: int) -> bool: """ @@ -317,39 +318,6 @@ def goldbach(number): # ---------------------------------------------- -def gcd(number1, number2): - """ - Greatest common divisor - input: two positive integer 'number1' and 'number2' - returns the greatest common divisor of 'number1' and 'number2' - """ - - # precondition - assert ( - isinstance(number1, int) - and isinstance(number2, int) - and (number1 >= 0) - and (number2 >= 0) - ), "'number1' and 'number2' must been positive integer." - - rest = 0 - - while number2 != 0: - rest = number1 % number2 - number1 = number2 - number2 = rest - - # precondition - assert isinstance(number1, int) and ( - number1 >= 0 - ), "'number' must been from type int and positive" - - return number1 - - -# ---------------------------------------------------- - - def kg_v(number1, number2): """ Least common multiple @@ -567,14 +535,14 @@ def simplify_fraction(numerator, denominator): ), "The arguments must been from type int and 'denominator' != 0" # build the greatest common divisor of numerator and denominator. - gcd_of_fraction = gcd(abs(numerator), abs(denominator)) + gcd_of_fraction = gcd_by_iterative(abs(numerator), abs(denominator)) # precondition assert ( isinstance(gcd_of_fraction, int) and (numerator % gcd_of_fraction == 0) and (denominator % gcd_of_fraction == 0) - ), "Error in function gcd(...,...)" + ), "Error in function gcd_by_iterative(...,...)" return (numerator // gcd_of_fraction, denominator // gcd_of_fraction) diff --git a/project_euler/problem_005/sol2.py b/project_euler/problem_005/sol2.py index 1b3e5e130f03..4558e21fd0f0 100644 --- a/project_euler/problem_005/sol2.py +++ b/project_euler/problem_005/sol2.py @@ -1,3 +1,5 @@ +from maths.greatest_common_divisor import greatest_common_divisor + """ Project Euler Problem 5: https://projecteuler.net/problem=5 @@ -16,23 +18,6 @@ """ -def greatest_common_divisor(x: int, y: int) -> int: - """ - Euclidean Greatest Common Divisor algorithm - - >>> greatest_common_divisor(0, 0) - 0 - >>> greatest_common_divisor(23, 42) - 1 - >>> greatest_common_divisor(15, 33) - 3 - >>> greatest_common_divisor(12345, 67890) - 15 - """ - - return x if y == 0 else greatest_common_divisor(y, x % y) - - def lcm(x: int, y: int) -> int: """ Least Common Multiple. From 53d78b9cc09021c8f65fae41f8b345304a88aedd Mon Sep 17 00:00:00 2001 From: Kausthub Kannan Date: Mon, 9 Oct 2023 20:03:47 +0530 Subject: [PATCH 313/757] Added Huber Loss Function (#10141) --- machine_learning/loss_functions/huber_loss.py | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 machine_learning/loss_functions/huber_loss.py diff --git a/machine_learning/loss_functions/huber_loss.py b/machine_learning/loss_functions/huber_loss.py new file mode 100644 index 000000000000..202e013f2928 --- /dev/null +++ b/machine_learning/loss_functions/huber_loss.py @@ -0,0 +1,52 @@ +""" +Huber Loss Function + +Description: +Huber loss function describes the penalty incurred by an estimation procedure. +It serves as a measure of the model's accuracy in regression tasks. + +Formula: +Huber Loss = if |y_true - y_pred| <= delta then 0.5 * (y_true - y_pred)^2 + else delta * |y_true - y_pred| - 0.5 * delta^2 + +Source: +[Wikipedia - Huber Loss](https://en.wikipedia.org/wiki/Huber_loss) +""" + +import numpy as np + + +def huber_loss(y_true: np.ndarray, y_pred: np.ndarray, delta: float) -> float: + """ + Calculate the mean of Huber Loss. + + Parameters: + - y_true: The true values (ground truth). + - y_pred: The predicted values. + + Returns: + - huber_loss: The mean of Huber Loss between y_true and y_pred. + + Example usage: + >>> true_values = np.array([0.9, 10.0, 2.0, 1.0, 5.2]) + >>> predicted_values = np.array([0.8, 2.1, 2.9, 4.2, 5.2]) + >>> np.isclose(huber_loss(true_values, predicted_values, 1.0), 2.102) + True + >>> true_labels = np.array([11.0, 21.0, 3.32, 4.0, 5.0]) + >>> predicted_probs = np.array([8.3, 20.8, 2.9, 11.2, 5.0]) + >>> np.isclose(huber_loss(true_labels, predicted_probs, 1.0), 1.80164) + True + """ + + if len(y_true) != len(y_pred): + raise ValueError("Input arrays must have the same length.") + + huber_mse = 0.5 * (y_true - y_pred) ** 2 + huber_mae = delta * (np.abs(y_true - y_pred) - 0.5 * delta) + return np.where(np.abs(y_true - y_pred) <= delta, huber_mse, huber_mae).mean() + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From c0da015b7d49f9f6e99fffd279f65c5605a0ebe1 Mon Sep 17 00:00:00 2001 From: Sai Harsha Kottapalli Date: Mon, 9 Oct 2023 20:49:05 +0530 Subject: [PATCH 314/757] Add DocTests to diffie.py (#10156) * diffie doctest * fix ut * update doctest --------- Co-authored-by: Harsha Kottapalli --- ciphers/diffie.py | 45 +++++++++++++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/ciphers/diffie.py b/ciphers/diffie.py index 4ff90be009c1..1e1e868999b6 100644 --- a/ciphers/diffie.py +++ b/ciphers/diffie.py @@ -1,11 +1,28 @@ from __future__ import annotations -def find_primitive(n: int) -> int | None: - for r in range(1, n): +def find_primitive(modulus: int) -> int | None: + """ + Find a primitive root modulo modulus, if one exists. + + Args: + modulus : The modulus for which to find a primitive root. + + Returns: + The primitive root if one exists, or None if there is none. + + Examples: + >>> find_primitive(7) # Modulo 7 has primitive root 3 + 3 + >>> find_primitive(11) # Modulo 11 has primitive root 2 + 2 + >>> find_primitive(8) == None # Modulo 8 has no primitive root + True + """ + for r in range(1, modulus): li = [] - for x in range(n - 1): - val = pow(r, x, n) + for x in range(modulus - 1): + val = pow(r, x, modulus) if val in li: break li.append(val) @@ -15,18 +32,22 @@ def find_primitive(n: int) -> int | None: if __name__ == "__main__": - q = int(input("Enter a prime number q: ")) - a = find_primitive(q) - if a is None: - print(f"Cannot find the primitive for the value: {a!r}") + import doctest + + doctest.testmod() + + prime = int(input("Enter a prime number q: ")) + primitive_root = find_primitive(prime) + if primitive_root is None: + print(f"Cannot find the primitive for the value: {primitive_root!r}") else: a_private = int(input("Enter private key of A: ")) - a_public = pow(a, a_private, q) + a_public = pow(primitive_root, a_private, prime) b_private = int(input("Enter private key of B: ")) - b_public = pow(a, b_private, q) + b_public = pow(primitive_root, b_private, prime) - a_secret = pow(b_public, a_private, q) - b_secret = pow(a_public, b_private, q) + a_secret = pow(b_public, a_private, prime) + b_secret = pow(a_public, b_private, prime) print("The key value generated by A is: ", a_secret) print("The key value generated by B is: ", b_secret) From ba828fe621d1f5623fffcf0014b243da3a6122fc Mon Sep 17 00:00:00 2001 From: Kamil <32775019+quant12345@users.noreply.github.com> Date: Mon, 9 Oct 2023 20:46:38 +0500 Subject: [PATCH 315/757] test_digital_image_processing -> test_local_binary_pattern replacing a large image with a smaller one (#10161) * Replacing the generator with numpy vector operations from lu_decomposition. * Revert "Replacing the generator with numpy vector operations from lu_decomposition." This reverts commit ad217c66165898d62b76cc89ba09c2d7049b6448. * Replaced lena.jpg with lena_small.jpg to make tests faster. * Update digital_image_processing/test_digital_image_processing.py Co-authored-by: Christian Clauss * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update test_digital_image_processing.py tests fail, I'll try an empty commit * Apply suggestions from code review * Update test_digital_image_processing.py added clarifications * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update test_digital_image_processing.py --------- Co-authored-by: Christian Clauss Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Tianyi Zheng --- .../test_digital_image_processing.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/digital_image_processing/test_digital_image_processing.py b/digital_image_processing/test_digital_image_processing.py index 528b4bc3b74c..2e5630458c8e 100644 --- a/digital_image_processing/test_digital_image_processing.py +++ b/digital_image_processing/test_digital_image_processing.py @@ -96,9 +96,16 @@ def test_nearest_neighbour( def test_local_binary_pattern(): - file_path = "digital_image_processing/image_data/lena.jpg" + # pull request 10161 before: + # "digital_image_processing/image_data/lena.jpg" + # after: "digital_image_processing/image_data/lena_small.jpg" - # Reading the image and converting it to grayscale. + from os import getenv # Speed up our Continuous Integration tests + + file_name = "lena_small.jpg" if getenv("CI") else "lena.jpg" + file_path = f"digital_image_processing/image_data/{file_name}" + + # Reading the image and converting it to grayscale image = imread(file_path, 0) # Test for get_neighbors_pixel function() return not None From 844270c6e91387940e062a1522f58bde1026bb08 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Mon, 9 Oct 2023 19:42:07 +0200 Subject: [PATCH 316/757] Remove backslashes from is_palindrome.py (#10169) @SaiHarshaK Fixes https://github.com/TheAlgorithms/Python/pull/10081#discussion_r1349651289 --- data_structures/linked_list/is_palindrome.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/data_structures/linked_list/is_palindrome.py b/data_structures/linked_list/is_palindrome.py index 7d89f085c67f..f949d9a2f201 100644 --- a/data_structures/linked_list/is_palindrome.py +++ b/data_structures/linked_list/is_palindrome.py @@ -147,9 +147,11 @@ def is_palindrome_dict(head: ListNode | None) -> bool: >>> is_palindrome_dict(ListNode(1, ListNode(2, ListNode(2, ListNode(1))))) True - >>> is_palindrome_dict(\ - ListNode(\ - 1, ListNode(2, ListNode(1, ListNode(3, ListNode(2, ListNode(1))))))) + >>> is_palindrome_dict( + ... ListNode( + ... 1, ListNode(2, ListNode(1, ListNode(3, ListNode(2, ListNode(1))))) + ... ) + ... ) False """ if not head or not head.next_node: From 5d0a46814e5b69f79d623187912c0f81ab5ab7a7 Mon Sep 17 00:00:00 2001 From: aryan1165 <111041731+aryan1165@users.noreply.github.com> Date: Tue, 10 Oct 2023 01:08:04 +0530 Subject: [PATCH 317/757] Added ciphers/permutation_cipher.py. (#9163) * Added permutation_cipher.py * Added type hints for parameters * Added doctest in functions * Update ciphers/permutation_cipher.py Ya i felt the same but held back because there is a implementation of transposition_cipher.py. But that's is different from the one i have implemented here. Co-authored-by: Tianyi Zheng * Update ciphers/permutation_cipher.py Co-authored-by: Tianyi Zheng * Update ciphers/permutation_cipher.py Co-authored-by: Tianyi Zheng * Update ciphers/permutation_cipher.py Co-authored-by: Tianyi Zheng * Update ciphers/permutation_cipher.py Co-authored-by: Tianyi Zheng * Update ciphers/permutation_cipher.py Co-authored-by: Tianyi Zheng * Update ciphers/permutation_cipher.py Co-authored-by: Tianyi Zheng * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Changes --------- Co-authored-by: Tianyi Zheng Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- ciphers/permutation_cipher.py | 142 ++++++++++++++++++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 ciphers/permutation_cipher.py diff --git a/ciphers/permutation_cipher.py b/ciphers/permutation_cipher.py new file mode 100644 index 000000000000..c3f3fd1f7f94 --- /dev/null +++ b/ciphers/permutation_cipher.py @@ -0,0 +1,142 @@ +""" +The permutation cipher, also called the transposition cipher, is a simple encryption +technique that rearranges the characters in a message based on a secret key. It +divides the message into blocks and applies a permutation to the characters within +each block according to the key. The key is a sequence of unique integers that +determine the order of character rearrangement. + +For more info: https://www.nku.edu/~christensen/1402%20permutation%20ciphers.pdf +""" +import random + + +def generate_valid_block_size(message_length: int) -> int: + """ + Generate a valid block size that is a factor of the message length. + + Args: + message_length (int): The length of the message. + + Returns: + int: A valid block size. + + Example: + >>> random.seed(1) + >>> generate_valid_block_size(12) + 3 + """ + block_sizes = [ + block_size + for block_size in range(2, message_length + 1) + if message_length % block_size == 0 + ] + return random.choice(block_sizes) + + +def generate_permutation_key(block_size: int) -> list[int]: + """ + Generate a random permutation key of a specified block size. + + Args: + block_size (int): The size of each permutation block. + + Returns: + list[int]: A list containing a random permutation of digits. + + Example: + >>> random.seed(0) + >>> generate_permutation_key(4) + [2, 0, 1, 3] + """ + digits = list(range(block_size)) + random.shuffle(digits) + return digits + + +def encrypt( + message: str, key: list[int] | None = None, block_size: int | None = None +) -> tuple[str, list[int]]: + """ + Encrypt a message using a permutation cipher with block rearrangement using a key. + + Args: + message (str): The plaintext message to be encrypted. + key (list[int]): The permutation key for decryption. + block_size (int): The size of each permutation block. + + Returns: + tuple: A tuple containing the encrypted message and the encryption key. + + Example: + >>> encrypted_message, key = encrypt("HELLO WORLD") + >>> decrypted_message = decrypt(encrypted_message, key) + >>> decrypted_message + 'HELLO WORLD' + """ + message = message.upper() + message_length = len(message) + + if key is None or block_size is None: + block_size = generate_valid_block_size(message_length) + key = generate_permutation_key(block_size) + + encrypted_message = "" + + for i in range(0, message_length, block_size): + block = message[i : i + block_size] + rearranged_block = [block[digit] for digit in key] + encrypted_message += "".join(rearranged_block) + + return encrypted_message, key + + +def decrypt(encrypted_message: str, key: list[int]) -> str: + """ + Decrypt an encrypted message using a permutation cipher with block rearrangement. + + Args: + encrypted_message (str): The encrypted message. + key (list[int]): The permutation key for decryption. + + Returns: + str: The decrypted plaintext message. + + Example: + >>> encrypted_message, key = encrypt("HELLO WORLD") + >>> decrypted_message = decrypt(encrypted_message, key) + >>> decrypted_message + 'HELLO WORLD' + """ + key_length = len(key) + decrypted_message = "" + + for i in range(0, len(encrypted_message), key_length): + block = encrypted_message[i : i + key_length] + original_block = [""] * key_length + for j, digit in enumerate(key): + original_block[digit] = block[j] + decrypted_message += "".join(original_block) + + return decrypted_message + + +def main() -> None: + """ + Driver function to pass message to get encrypted, then decrypted. + + Example: + >>> main() + Decrypted message: HELLO WORLD + """ + message = "HELLO WORLD" + encrypted_message, key = encrypt(message) + + decrypted_message = decrypt(encrypted_message, key) + print(f"Decrypted message: {decrypted_message}") + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + main() From b0aa35c7b360f1d141705b97c89d51603a3461a6 Mon Sep 17 00:00:00 2001 From: Dean Bring Date: Mon, 9 Oct 2023 14:21:46 -0700 Subject: [PATCH 318/757] Added the Chebyshev distance function (#10144) * Added the Chebyshev distance function * Remove float cast and made error handling more precise --- maths/chebyshev_distance.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 maths/chebyshev_distance.py diff --git a/maths/chebyshev_distance.py b/maths/chebyshev_distance.py new file mode 100644 index 000000000000..4801d391621f --- /dev/null +++ b/maths/chebyshev_distance.py @@ -0,0 +1,20 @@ +def chebyshev_distance(point_a: list[float], point_b: list[float]) -> float: + """ + This function calculates the Chebyshev distance (also known as the + Chessboard distance) between two n-dimensional points represented as lists. + + https://en.wikipedia.org/wiki/Chebyshev_distance + + >>> chebyshev_distance([1.0, 1.0], [2.0, 2.0]) + 1.0 + >>> chebyshev_distance([1.0, 1.0, 9.0], [2.0, 2.0, -5.2]) + 14.2 + >>> chebyshev_distance([1.0], [2.0, 2.0]) + Traceback (most recent call last): + ... + ValueError: Both points must have the same dimension. + """ + if len(point_a) != len(point_b): + raise ValueError("Both points must have the same dimension.") + + return max(abs(a - b) for a, b in zip(point_a, point_b)) From 53638fcec4ff990ced9afb569c18b927df652596 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 9 Oct 2023 23:38:32 +0200 Subject: [PATCH 319/757] [pre-commit.ci] pre-commit autoupdate (#10197) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/pre-commit-hooks: v4.4.0 → v4.5.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.4.0...v4.5.0) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8a88dcc07622..7340a0fd08ee 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v4.5.0 hooks: - id: check-executables-have-shebangs - id: check-toml From b9a797f3d4f1a66da1e213bd92e08fa9cf6c3643 Mon Sep 17 00:00:00 2001 From: Dean Bring Date: Mon, 9 Oct 2023 16:00:37 -0700 Subject: [PATCH 320/757] Added the Minkowski distance function (#10143) * Added the Minkowski distance function * Made error handling more precise * Added note about floating point errors and corresponding doctest --- maths/minkowski_distance.py | 45 +++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 maths/minkowski_distance.py diff --git a/maths/minkowski_distance.py b/maths/minkowski_distance.py new file mode 100644 index 000000000000..3237124e8d36 --- /dev/null +++ b/maths/minkowski_distance.py @@ -0,0 +1,45 @@ +def minkowski_distance( + point_a: list[float], + point_b: list[float], + order: int, +) -> float: + """ + This function calculates the Minkowski distance for a given order between + two n-dimensional points represented as lists. For the case of order = 1, + the Minkowski distance degenerates to the Manhattan distance. For + order = 2, the usual Euclidean distance is obtained. + + https://en.wikipedia.org/wiki/Minkowski_distance + + Note: due to floating point calculation errors the output of this + function may be inaccurate. + + >>> minkowski_distance([1.0, 1.0], [2.0, 2.0], 1) + 2.0 + >>> minkowski_distance([1.0, 2.0, 3.0, 4.0], [5.0, 6.0, 7.0, 8.0], 2) + 8.0 + >>> import numpy as np + >>> np.isclose(5.0, minkowski_distance([5.0], [0.0], 3)) + True + >>> minkowski_distance([1.0], [2.0], -1) + Traceback (most recent call last): + ... + ValueError: The order must be greater than or equal to 1. + >>> minkowski_distance([1.0], [1.0, 2.0], 1) + Traceback (most recent call last): + ... + ValueError: Both points must have the same dimension. + """ + if order < 1: + raise ValueError("The order must be greater than or equal to 1.") + + if len(point_a) != len(point_b): + raise ValueError("Both points must have the same dimension.") + + return sum(abs(a - b) ** order for a, b in zip(point_a, point_b)) ** (1 / order) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 7b996e2c221aa88b5688ea08f2bb3a391b5be2c6 Mon Sep 17 00:00:00 2001 From: Kamil <32775019+quant12345@users.noreply.github.com> Date: Tue, 10 Oct 2023 09:16:02 +0500 Subject: [PATCH 321/757] backtracking -> word_search - replacing the example in doctest (#10188) * Replacing the generator with numpy vector operations from lu_decomposition. * Revert "Replacing the generator with numpy vector operations from lu_decomposition." This reverts commit ad217c66165898d62b76cc89ba09c2d7049b6448. * Replacing the example in doctest with a less resource-intensive example. --- backtracking/word_search.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/backtracking/word_search.py b/backtracking/word_search.py index c9d52012b42b..8a9b2f1b5359 100644 --- a/backtracking/word_search.py +++ b/backtracking/word_search.py @@ -98,13 +98,7 @@ def word_exists(board: list[list[str]], word: str) -> bool: False >>> word_exists([["A"]], "A") True - >>> word_exists([["A","A","A","A","A","A"], - ... ["A","A","A","A","A","A"], - ... ["A","A","A","A","A","A"], - ... ["A","A","A","A","A","A"], - ... ["A","A","A","A","A","B"], - ... ["A","A","A","A","B","A"]], - ... "AAAAAAAAAAAAABB") + >>> word_exists([["B", "A", "A"], ["A", "A", "A"], ["A", "B", "A"]], "ABB") False >>> word_exists([["A"]], 123) Traceback (most recent call last): From 4f8fa3c44a29cafaed64a73588a309e88d1f3ded Mon Sep 17 00:00:00 2001 From: Md Mahiuddin <68785084+mahiuddin-dev@users.noreply.github.com> Date: Tue, 10 Oct 2023 10:19:40 +0600 Subject: [PATCH 322/757] TypeError for non-integer input (#9250) * type error check * remove str input * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- maths/number_of_digits.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/maths/number_of_digits.py b/maths/number_of_digits.py index 86bc67f72490..bb9c0d248fd1 100644 --- a/maths/number_of_digits.py +++ b/maths/number_of_digits.py @@ -16,7 +16,15 @@ def num_digits(n: int) -> int: 1 >>> num_digits(-123456) 6 + >>> num_digits('123') # Raises a TypeError for non-integer input + Traceback (most recent call last): + ... + TypeError: Input must be an integer """ + + if not isinstance(n, int): + raise TypeError("Input must be an integer") + digits = 0 n = abs(n) while True: @@ -42,7 +50,15 @@ def num_digits_fast(n: int) -> int: 1 >>> num_digits_fast(-123456) 6 + >>> num_digits('123') # Raises a TypeError for non-integer input + Traceback (most recent call last): + ... + TypeError: Input must be an integer """ + + if not isinstance(n, int): + raise TypeError("Input must be an integer") + return 1 if n == 0 else math.floor(math.log(abs(n), 10) + 1) @@ -61,7 +77,15 @@ def num_digits_faster(n: int) -> int: 1 >>> num_digits_faster(-123456) 6 + >>> num_digits('123') # Raises a TypeError for non-integer input + Traceback (most recent call last): + ... + TypeError: Input must be an integer """ + + if not isinstance(n, int): + raise TypeError("Input must be an integer") + return len(str(abs(n))) From 1b4c4e7db216305e059cc087c3f09bc6d3e17575 Mon Sep 17 00:00:00 2001 From: dimonalik <114773527+dimonalik@users.noreply.github.com> Date: Tue, 10 Oct 2023 07:34:36 +0300 Subject: [PATCH 323/757] Made problem explanation more clear (#9841) * Update minimum_steps_to_one.py Made the problem explanation more clear and readable * updating DIRECTORY.md * Apply suggestions from code review --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: Tianyi Zheng --- dynamic_programming/minimum_steps_to_one.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dynamic_programming/minimum_steps_to_one.py b/dynamic_programming/minimum_steps_to_one.py index 8785027fbff3..68eaf56e21a7 100644 --- a/dynamic_programming/minimum_steps_to_one.py +++ b/dynamic_programming/minimum_steps_to_one.py @@ -1,7 +1,7 @@ """ YouTube Explanation: https://www.youtube.com/watch?v=f2xi3c1S95M -Given an integer n, return the minimum steps to 1 +Given an integer n, return the minimum steps from n to 1 AVAILABLE STEPS: * Decrement by 1 From 9c02f1220e571f2922855e245c5a92d4f2220f8a Mon Sep 17 00:00:00 2001 From: AkhilYadavPadala <142014008+AkhilYadavPadala@users.noreply.github.com> Date: Tue, 10 Oct 2023 10:43:32 +0530 Subject: [PATCH 324/757] seperation between description and docstrings (#9687) * seperation between description and docstrings * Update maths/factorial.py --------- Co-authored-by: sarayu sree Co-authored-by: Tianyi Zheng --- maths/factorial.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/maths/factorial.py b/maths/factorial.py index 18cacdef9b1f..aaf90f384bb9 100644 --- a/maths/factorial.py +++ b/maths/factorial.py @@ -1,4 +1,5 @@ -"""Factorial of a positive integer -- https://en.wikipedia.org/wiki/Factorial +""" +Factorial of a positive integer -- https://en.wikipedia.org/wiki/Factorial """ From f3acb52cadade9e7d012bf7f50cad32669b67b75 Mon Sep 17 00:00:00 2001 From: Paarth Goyal <138299656+pluto-tofu@users.noreply.github.com> Date: Tue, 10 Oct 2023 10:54:04 +0530 Subject: [PATCH 325/757] Added the algorithm to compute Reynolds number in the physics section (#9913) * added the algorithm to compute Reynolds number * fixed file name issue * Apply suggestions from code review --------- Co-authored-by: Tianyi Zheng --- physics/reynolds_number.py | 63 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 physics/reynolds_number.py diff --git a/physics/reynolds_number.py b/physics/reynolds_number.py new file mode 100644 index 000000000000..dffe690f8822 --- /dev/null +++ b/physics/reynolds_number.py @@ -0,0 +1,63 @@ +""" +Title : computing the Reynolds number to find + out the type of flow (laminar or turbulent) + +Reynolds number is a dimensionless quantity that is used to determine +the type of flow pattern as laminar or turbulent while flowing through a +pipe. Reynolds number is defined by the ratio of inertial forces to that of +viscous forces. + +R = Inertial Forces / Viscous Forces +R = (ρ * V * D)/μ + +where : +ρ = Density of fluid (in Kg/m^3) +D = Diameter of pipe through which fluid flows (in m) +V = Velocity of flow of the fluid (in m/s) +μ = Viscosity of the fluid (in Ns/m^2) + +If the Reynolds number calculated is high (greater than 2000), then the +flow through the pipe is said to be turbulent. If Reynolds number is low +(less than 2000), the flow is said to be laminar. Numerically, these are +acceptable values, although in general the laminar and turbulent flows +are classified according to a range. Laminar flow falls below Reynolds +number of 1100 and turbulent falls in a range greater than 2200. +Laminar flow is the type of flow in which the fluid travels smoothly in +regular paths. Conversely, turbulent flow isn't smooth and follows an +irregular path with lots of mixing. + +Reference : https://byjus.com/physics/reynolds-number/ +""" + + +def reynolds_number( + density: float, velocity: float, diameter: float, viscosity: float +) -> float: + """ + >>> reynolds_number(900, 2.5, 0.05, 0.4) + 281.25 + >>> reynolds_number(450, 3.86, 0.078, 0.23) + 589.0695652173912 + >>> reynolds_number(234, -4.5, 0.3, 0.44) + 717.9545454545454 + >>> reynolds_number(-90, 2, 0.045, 1) + Traceback (most recent call last): + ... + ValueError: please ensure that density, diameter and viscosity are positive + >>> reynolds_number(0, 2, -0.4, -2) + Traceback (most recent call last): + ... + ValueError: please ensure that density, diameter and viscosity are positive + """ + + if density <= 0 or diameter <= 0 or viscosity <= 0: + raise ValueError( + "please ensure that density, diameter and viscosity are positive" + ) + return (density * abs(velocity) * diameter) / viscosity + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 6d136036672072a2c4870da7741d4ad3026a7357 Mon Sep 17 00:00:00 2001 From: Vipin Karthic <143083087+vipinkarthic@users.noreply.github.com> Date: Tue, 10 Oct 2023 11:22:37 +0530 Subject: [PATCH 326/757] Fixes #9943 Added Doctests to binary_exponentiation_3.py (#10121) * Python mirror_formulae.py is added to the repository * Changes done after reading readme.md * Changes for running doctest on all platforms * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Change 2 for Doctests * Changes for doctest 2 * updating DIRECTORY.md * Doctest whitespace error rectification to mirror_formulae.py * updating DIRECTORY.md * Adding Thermodynamic Work Done Formulae * Work done on/by body in a thermodynamic setting * updating DIRECTORY.md * updating DIRECTORY.md * Doctest adiition to binary_exponentiation_3.py * Change 1 * updating DIRECTORY.md * Rename binary_exponentiation_3.py to binary_exponentiation_2.py * updating DIRECTORY.md * updating DIRECTORY.md * Formatting --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: Christian Clauss Co-authored-by: Tianyi Zheng --- DIRECTORY.md | 26 ++++++++++++-- maths/binary_exponentiation_2.py | 59 +++++++++++++++++++------------- 2 files changed, 59 insertions(+), 26 deletions(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index b1a23a239b01..015efb3c796d 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -54,13 +54,12 @@ * [Largest Pow Of Two Le Num](bit_manipulation/largest_pow_of_two_le_num.py) * [Missing Number](bit_manipulation/missing_number.py) * [Numbers Different Signs](bit_manipulation/numbers_different_signs.py) + * [Power Of 4](bit_manipulation/power_of_4.py) * [Reverse Bits](bit_manipulation/reverse_bits.py) * [Single Bit Manipulation Operations](bit_manipulation/single_bit_manipulation_operations.py) ## Blockchain - * [Chinese Remainder Theorem](blockchain/chinese_remainder_theorem.py) * [Diophantine Equation](blockchain/diophantine_equation.py) - * [Modular Division](blockchain/modular_division.py) ## Boolean Algebra * [And Gate](boolean_algebra/and_gate.py) @@ -101,11 +100,13 @@ * [Diffie Hellman](ciphers/diffie_hellman.py) * [Elgamal Key Generator](ciphers/elgamal_key_generator.py) * [Enigma Machine2](ciphers/enigma_machine2.py) + * [Fractionated Morse Cipher](ciphers/fractionated_morse_cipher.py) * [Hill Cipher](ciphers/hill_cipher.py) * [Mixed Keyword Cypher](ciphers/mixed_keyword_cypher.py) * [Mono Alphabetic Ciphers](ciphers/mono_alphabetic_ciphers.py) * [Morse Code](ciphers/morse_code.py) * [Onepad Cipher](ciphers/onepad_cipher.py) + * [Permutation Cipher](ciphers/permutation_cipher.py) * [Playfair Cipher](ciphers/playfair_cipher.py) * [Polybius](ciphers/polybius.py) * [Porta Cipher](ciphers/porta_cipher.py) @@ -172,6 +173,7 @@ ## Data Structures * Arrays + * [Equilibrium Index In Array](data_structures/arrays/equilibrium_index_in_array.py) * [Median Two Array](data_structures/arrays/median_two_array.py) * [Permutations](data_structures/arrays/permutations.py) * [Prefix Sum](data_structures/arrays/prefix_sum.py) @@ -352,6 +354,7 @@ * [Smith Waterman](dynamic_programming/smith_waterman.py) * [Subset Generation](dynamic_programming/subset_generation.py) * [Sum Of Subset](dynamic_programming/sum_of_subset.py) + * [Trapped Water](dynamic_programming/trapped_water.py) * [Tribonacci](dynamic_programming/tribonacci.py) * [Viterbi](dynamic_programming/viterbi.py) * [Word Break](dynamic_programming/word_break.py) @@ -360,6 +363,7 @@ * [Apparent Power](electronics/apparent_power.py) * [Builtin Voltage](electronics/builtin_voltage.py) * [Carrier Concentration](electronics/carrier_concentration.py) + * [Charging Capacitor](electronics/charging_capacitor.py) * [Circular Convolution](electronics/circular_convolution.py) * [Coulombs Law](electronics/coulombs_law.py) * [Electric Conductivity](electronics/electric_conductivity.py) @@ -466,6 +470,8 @@ * [Test Min Spanning Tree Prim](graphs/tests/test_min_spanning_tree_prim.py) ## Greedy Methods + * [Best Time To Buy And Sell Stock](greedy_methods/best_time_to_buy_and_sell_stock.py) + * [Fractional Cover Problem](greedy_methods/fractional_cover_problem.py) * [Fractional Knapsack](greedy_methods/fractional_knapsack.py) * [Fractional Knapsack 2](greedy_methods/fractional_knapsack_2.py) * [Gas Station](greedy_methods/gas_station.py) @@ -524,6 +530,10 @@ * Local Weighted Learning * [Local Weighted Learning](machine_learning/local_weighted_learning/local_weighted_learning.py) * [Logistic Regression](machine_learning/logistic_regression.py) + * Loss Functions + * [Binary Cross Entropy](machine_learning/loss_functions/binary_cross_entropy.py) + * [Huber Loss](machine_learning/loss_functions/huber_loss.py) + * [Mean Squared Error](machine_learning/loss_functions/mean_squared_error.py) * [Mfcc](machine_learning/mfcc.py) * [Multilayer Perceptron Classifier](machine_learning/multilayer_perceptron_classifier.py) * [Polynomial Regression](machine_learning/polynomial_regression.py) @@ -564,7 +574,9 @@ * [Carmichael Number](maths/carmichael_number.py) * [Catalan Number](maths/catalan_number.py) * [Ceil](maths/ceil.py) + * [Chebyshev Distance](maths/chebyshev_distance.py) * [Check Polygon](maths/check_polygon.py) + * [Chinese Remainder Theorem](maths/chinese_remainder_theorem.py) * [Chudnovsky Algorithm](maths/chudnovsky_algorithm.py) * [Collatz Sequence](maths/collatz_sequence.py) * [Combinations](maths/combinations.py) @@ -591,6 +603,7 @@ * [Gaussian](maths/gaussian.py) * [Gaussian Error Linear Unit](maths/gaussian_error_linear_unit.py) * [Gcd Of N Numbers](maths/gcd_of_n_numbers.py) + * [Germain Primes](maths/germain_primes.py) * [Greatest Common Divisor](maths/greatest_common_divisor.py) * [Greedy Coin Change](maths/greedy_coin_change.py) * [Hamming Numbers](maths/hamming_numbers.py) @@ -618,7 +631,9 @@ * [Matrix Exponentiation](maths/matrix_exponentiation.py) * [Max Sum Sliding Window](maths/max_sum_sliding_window.py) * [Median Of Two Arrays](maths/median_of_two_arrays.py) + * [Minkowski Distance](maths/minkowski_distance.py) * [Mobius Function](maths/mobius_function.py) + * [Modular Division](maths/modular_division.py) * [Modular Exponential](maths/modular_exponential.py) * [Monte Carlo](maths/monte_carlo.py) * [Monte Carlo Dice](maths/monte_carlo_dice.py) @@ -720,12 +735,16 @@ ## Neural Network * [2 Hidden Layers Neural Network](neural_network/2_hidden_layers_neural_network.py) * Activation Functions + * [Binary Step](neural_network/activation_functions/binary_step.py) * [Exponential Linear Unit](neural_network/activation_functions/exponential_linear_unit.py) * [Leaky Rectified Linear Unit](neural_network/activation_functions/leaky_rectified_linear_unit.py) * [Mish](neural_network/activation_functions/mish.py) * [Rectified Linear Unit](neural_network/activation_functions/rectified_linear_unit.py) * [Scaled Exponential Linear Unit](neural_network/activation_functions/scaled_exponential_linear_unit.py) * [Sigmoid Linear Unit](neural_network/activation_functions/sigmoid_linear_unit.py) + * [Soboleva Modified Hyperbolic Tangent](neural_network/activation_functions/soboleva_modified_hyperbolic_tangent.py) + * [Softplus](neural_network/activation_functions/softplus.py) + * [Squareplus](neural_network/activation_functions/squareplus.py) * [Back Propagation Neural Network](neural_network/back_propagation_neural_network.py) * [Convolution Neural Network](neural_network/convolution_neural_network.py) * [Perceptron](neural_network/perceptron.py) @@ -779,6 +798,7 @@ * [Newtons Second Law Of Motion](physics/newtons_second_law_of_motion.py) * [Photoelectric Effect](physics/photoelectric_effect.py) * [Potential Energy](physics/potential_energy.py) + * [Reynolds Number](physics/reynolds_number.py) * [Rms Speed Of Molecule](physics/rms_speed_of_molecule.py) * [Shear Stress](physics/shear_stress.py) * [Speed Of Sound](physics/speed_of_sound.py) @@ -1101,6 +1121,7 @@ * [Interpolation Search](searches/interpolation_search.py) * [Jump Search](searches/jump_search.py) * [Linear Search](searches/linear_search.py) + * [Median Of Medians](searches/median_of_medians.py) * [Quick Select](searches/quick_select.py) * [Sentinel Linear Search](searches/sentinel_linear_search.py) * [Simple Binary Search](searches/simple_binary_search.py) @@ -1201,6 +1222,7 @@ * [Snake Case To Camel Pascal Case](strings/snake_case_to_camel_pascal_case.py) * [Split](strings/split.py) * [String Switch Case](strings/string_switch_case.py) + * [Strip](strings/strip.py) * [Text Justification](strings/text_justification.py) * [Top K Frequent Words](strings/top_k_frequent_words.py) * [Upper](strings/upper.py) diff --git a/maths/binary_exponentiation_2.py b/maths/binary_exponentiation_2.py index 9cd143e09207..edb6b66b2594 100644 --- a/maths/binary_exponentiation_2.py +++ b/maths/binary_exponentiation_2.py @@ -1,17 +1,33 @@ """ -* Binary Exponentiation for Powers -* This is a method to find a^b in a time complexity of O(log b) -* This is one of the most commonly used methods of finding powers. -* Also useful in cases where solution to (a^b)%c is required, -* where a,b,c can be numbers over the computers calculation limits. -* Done using iteration, can also be done using recursion - -* @author chinmoy159 -* @version 1.0 dated 10/08/2017 +Binary Exponentiation +This is a method to find a^b in O(log b) time complexity +This is one of the most commonly used methods of exponentiation +It's also useful when the solution to (a^b) % c is required because a, b, c may be +over the computer's calculation limits + +Let's say you need to calculate a ^ b +- RULE 1 : a ^ b = (a*a) ^ (b/2) ---- example : 4 ^ 4 = (4*4) ^ (4/2) = 16 ^ 2 +- RULE 2 : IF b is odd, then a ^ b = a * (a ^ (b - 1)), where b - 1 is even +Once b is even, repeat the process until b = 1 or b = 0, because a^1 = a and a^0 = 1 + +For modular exponentiation, we use the fact that (a*b) % c = ((a%c) * (b%c)) % c +Now apply RULE 1 or 2 as required + +@author chinmoy159 """ def b_expo(a: int, b: int) -> int: + """ + >>> b_expo(2, 10) + 1024 + >>> b_expo(9, 0) + 1 + >>> b_expo(0, 12) + 0 + >>> b_expo(4, 12) + 16777216 + """ res = 1 while b > 0: if b & 1: @@ -24,6 +40,16 @@ def b_expo(a: int, b: int) -> int: def b_expo_mod(a: int, b: int, c: int) -> int: + """ + >>> b_expo_mod(2, 10, 1000000007) + 1024 + >>> b_expo_mod(11, 13, 19) + 11 + >>> b_expo_mod(0, 19, 20) + 0 + >>> b_expo_mod(15, 5, 4) + 3 + """ res = 1 while b > 0: if b & 1: @@ -33,18 +59,3 @@ def b_expo_mod(a: int, b: int, c: int) -> int: b >>= 1 return res - - -""" -* Wondering how this method works ! -* It's pretty simple. -* Let's say you need to calculate a ^ b -* RULE 1 : a ^ b = (a*a) ^ (b/2) ---- example : 4 ^ 4 = (4*4) ^ (4/2) = 16 ^ 2 -* RULE 2 : IF b is ODD, then ---- a ^ b = a * (a ^ (b - 1)) :: where (b - 1) is even. -* Once b is even, repeat the process to get a ^ b -* Repeat the process till b = 1 OR b = 0, because a^1 = a AND a^0 = 1 -* -* As far as the modulo is concerned, -* the fact : (a*b) % c = ((a%c) * (b%c)) % c -* Now apply RULE 1 OR 2 whichever is required. -""" From 59fc0cefefce77718044eb797e2c33cf8a7e1f9a Mon Sep 17 00:00:00 2001 From: Arnav Kohli <95236897+THEGAMECHANGER416@users.noreply.github.com> Date: Tue, 10 Oct 2023 18:50:49 +0530 Subject: [PATCH 327/757] Added categorical_crossentropy loss function (#10152) --- .../categorical_cross_entropy.py | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 machine_learning/loss_functions/categorical_cross_entropy.py diff --git a/machine_learning/loss_functions/categorical_cross_entropy.py b/machine_learning/loss_functions/categorical_cross_entropy.py new file mode 100644 index 000000000000..68f98902b473 --- /dev/null +++ b/machine_learning/loss_functions/categorical_cross_entropy.py @@ -0,0 +1,85 @@ +""" +Categorical Cross-Entropy Loss + +This function calculates the Categorical Cross-Entropy Loss between true class +labels and predicted class probabilities. + +Formula: +Categorical Cross-Entropy Loss = -Σ(y_true * ln(y_pred)) + +Resources: +- [Wikipedia - Cross entropy](https://en.wikipedia.org/wiki/Cross_entropy) +""" + +import numpy as np + + +def categorical_cross_entropy( + y_true: np.ndarray, y_pred: np.ndarray, epsilon: float = 1e-15 +) -> float: + """ + Calculate Categorical Cross-Entropy Loss between true class labels and + predicted class probabilities. + + Parameters: + - y_true: True class labels (one-hot encoded) as a NumPy array. + - y_pred: Predicted class probabilities as a NumPy array. + - epsilon: Small constant to avoid numerical instability. + + Returns: + - ce_loss: Categorical Cross-Entropy Loss as a floating-point number. + + Example: + >>> true_labels = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]]) + >>> pred_probs = np.array([[0.9, 0.1, 0.0], [0.2, 0.7, 0.1], [0.0, 0.1, 0.9]]) + >>> categorical_cross_entropy(true_labels, pred_probs) + 0.567395975254385 + + >>> y_true = np.array([[1, 0], [0, 1]]) + >>> y_pred = np.array([[0.9, 0.1, 0.0], [0.2, 0.7, 0.1]]) + >>> categorical_cross_entropy(y_true, y_pred) + Traceback (most recent call last): + ... + ValueError: Input arrays must have the same shape. + + >>> y_true = np.array([[2, 0, 1], [1, 0, 0]]) + >>> y_pred = np.array([[0.9, 0.1, 0.0], [0.2, 0.7, 0.1]]) + >>> categorical_cross_entropy(y_true, y_pred) + Traceback (most recent call last): + ... + ValueError: y_true must be one-hot encoded. + + >>> y_true = np.array([[1, 0, 1], [1, 0, 0]]) + >>> y_pred = np.array([[0.9, 0.1, 0.0], [0.2, 0.7, 0.1]]) + >>> categorical_cross_entropy(y_true, y_pred) + Traceback (most recent call last): + ... + ValueError: y_true must be one-hot encoded. + + >>> y_true = np.array([[1, 0, 0], [0, 1, 0]]) + >>> y_pred = np.array([[0.9, 0.1, 0.1], [0.2, 0.7, 0.1]]) + >>> categorical_cross_entropy(y_true, y_pred) + Traceback (most recent call last): + ... + ValueError: Predicted probabilities must sum to approximately 1. + """ + if y_true.shape != y_pred.shape: + raise ValueError("Input arrays must have the same shape.") + + if np.any((y_true != 0) & (y_true != 1)) or np.any(y_true.sum(axis=1) != 1): + raise ValueError("y_true must be one-hot encoded.") + + if not np.all(np.isclose(np.sum(y_pred, axis=1), 1, rtol=epsilon, atol=epsilon)): + raise ValueError("Predicted probabilities must sum to approximately 1.") + + # Clip predicted probabilities to avoid log(0) + y_pred = np.clip(y_pred, epsilon, 1) + + # Calculate categorical cross-entropy loss + return -np.sum(y_true * np.log(y_pred)) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 0b440285e813c54cda188eac278bda6fa4b1169f Mon Sep 17 00:00:00 2001 From: Kamil <32775019+quant12345@users.noreply.github.com> Date: Tue, 10 Oct 2023 19:24:51 +0500 Subject: [PATCH 328/757] Gaussian_elemination - change to remove warning (#10221) * Replacing the generator with numpy vector operations from lu_decomposition. * Revert "Replacing the generator with numpy vector operations from lu_decomposition." This reverts commit ad217c66165898d62b76cc89ba09c2d7049b6448. * Removes the warning: Conversion of an array with ndim > 0 to a scalar is deprecated, and will error in future. Ensure you extract a single element from your array before performing this operation --- arithmetic_analysis/gaussian_elimination.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arithmetic_analysis/gaussian_elimination.py b/arithmetic_analysis/gaussian_elimination.py index 13f509a4f117..a1a35131b157 100644 --- a/arithmetic_analysis/gaussian_elimination.py +++ b/arithmetic_analysis/gaussian_elimination.py @@ -34,7 +34,7 @@ def retroactive_resolution( x: NDArray[float64] = np.zeros((rows, 1), dtype=float) for row in reversed(range(rows)): total = np.dot(coefficients[row, row + 1 :], x[row + 1 :]) - x[row, 0] = (vector[row] - total) / coefficients[row, row] + x[row, 0] = (vector[row][0] - total[0]) / coefficients[row, row] return x From 5be5d21bed4bb546c81b5771bebca336978111e7 Mon Sep 17 00:00:00 2001 From: hollowcrust <72879387+hollowcrust@users.noreply.github.com> Date: Wed, 11 Oct 2023 00:52:53 +0800 Subject: [PATCH 329/757] Add tests for infix_2_postfix() in infix_to_prefix_conversion.py (#10095) * Add doctests, exceptions, type hints and fix bug for infix_to_prefix_conversion.py Add doctests Add exceptions for expressions with invalid bracket positions Add type hints for functions Fix a bug on line 53 (57 in PR) * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Change type hints in infix_to_prefix_conversion.py * Remove printing trailing whitespace in the output table * Fix type hint errors * Fix doctests * Adjust table convention in output and doctests * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Add doctests for infix_2_postfix() * Update print_width * Update print_width * Fix the doctests --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- .../stacks/infix_to_prefix_conversion.py | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/data_structures/stacks/infix_to_prefix_conversion.py b/data_structures/stacks/infix_to_prefix_conversion.py index 6f6d5d57e2cb..1127211d59f9 100644 --- a/data_structures/stacks/infix_to_prefix_conversion.py +++ b/data_structures/stacks/infix_to_prefix_conversion.py @@ -16,6 +16,39 @@ def infix_2_postfix(infix): + """ + >>> infix_2_postfix("a+b^c") # doctest: +NORMALIZE_WHITESPACE + Symbol | Stack | Postfix + ---------------------------- + a | | a + + | + | a + b | + | ab + ^ | +^ | ab + c | +^ | abc + | + | abc^ + | | abc^+ + 'abc^+' + >>> infix_2_postfix("1*((-a)*2+b)") + Traceback (most recent call last): + ... + KeyError: '(' + >>> infix_2_postfix("") + Symbol | Stack | Postfix + ---------------------------- + '' + >>> infix_2_postfix("(()") # doctest: +NORMALIZE_WHITESPACE + Symbol | Stack | Postfix + ---------------------------- + ( | ( | + ( | (( | + ) | ( | + | | ( + '(' + >>> infix_2_postfix("())") + Traceback (most recent call last): + ... + IndexError: list index out of range + """ stack = [] post_fix = [] priority = { @@ -74,6 +107,42 @@ def infix_2_postfix(infix): def infix_2_prefix(infix): + """ + >>> infix_2_prefix("a+b^c") # doctest: +NORMALIZE_WHITESPACE + Symbol | Stack | Postfix + ---------------------------- + c | | c + ^ | ^ | c + b | ^ | cb + + | + | cb^ + a | + | cb^a + | | cb^a+ + '+a^bc' + + >>> infix_2_prefix("1*((-a)*2+b)") + Traceback (most recent call last): + ... + KeyError: '(' + + >>> infix_2_prefix('') + Symbol | Stack | Postfix + ---------------------------- + '' + + >>> infix_2_prefix('(()') + Traceback (most recent call last): + ... + IndexError: list index out of range + + >>> infix_2_prefix('())') # doctest: +NORMALIZE_WHITESPACE + Symbol | Stack | Postfix + ---------------------------- + ( | ( | + ( | (( | + ) | ( | + | | ( + '(' + """ infix = list(infix[::-1]) # reverse the infix equation for i in range(len(infix)): @@ -88,6 +157,10 @@ def infix_2_prefix(infix): if __name__ == "__main__": + from doctest import testmod + + testmod() + Infix = input("\nEnter an Infix Equation = ") # Input an Infix equation Infix = "".join(Infix.split()) # Remove spaces from the input print("\n\t", Infix, "(Infix) -> ", infix_2_prefix(Infix), "(Prefix)") From 9a5a6c663cefb8cbc63329c27188f64462072a4c Mon Sep 17 00:00:00 2001 From: Kamil <32775019+quant12345@users.noreply.github.com> Date: Wed, 11 Oct 2023 01:14:13 +0500 Subject: [PATCH 330/757] carmichael_number - add doctests (#10038) * Replacing the generator with numpy vector operations from lu_decomposition. * Revert "Replacing the generator with numpy vector operations from lu_decomposition." This reverts commit ad217c66165898d62b76cc89ba09c2d7049b6448. * Added doctests * Update carmichael_number.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update carmichael_number.py I make an empty commit to reset: tests are failing. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update carmichael_number.py Made changes taking into account the addition: from maths.greatest_common_divisor import greatest_common_divisor. Now instead of gcd it is used: greatest_common_divisor. * Update carmichael_number.py * Update carmichael_number.py * Update carmichael_number.py I added a check for 0 and negative numbers in the tests and the code itself. Simplified obtaining the final result. * Update carmichael_number.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update maths/carmichael_number.py Co-authored-by: Tianyi Zheng * Update carmichael_number.py * Update carmichael_number.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Tianyi Zheng --- maths/carmichael_number.py | 55 +++++++++++++++++++++++++++++++++----- 1 file changed, 49 insertions(+), 6 deletions(-) diff --git a/maths/carmichael_number.py b/maths/carmichael_number.py index 81712520ffc7..08b5c70e8fe7 100644 --- a/maths/carmichael_number.py +++ b/maths/carmichael_number.py @@ -10,10 +10,21 @@ Examples of Carmichael Numbers: 561, 1105, ... https://en.wikipedia.org/wiki/Carmichael_number """ + from maths.greatest_common_divisor import greatest_common_divisor def power(x: int, y: int, mod: int) -> int: + """ + + Examples: + >>> power(2, 15, 3) + 2 + + >>> power(5, 1, 30) + 5 + """ + if y == 0: return 1 temp = power(x, y // 2, mod) % mod @@ -24,15 +35,47 @@ def power(x: int, y: int, mod: int) -> int: def is_carmichael_number(n: int) -> bool: - b = 2 - while b < n: - if greatest_common_divisor(b, n) == 1 and power(b, n - 1, n) != 1: - return False - b += 1 - return True + """ + + Examples: + >>> is_carmichael_number(562) + False + + >>> is_carmichael_number(561) + True + + >>> is_carmichael_number(5.1) + Traceback (most recent call last): + ... + ValueError: Number 5.1 must instead be a positive integer + + >>> is_carmichael_number(-7) + Traceback (most recent call last): + ... + ValueError: Number -7 must instead be a positive integer + + >>> is_carmichael_number(0) + Traceback (most recent call last): + ... + ValueError: Number 0 must instead be a positive integer + """ + + if n <= 0 or not isinstance(n, int): + msg = f"Number {n} must instead be a positive integer" + raise ValueError(msg) + + return all( + power(b, n - 1, n) == 1 + for b in range(2, n) + if greatest_common_divisor(b, n) == 1 + ) if __name__ == "__main__": + import doctest + + doctest.testmod() + number = int(input("Enter number: ").strip()) if is_carmichael_number(number): print(f"{number} is a Carmichael Number.") From 00707392332b90cc9babf7258b1de3e0efa0a580 Mon Sep 17 00:00:00 2001 From: Kamil <32775019+quant12345@users.noreply.github.com> Date: Wed, 11 Oct 2023 01:18:31 +0500 Subject: [PATCH 331/757] k_means_clust - change to remove warning (#10244) * Replacing the generator with numpy vector operations from lu_decomposition. * Revert "Replacing the generator with numpy vector operations from lu_decomposition." This reverts commit ad217c66165898d62b76cc89ba09c2d7049b6448. * the change removes the warning: /home/runner/work/Python/Python/machine_learning/k_means_clust.py:236: FutureWarning: The provided callable is currently using SeriesGroupBy.sum. In a future version of pandas, the provided callable will be used directly. To keep current behavior pass the string "sum" instead. .agg( And /home/runner/work/Python/Python/machine_learning/k_means_clust.py:236: FutureWarning: The provided callable is currently using SeriesGroupBy.mean. In a future version of pandas, the provided callable will be used directly. To keep current behavior pass the string "mean" instead. .agg( --- machine_learning/k_means_clust.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/machine_learning/k_means_clust.py b/machine_learning/k_means_clust.py index d93c5addf2ee..3fe151442e2e 100644 --- a/machine_learning/k_means_clust.py +++ b/machine_learning/k_means_clust.py @@ -235,7 +235,7 @@ def report_generator( ] # group by cluster number .agg( [ - ("sum", np.sum), + ("sum", "sum"), ("mean_with_zeros", lambda x: np.mean(np.nan_to_num(x))), ("mean_without_zeros", lambda x: x.replace(0, np.NaN).mean()), ( @@ -248,7 +248,7 @@ def report_generator( ) ), ), - ("mean_with_na", np.mean), + ("mean_with_na", "mean"), ("min", lambda x: x.min()), ("5%", lambda x: x.quantile(0.05)), ("25%", lambda x: x.quantile(0.25)), From c850227bee5efd9383d1cb8150500eb304c809fc Mon Sep 17 00:00:00 2001 From: cornbread-eater <146371786+cornbread-eater@users.noreply.github.com> Date: Tue, 10 Oct 2023 14:07:07 -0700 Subject: [PATCH 332/757] Add doctests to primelib.py (#10242) * Update primelib.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- maths/primelib.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/maths/primelib.py b/maths/primelib.py index cf01750cf912..d5c124255e56 100644 --- a/maths/primelib.py +++ b/maths/primelib.py @@ -574,6 +574,11 @@ def fib(n): """ input: positive integer 'n' returns the n-th fibonacci term , indexing by 0 + + >>> fib(5) + 8 + >>> fib(99) + 354224848179261915075 """ # precondition @@ -589,3 +594,9 @@ def fib(n): fib1 = tmp return ans + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 672fda913087ab64f9eb7b3a5600cbf83680fb8e Mon Sep 17 00:00:00 2001 From: hollowcrust <72879387+hollowcrust@users.noreply.github.com> Date: Wed, 11 Oct 2023 17:00:49 +0800 Subject: [PATCH 333/757] Fix bug and edit doctests for infix_to_prefix_conversion (#10259) * Fix bug and edit doctests * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Add type hints, raiseError and other minor adjustments * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Cleaning code * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .../stacks/infix_to_prefix_conversion.py | 101 +++++++++++------- 1 file changed, 64 insertions(+), 37 deletions(-) diff --git a/data_structures/stacks/infix_to_prefix_conversion.py b/data_structures/stacks/infix_to_prefix_conversion.py index 1127211d59f9..beff421c0cfa 100644 --- a/data_structures/stacks/infix_to_prefix_conversion.py +++ b/data_structures/stacks/infix_to_prefix_conversion.py @@ -15,7 +15,7 @@ """ -def infix_2_postfix(infix): +def infix_2_postfix(infix: str) -> str: """ >>> infix_2_postfix("a+b^c") # doctest: +NORMALIZE_WHITESPACE Symbol | Stack | Postfix @@ -28,22 +28,35 @@ def infix_2_postfix(infix): | + | abc^ | | abc^+ 'abc^+' - >>> infix_2_postfix("1*((-a)*2+b)") - Traceback (most recent call last): - ... - KeyError: '(' + + >>> infix_2_postfix("1*((-a)*2+b)") # doctest: +NORMALIZE_WHITESPACE + Symbol | Stack | Postfix + ------------------------------------------- + 1 | | 1 + * | * | 1 + ( | *( | 1 + ( | *(( | 1 + - | *((- | 1 + a | *((- | 1a + ) | *( | 1a- + * | *(* | 1a- + 2 | *(* | 1a-2 + + | *(+ | 1a-2* + b | *(+ | 1a-2*b + ) | * | 1a-2*b+ + | | 1a-2*b+* + '1a-2*b+*' + >>> infix_2_postfix("") Symbol | Stack | Postfix ---------------------------- '' - >>> infix_2_postfix("(()") # doctest: +NORMALIZE_WHITESPACE - Symbol | Stack | Postfix - ---------------------------- - ( | ( | - ( | (( | - ) | ( | - | | ( - '(' + + >>> infix_2_postfix("(()") + Traceback (most recent call last): + ... + ValueError: invalid expression + >>> infix_2_postfix("())") Traceback (most recent call last): ... @@ -59,7 +72,7 @@ def infix_2_postfix(infix): "+": 1, "-": 1, } # Priority of each operator - print_width = len(infix) if (len(infix) > 7) else 7 + print_width = max(len(infix), 7) # Print table header for output print( @@ -76,6 +89,9 @@ def infix_2_postfix(infix): elif x == "(": stack.append(x) # if x is "(" push to Stack elif x == ")": # if x is ")" pop stack until "(" is encountered + if len(stack) == 0: # close bracket without open bracket + raise IndexError("list index out of range") + while stack[-1] != "(": post_fix.append(stack.pop()) # Pop stack & add the content to Postfix stack.pop() @@ -83,7 +99,7 @@ def infix_2_postfix(infix): if len(stack) == 0: stack.append(x) # If stack is empty, push x to stack else: # while priority of x is not > priority of element in the stack - while len(stack) > 0 and priority[x] <= priority[stack[-1]]: + while stack and stack[-1] != "(" and priority[x] <= priority[stack[-1]]: post_fix.append(stack.pop()) # pop stack & add to Postfix stack.append(x) # push x to stack @@ -95,6 +111,9 @@ def infix_2_postfix(infix): ) # Output in tabular format while len(stack) > 0: # while stack is not empty + if stack[-1] == "(": # open bracket with no close bracket + raise ValueError("invalid expression") + post_fix.append(stack.pop()) # pop stack & add to Postfix print( " ".center(8), @@ -106,7 +125,7 @@ def infix_2_postfix(infix): return "".join(post_fix) # return Postfix as str -def infix_2_prefix(infix): +def infix_2_prefix(infix: str) -> str: """ >>> infix_2_prefix("a+b^c") # doctest: +NORMALIZE_WHITESPACE Symbol | Stack | Postfix @@ -119,10 +138,23 @@ def infix_2_prefix(infix): | | cb^a+ '+a^bc' - >>> infix_2_prefix("1*((-a)*2+b)") - Traceback (most recent call last): - ... - KeyError: '(' + >>> infix_2_prefix("1*((-a)*2+b)") # doctest: +NORMALIZE_WHITESPACE + Symbol | Stack | Postfix + ------------------------------------------- + ( | ( | + b | ( | b + + | (+ | b + 2 | (+ | b2 + * | (+* | b2 + ( | (+*( | b2 + a | (+*( | b2a + - | (+*(- | b2a + ) | (+* | b2a- + ) | | b2a-*+ + * | * | b2a-*+ + 1 | * | b2a-*+1 + | | b2a-*+1* + '*1+*-a2b' >>> infix_2_prefix('') Symbol | Stack | Postfix @@ -134,26 +166,21 @@ def infix_2_prefix(infix): ... IndexError: list index out of range - >>> infix_2_prefix('())') # doctest: +NORMALIZE_WHITESPACE - Symbol | Stack | Postfix - ---------------------------- - ( | ( | - ( | (( | - ) | ( | - | | ( - '(' + >>> infix_2_prefix('())') + Traceback (most recent call last): + ... + ValueError: invalid expression """ - infix = list(infix[::-1]) # reverse the infix equation + reversed_infix = list(infix[::-1]) # reverse the infix equation - for i in range(len(infix)): - if infix[i] == "(": - infix[i] = ")" # change "(" to ")" - elif infix[i] == ")": - infix[i] = "(" # change ")" to "(" + for i in range(len(reversed_infix)): + if reversed_infix[i] == "(": + reversed_infix[i] = ")" # change "(" to ")" + elif reversed_infix[i] == ")": + reversed_infix[i] = "(" # change ")" to "(" - return (infix_2_postfix("".join(infix)))[ - ::-1 - ] # call infix_2_postfix on Infix, return reverse of Postfix + # call infix_2_postfix on Infix, return reverse of Postfix + return (infix_2_postfix("".join(reversed_infix)))[::-1] if __name__ == "__main__": From 5fb6496d1bcd076018e6c829c312f486ed7bb2ee Mon Sep 17 00:00:00 2001 From: Ricardo Martinez Peinado <43684906+rmp2000@users.noreply.github.com> Date: Wed, 11 Oct 2023 12:11:05 +0200 Subject: [PATCH 334/757] Improve primelib.py test coverage #9943 (#10251) * Update the doctest of primelib.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Correct errors for the doctest of primelib.py * last error for the doctest of primelib.py * last error for the doctest of primelib.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- maths/primelib.py | 243 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 238 insertions(+), 5 deletions(-) diff --git a/maths/primelib.py b/maths/primelib.py index d5c124255e56..7e33844be12b 100644 --- a/maths/primelib.py +++ b/maths/primelib.py @@ -46,6 +46,19 @@ def is_prime(number: int) -> bool: """ input: positive integer 'number' returns true if 'number' is prime otherwise false. + + >>> is_prime(3) + True + >>> is_prime(10) + False + >>> is_prime(-1) + Traceback (most recent call last): + ... + AssertionError: 'number' must been an int and positive + >>> is_prime("test") + Traceback (most recent call last): + ... + AssertionError: 'number' must been an int and positive """ # precondition @@ -83,6 +96,16 @@ def sieve_er(n): This function implements the algorithm called sieve of erathostenes. + >>> sieve_er(8) + [2, 3, 5, 7] + >>> sieve_er(-1) + Traceback (most recent call last): + ... + AssertionError: 'N' must been an int and > 2 + >>> sieve_er("test") + Traceback (most recent call last): + ... + AssertionError: 'N' must been an int and > 2 """ # precondition @@ -116,6 +139,17 @@ def get_prime_numbers(n): input: positive integer 'N' > 2 returns a list of prime numbers from 2 up to N (inclusive) This function is more efficient as function 'sieveEr(...)' + + >>> get_prime_numbers(8) + [2, 3, 5, 7] + >>> get_prime_numbers(-1) + Traceback (most recent call last): + ... + AssertionError: 'N' must been an int and > 2 + >>> get_prime_numbers("test") + Traceback (most recent call last): + ... + AssertionError: 'N' must been an int and > 2 """ # precondition @@ -142,6 +176,21 @@ def prime_factorization(number): """ input: positive integer 'number' returns a list of the prime number factors of 'number' + + >>> prime_factorization(0) + [0] + >>> prime_factorization(8) + [2, 2, 2] + >>> prime_factorization(287) + [7, 41] + >>> prime_factorization(-1) + Traceback (most recent call last): + ... + AssertionError: 'number' must been an int and >= 0 + >>> prime_factorization("test") + Traceback (most recent call last): + ... + AssertionError: 'number' must been an int and >= 0 """ # precondition @@ -183,12 +232,27 @@ def greatest_prime_factor(number): """ input: positive integer 'number' >= 0 returns the greatest prime number factor of 'number' + + >>> greatest_prime_factor(0) + 0 + >>> greatest_prime_factor(8) + 2 + >>> greatest_prime_factor(287) + 41 + >>> greatest_prime_factor(-1) + Traceback (most recent call last): + ... + AssertionError: 'number' must been an int and >= 0 + >>> greatest_prime_factor("test") + Traceback (most recent call last): + ... + AssertionError: 'number' must been an int and >= 0 """ # precondition assert isinstance(number, int) and ( number >= 0 - ), "'number' bust been an int and >= 0" + ), "'number' must been an int and >= 0" ans = 0 @@ -210,12 +274,27 @@ def smallest_prime_factor(number): """ input: integer 'number' >= 0 returns the smallest prime number factor of 'number' + + >>> smallest_prime_factor(0) + 0 + >>> smallest_prime_factor(8) + 2 + >>> smallest_prime_factor(287) + 7 + >>> smallest_prime_factor(-1) + Traceback (most recent call last): + ... + AssertionError: 'number' must been an int and >= 0 + >>> smallest_prime_factor("test") + Traceback (most recent call last): + ... + AssertionError: 'number' must been an int and >= 0 """ # precondition assert isinstance(number, int) and ( number >= 0 - ), "'number' bust been an int and >= 0" + ), "'number' must been an int and >= 0" ans = 0 @@ -237,11 +316,24 @@ def is_even(number): """ input: integer 'number' returns true if 'number' is even, otherwise false. + + >>> is_even(0) + True + >>> is_even(8) + True + >>> is_even(287) + False + >>> is_even(-1) + False + >>> is_even("test") + Traceback (most recent call last): + ... + AssertionError: 'number' must been an int """ # precondition assert isinstance(number, int), "'number' must been an int" - assert isinstance(number % 2 == 0, bool), "compare bust been from type bool" + assert isinstance(number % 2 == 0, bool), "compare must been from type bool" return number % 2 == 0 @@ -253,11 +345,24 @@ def is_odd(number): """ input: integer 'number' returns true if 'number' is odd, otherwise false. + + >>> is_odd(0) + False + >>> is_odd(8) + False + >>> is_odd(287) + True + >>> is_odd(-1) + True + >>> is_odd("test") + Traceback (most recent call last): + ... + AssertionError: 'number' must been an int """ # precondition assert isinstance(number, int), "'number' must been an int" - assert isinstance(number % 2 != 0, bool), "compare bust been from type bool" + assert isinstance(number % 2 != 0, bool), "compare must been from type bool" return number % 2 != 0 @@ -270,6 +375,23 @@ def goldbach(number): Goldbach's assumption input: a even positive integer 'number' > 2 returns a list of two prime numbers whose sum is equal to 'number' + + >>> goldbach(8) + [3, 5] + >>> goldbach(824) + [3, 821] + >>> goldbach(0) + Traceback (most recent call last): + ... + AssertionError: 'number' must been an int, even and > 2 + >>> goldbach(-1) + Traceback (most recent call last): + ... + AssertionError: 'number' must been an int, even and > 2 + >>> goldbach("test") + Traceback (most recent call last): + ... + AssertionError: 'number' must been an int, even and > 2 """ # precondition @@ -323,6 +445,23 @@ def kg_v(number1, number2): Least common multiple input: two positive integer 'number1' and 'number2' returns the least common multiple of 'number1' and 'number2' + + >>> kg_v(8,10) + 40 + >>> kg_v(824,67) + 55208 + >>> kg_v(0) + Traceback (most recent call last): + ... + TypeError: kg_v() missing 1 required positional argument: 'number2' + >>> kg_v(10,-1) + Traceback (most recent call last): + ... + AssertionError: 'number1' and 'number2' must been positive integer. + >>> kg_v("test","test2") + Traceback (most recent call last): + ... + AssertionError: 'number1' and 'number2' must been positive integer. """ # precondition @@ -395,6 +534,21 @@ def get_prime(n): Gets the n-th prime number. input: positive integer 'n' >= 0 returns the n-th prime number, beginning at index 0 + + >>> get_prime(0) + 2 + >>> get_prime(8) + 23 + >>> get_prime(824) + 6337 + >>> get_prime(-1) + Traceback (most recent call last): + ... + AssertionError: 'number' must been a positive int + >>> get_prime("test") + Traceback (most recent call last): + ... + AssertionError: 'number' must been a positive int """ # precondition @@ -430,6 +584,25 @@ def get_primes_between(p_number_1, p_number_2): pNumber1 < pNumber2 returns a list of all prime numbers between 'pNumber1' (exclusive) and 'pNumber2' (exclusive) + + >>> get_primes_between(3, 67) + [5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61] + >>> get_primes_between(0) + Traceback (most recent call last): + ... + TypeError: get_primes_between() missing 1 required positional argument: 'p_number_2' + >>> get_primes_between(0, 1) + Traceback (most recent call last): + ... + AssertionError: The arguments must been prime numbers and 'pNumber1' < 'pNumber2' + >>> get_primes_between(-1, 3) + Traceback (most recent call last): + ... + AssertionError: 'number' must been an int and positive + >>> get_primes_between("test","test") + Traceback (most recent call last): + ... + AssertionError: 'number' must been an int and positive """ # precondition @@ -473,6 +646,19 @@ def get_divisors(n): """ input: positive integer 'n' >= 1 returns all divisors of n (inclusive 1 and 'n') + + >>> get_divisors(8) + [1, 2, 4, 8] + >>> get_divisors(824) + [1, 2, 4, 8, 103, 206, 412, 824] + >>> get_divisors(-1) + Traceback (most recent call last): + ... + AssertionError: 'n' must been int and >= 1 + >>> get_divisors("test") + Traceback (most recent call last): + ... + AssertionError: 'n' must been int and >= 1 """ # precondition @@ -497,6 +683,19 @@ def is_perfect_number(number): """ input: positive integer 'number' > 1 returns true if 'number' is a perfect number otherwise false. + + >>> is_perfect_number(28) + True + >>> is_perfect_number(824) + False + >>> is_perfect_number(-1) + Traceback (most recent call last): + ... + AssertionError: 'number' must been an int and >= 1 + >>> is_perfect_number("test") + Traceback (most recent call last): + ... + AssertionError: 'number' must been an int and >= 1 """ # precondition @@ -525,6 +724,15 @@ def simplify_fraction(numerator, denominator): input: two integer 'numerator' and 'denominator' assumes: 'denominator' != 0 returns: a tuple with simplify numerator and denominator. + + >>> simplify_fraction(10, 20) + (1, 2) + >>> simplify_fraction(10, -1) + (10, -1) + >>> simplify_fraction("test","test") + Traceback (most recent call last): + ... + AssertionError: The arguments must been from type int and 'denominator' != 0 """ # precondition @@ -554,6 +762,19 @@ def factorial(n): """ input: positive integer 'n' returns the factorial of 'n' (n!) + + >>> factorial(0) + 1 + >>> factorial(20) + 2432902008176640000 + >>> factorial(-1) + Traceback (most recent call last): + ... + AssertionError: 'n' must been a int and >= 0 + >>> factorial("test") + Traceback (most recent call last): + ... + AssertionError: 'n' must been a int and >= 0 """ # precondition @@ -570,15 +791,27 @@ def factorial(n): # ------------------------------------------------------------------- -def fib(n): +def fib(n: int) -> int: """ input: positive integer 'n' returns the n-th fibonacci term , indexing by 0 + >>> fib(0) + 1 >>> fib(5) 8 + >>> fib(20) + 10946 >>> fib(99) 354224848179261915075 + >>> fib(-1) + Traceback (most recent call last): + ... + AssertionError: 'n' must been an int and >= 0 + >>> fib("test") + Traceback (most recent call last): + ... + AssertionError: 'n' must been an int and >= 0 """ # precondition From d5323dbaee21a9ae209efa17852b02c3101a0220 Mon Sep 17 00:00:00 2001 From: Aasheesh <126905285+AasheeshLikePanner@users.noreply.github.com> Date: Wed, 11 Oct 2023 23:50:18 +0530 Subject: [PATCH 335/757] Adding doctests in simpson_rule.py (#10269) * Adding doctests in simpson_rule.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update maths/simpson_rule.py Co-authored-by: Christian Clauss * Update maths/simpson_rule.py Co-authored-by: Christian Clauss * Adding doctests in simpson_rule.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Adding doctests in simpson_rule.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Adding doctests in simpson_rule.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Adding doctests in simpson_rule.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Adding doctests in simpson_rule.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update simpson_rule.py * Adding doctests in simpson_rule.py * Adding doctests in simpson_rule.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- maths/simpson_rule.py | 43 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/maths/simpson_rule.py b/maths/simpson_rule.py index d66dc39a7171..e75fb557a2f5 100644 --- a/maths/simpson_rule.py +++ b/maths/simpson_rule.py @@ -1,7 +1,7 @@ """ Numerical integration or quadrature for a smooth function f with known values at x_i -This method is the classical approach of suming 'Equally Spaced Abscissas' +This method is the classical approach of summing 'Equally Spaced Abscissas' method 2: "Simpson Rule" @@ -9,9 +9,41 @@ """ -def method_2(boundary, steps): +def method_2(boundary: list[int], steps: int) -> float: # "Simpson Rule" # int(f) = delta_x/2 * (b-a)/3*(f1 + 4f2 + 2f_3 + ... + fn) + """ + Calculate the definite integral of a function using Simpson's Rule. + :param boundary: A list containing the lower and upper bounds of integration. + :param steps: The number of steps or resolution for the integration. + :return: The approximate integral value. + + >>> round(method_2([0, 2, 4], 10), 10) + 2.6666666667 + >>> round(method_2([2, 0], 10), 10) + -0.2666666667 + >>> round(method_2([-2, -1], 10), 10) + 2.172 + >>> round(method_2([0, 1], 10), 10) + 0.3333333333 + >>> round(method_2([0, 2], 10), 10) + 2.6666666667 + >>> round(method_2([0, 2], 100), 10) + 2.5621226667 + >>> round(method_2([0, 1], 1000), 10) + 0.3320026653 + >>> round(method_2([0, 2], 0), 10) + Traceback (most recent call last): + ... + ZeroDivisionError: Number of steps must be greater than zero + >>> round(method_2([0, 2], -10), 10) + Traceback (most recent call last): + ... + ZeroDivisionError: Number of steps must be greater than zero + """ + if steps <= 0: + raise ZeroDivisionError("Number of steps must be greater than zero") + h = (boundary[1] - boundary[0]) / steps a = boundary[0] b = boundary[1] @@ -41,11 +73,14 @@ def f(x): # enter your function here def main(): a = 0.0 # Lower bound of integration b = 1.0 # Upper bound of integration - steps = 10.0 # define number of steps or resolution - boundary = [a, b] # define boundary of integration + steps = 10.0 # number of steps or resolution + boundary = [a, b] # boundary of integration y = method_2(boundary, steps) print(f"y = {y}") if __name__ == "__main__": + import doctest + + doctest.testmod() main() From 3f094fe49d14e64d2c8f0e2c14d339ab6d0ee735 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Wed, 11 Oct 2023 20:30:02 +0200 Subject: [PATCH 336/757] Ruff pandas vet (#10281) * Python linting: Add ruff rules for Pandas-vet and Pytest-style * updating DIRECTORY.md --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- DIRECTORY.md | 1 + blockchain/diophantine_equation.py | 6 +- ciphers/xor_cipher.py | 18 +++-- conversions/decimal_to_hexadecimal.py | 3 +- .../binary_search_tree_recursive.py | 28 ++++---- .../hashing/tests/test_hash_map.py | 4 +- .../linked_list/circular_linked_list.py | 6 +- .../test_digital_image_processing.py | 3 +- graphs/graph_adjacency_list.py | 60 ++++++++-------- graphs/graph_adjacency_matrix.py | 60 ++++++++-------- hashes/sha256.py | 2 +- knapsack/tests/test_greedy_knapsack.py | 16 ++--- knapsack/tests/test_knapsack.py | 8 +-- linear_algebra/src/lib.py | 11 ++- linear_algebra/src/schur_complement.py | 7 +- linear_algebra/src/test_linear_algebra.py | 69 ++++++++++--------- machine_learning/dimensionality_reduction.py | 4 +- machine_learning/k_means_clust.py | 69 +++++++++---------- maths/least_common_multiple.py | 4 +- maths/modular_division.py | 10 ++- maths/prime_check.py | 48 ++++++------- matrix/sherman_morrison.py | 6 +- matrix/tests/test_matrix_operation.py | 28 ++++---- project_euler/problem_054/test_poker_hand.py | 14 ++-- pyproject.toml | 8 ++- strings/knuth_morris_pratt.py | 3 +- strings/rabin_karp.py | 3 +- 28 files changed, 260 insertions(+), 241 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7340a0fd08ee..84f4a7770d00 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -51,7 +51,7 @@ repos: - id: validate-pyproject - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.5.1 + rev: v1.6.0 hooks: - id: mypy args: diff --git a/DIRECTORY.md b/DIRECTORY.md index 015efb3c796d..2c6000c94ed4 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -532,6 +532,7 @@ * [Logistic Regression](machine_learning/logistic_regression.py) * Loss Functions * [Binary Cross Entropy](machine_learning/loss_functions/binary_cross_entropy.py) + * [Categorical Cross Entropy](machine_learning/loss_functions/categorical_cross_entropy.py) * [Huber Loss](machine_learning/loss_functions/huber_loss.py) * [Mean Squared Error](machine_learning/loss_functions/mean_squared_error.py) * [Mfcc](machine_learning/mfcc.py) diff --git a/blockchain/diophantine_equation.py b/blockchain/diophantine_equation.py index 7110d90230c9..ae6a145d2922 100644 --- a/blockchain/diophantine_equation.py +++ b/blockchain/diophantine_equation.py @@ -83,7 +83,8 @@ def extended_gcd(a: int, b: int) -> tuple[int, int, int]: (1, -2, 3) """ - assert a >= 0 and b >= 0 + assert a >= 0 + assert b >= 0 if b == 0: d, x, y = a, 1, 0 @@ -92,7 +93,8 @@ def extended_gcd(a: int, b: int) -> tuple[int, int, int]: x = q y = p - q * (a // b) - assert a % d == 0 and b % d == 0 + assert a % d == 0 + assert b % d == 0 assert d == a * x + b * y return (d, x, y) diff --git a/ciphers/xor_cipher.py b/ciphers/xor_cipher.py index 0f369e38f85f..559036d305c5 100644 --- a/ciphers/xor_cipher.py +++ b/ciphers/xor_cipher.py @@ -38,7 +38,8 @@ def encrypt(self, content: str, key: int) -> list[str]: """ # precondition - assert isinstance(key, int) and isinstance(content, str) + assert isinstance(key, int) + assert isinstance(content, str) key = key or self.__key or 1 @@ -56,7 +57,8 @@ def decrypt(self, content: str, key: int) -> list[str]: """ # precondition - assert isinstance(key, int) and isinstance(content, list) + assert isinstance(key, int) + assert isinstance(content, list) key = key or self.__key or 1 @@ -74,7 +76,8 @@ def encrypt_string(self, content: str, key: int = 0) -> str: """ # precondition - assert isinstance(key, int) and isinstance(content, str) + assert isinstance(key, int) + assert isinstance(content, str) key = key or self.__key or 1 @@ -99,7 +102,8 @@ def decrypt_string(self, content: str, key: int = 0) -> str: """ # precondition - assert isinstance(key, int) and isinstance(content, str) + assert isinstance(key, int) + assert isinstance(content, str) key = key or self.__key or 1 @@ -125,7 +129,8 @@ def encrypt_file(self, file: str, key: int = 0) -> bool: """ # precondition - assert isinstance(file, str) and isinstance(key, int) + assert isinstance(file, str) + assert isinstance(key, int) try: with open(file) as fin, open("encrypt.out", "w+") as fout: @@ -148,7 +153,8 @@ def decrypt_file(self, file: str, key: int) -> bool: """ # precondition - assert isinstance(file, str) and isinstance(key, int) + assert isinstance(file, str) + assert isinstance(key, int) try: with open(file) as fin, open("decrypt.out", "w+") as fout: diff --git a/conversions/decimal_to_hexadecimal.py b/conversions/decimal_to_hexadecimal.py index 5ea48401f488..b1fb4f082242 100644 --- a/conversions/decimal_to_hexadecimal.py +++ b/conversions/decimal_to_hexadecimal.py @@ -57,7 +57,8 @@ def decimal_to_hexadecimal(decimal: float) -> str: >>> decimal_to_hexadecimal(-256) == hex(-256) True """ - assert type(decimal) in (int, float) and decimal == int(decimal) + assert isinstance(decimal, (int, float)) + assert decimal == int(decimal) decimal = int(decimal) hexadecimal = "" negative = False diff --git a/data_structures/binary_tree/binary_search_tree_recursive.py b/data_structures/binary_tree/binary_search_tree_recursive.py index b5b983b9ba4c..13b9b392175c 100644 --- a/data_structures/binary_tree/binary_search_tree_recursive.py +++ b/data_structures/binary_tree/binary_search_tree_recursive.py @@ -12,6 +12,8 @@ import unittest from collections.abc import Iterator +import pytest + class Node: def __init__(self, label: int, parent: Node | None) -> None: @@ -78,7 +80,7 @@ def _put(self, node: Node | None, label: int, parent: Node | None = None) -> Nod node.right = self._put(node.right, label, node) else: msg = f"Node with label {label} already exists" - raise Exception(msg) + raise ValueError(msg) return node @@ -95,14 +97,14 @@ def search(self, label: int) -> Node: >>> node = t.search(3) Traceback (most recent call last): ... - Exception: Node with label 3 does not exist + ValueError: Node with label 3 does not exist """ return self._search(self.root, label) def _search(self, node: Node | None, label: int) -> Node: if node is None: msg = f"Node with label {label} does not exist" - raise Exception(msg) + raise ValueError(msg) else: if label < node.label: node = self._search(node.left, label) @@ -124,7 +126,7 @@ def remove(self, label: int) -> None: >>> t.remove(3) Traceback (most recent call last): ... - Exception: Node with label 3 does not exist + ValueError: Node with label 3 does not exist """ node = self.search(label) if node.right and node.left: @@ -179,7 +181,7 @@ def exists(self, label: int) -> bool: try: self.search(label) return True - except Exception: + except ValueError: return False def get_max_label(self) -> int: @@ -190,7 +192,7 @@ def get_max_label(self) -> int: >>> t.get_max_label() Traceback (most recent call last): ... - Exception: Binary search tree is empty + ValueError: Binary search tree is empty >>> t.put(8) >>> t.put(10) @@ -198,7 +200,7 @@ def get_max_label(self) -> int: 10 """ if self.root is None: - raise Exception("Binary search tree is empty") + raise ValueError("Binary search tree is empty") node = self.root while node.right is not None: @@ -214,7 +216,7 @@ def get_min_label(self) -> int: >>> t.get_min_label() Traceback (most recent call last): ... - Exception: Binary search tree is empty + ValueError: Binary search tree is empty >>> t.put(8) >>> t.put(10) @@ -222,7 +224,7 @@ def get_min_label(self) -> int: 8 """ if self.root is None: - raise Exception("Binary search tree is empty") + raise ValueError("Binary search tree is empty") node = self.root while node.left is not None: @@ -359,7 +361,7 @@ def test_put(self) -> None: assert t.root.left.left.parent == t.root.left assert t.root.left.left.label == 1 - with self.assertRaises(Exception): # noqa: B017 + with pytest.raises(ValueError): t.put(1) def test_search(self) -> None: @@ -371,7 +373,7 @@ def test_search(self) -> None: node = t.search(13) assert node.label == 13 - with self.assertRaises(Exception): # noqa: B017 + with pytest.raises(ValueError): t.search(2) def test_remove(self) -> None: @@ -517,7 +519,7 @@ def test_get_max_label(self) -> None: assert t.get_max_label() == 14 t.empty() - with self.assertRaises(Exception): # noqa: B017 + with pytest.raises(ValueError): t.get_max_label() def test_get_min_label(self) -> None: @@ -526,7 +528,7 @@ def test_get_min_label(self) -> None: assert t.get_min_label() == 1 t.empty() - with self.assertRaises(Exception): # noqa: B017 + with pytest.raises(ValueError): t.get_min_label() def test_inorder_traversal(self) -> None: diff --git a/data_structures/hashing/tests/test_hash_map.py b/data_structures/hashing/tests/test_hash_map.py index 929e67311996..4292c0178b7b 100644 --- a/data_structures/hashing/tests/test_hash_map.py +++ b/data_structures/hashing/tests/test_hash_map.py @@ -65,14 +65,14 @@ def _run_operation(obj, fun, *args): @pytest.mark.parametrize( "operations", - ( + [ pytest.param(_add_items, id="add items"), pytest.param(_overwrite_items, id="overwrite items"), pytest.param(_delete_items, id="delete items"), pytest.param(_access_absent_items, id="access absent items"), pytest.param(_add_with_resize_up, id="add with resize up"), pytest.param(_add_with_resize_down, id="add with resize down"), - ), + ], ) def test_hash_map_is_the_same_as_dict(operations): my = HashMap(initial_block_size=4) diff --git a/data_structures/linked_list/circular_linked_list.py b/data_structures/linked_list/circular_linked_list.py index ef6658733a95..54343c80a30f 100644 --- a/data_structures/linked_list/circular_linked_list.py +++ b/data_structures/linked_list/circular_linked_list.py @@ -124,7 +124,8 @@ def delete_nth(self, index: int = 0) -> Any: if not 0 <= index < len(self): raise IndexError("list index out of range.") - assert self.head is not None and self.tail is not None + assert self.head is not None + assert self.tail is not None delete_node: Node = self.head if self.head == self.tail: # Just one node self.head = self.tail = None @@ -137,7 +138,8 @@ def delete_nth(self, index: int = 0) -> Any: for _ in range(index - 1): assert temp is not None temp = temp.next - assert temp is not None and temp.next is not None + assert temp is not None + assert temp.next is not None delete_node = temp.next temp.next = temp.next.next if index == len(self) - 1: # Delete at tail diff --git a/digital_image_processing/test_digital_image_processing.py b/digital_image_processing/test_digital_image_processing.py index 2e5630458c8e..7993110d6bdd 100644 --- a/digital_image_processing/test_digital_image_processing.py +++ b/digital_image_processing/test_digital_image_processing.py @@ -73,7 +73,8 @@ def test_median_filter(): def test_sobel_filter(): grad, theta = sob.sobel_filter(gray) - assert grad.any() and theta.any() + assert grad.any() + assert theta.any() def test_sepia(): diff --git a/graphs/graph_adjacency_list.py b/graphs/graph_adjacency_list.py index 76f34f845860..d0b94f03e9b4 100644 --- a/graphs/graph_adjacency_list.py +++ b/graphs/graph_adjacency_list.py @@ -22,6 +22,8 @@ from pprint import pformat from typing import Generic, TypeVar +import pytest + T = TypeVar("T") @@ -185,9 +187,9 @@ def __assert_graph_edge_exists_check( directed_graph: GraphAdjacencyList, edge: list[int], ) -> None: - self.assertTrue(undirected_graph.contains_edge(edge[0], edge[1])) - self.assertTrue(undirected_graph.contains_edge(edge[1], edge[0])) - self.assertTrue(directed_graph.contains_edge(edge[0], edge[1])) + assert undirected_graph.contains_edge(edge[0], edge[1]) + assert undirected_graph.contains_edge(edge[1], edge[0]) + assert directed_graph.contains_edge(edge[0], edge[1]) def __assert_graph_edge_does_not_exist_check( self, @@ -195,9 +197,9 @@ def __assert_graph_edge_does_not_exist_check( directed_graph: GraphAdjacencyList, edge: list[int], ) -> None: - self.assertFalse(undirected_graph.contains_edge(edge[0], edge[1])) - self.assertFalse(undirected_graph.contains_edge(edge[1], edge[0])) - self.assertFalse(directed_graph.contains_edge(edge[0], edge[1])) + assert not undirected_graph.contains_edge(edge[0], edge[1]) + assert not undirected_graph.contains_edge(edge[1], edge[0]) + assert not directed_graph.contains_edge(edge[0], edge[1]) def __assert_graph_vertex_exists_check( self, @@ -205,8 +207,8 @@ def __assert_graph_vertex_exists_check( directed_graph: GraphAdjacencyList, vertex: int, ) -> None: - self.assertTrue(undirected_graph.contains_vertex(vertex)) - self.assertTrue(directed_graph.contains_vertex(vertex)) + assert undirected_graph.contains_vertex(vertex) + assert directed_graph.contains_vertex(vertex) def __assert_graph_vertex_does_not_exist_check( self, @@ -214,13 +216,13 @@ def __assert_graph_vertex_does_not_exist_check( directed_graph: GraphAdjacencyList, vertex: int, ) -> None: - self.assertFalse(undirected_graph.contains_vertex(vertex)) - self.assertFalse(directed_graph.contains_vertex(vertex)) + assert not undirected_graph.contains_vertex(vertex) + assert not directed_graph.contains_vertex(vertex) def __generate_random_edges( self, vertices: list[int], edge_pick_count: int ) -> list[list[int]]: - self.assertTrue(edge_pick_count <= len(vertices)) + assert edge_pick_count <= len(vertices) random_source_vertices: list[int] = random.sample( vertices[0 : int(len(vertices) / 2)], edge_pick_count @@ -281,8 +283,8 @@ def test_init_check(self) -> None: self.__assert_graph_edge_exists_check( undirected_graph, directed_graph, edge ) - self.assertFalse(undirected_graph.directed) - self.assertTrue(directed_graph.directed) + assert not undirected_graph.directed + assert directed_graph.directed def test_contains_vertex(self) -> None: random_vertices: list[int] = random.sample(range(101), 20) @@ -297,12 +299,8 @@ def test_contains_vertex(self) -> None: # Test contains_vertex for num in range(101): - self.assertEqual( - num in random_vertices, undirected_graph.contains_vertex(num) - ) - self.assertEqual( - num in random_vertices, directed_graph.contains_vertex(num) - ) + assert (num in random_vertices) == undirected_graph.contains_vertex(num) + assert (num in random_vertices) == directed_graph.contains_vertex(num) def test_add_vertices(self) -> None: random_vertices: list[int] = random.sample(range(101), 20) @@ -507,9 +505,9 @@ def test_add_vertex_exception_check(self) -> None: ) = self.__generate_graphs(20, 0, 100, 4) for vertex in random_vertices: - with self.assertRaises(ValueError): + with pytest.raises(ValueError): undirected_graph.add_vertex(vertex) - with self.assertRaises(ValueError): + with pytest.raises(ValueError): directed_graph.add_vertex(vertex) def test_remove_vertex_exception_check(self) -> None: @@ -522,9 +520,9 @@ def test_remove_vertex_exception_check(self) -> None: for i in range(101): if i not in random_vertices: - with self.assertRaises(ValueError): + with pytest.raises(ValueError): undirected_graph.remove_vertex(i) - with self.assertRaises(ValueError): + with pytest.raises(ValueError): directed_graph.remove_vertex(i) def test_add_edge_exception_check(self) -> None: @@ -536,9 +534,9 @@ def test_add_edge_exception_check(self) -> None: ) = self.__generate_graphs(20, 0, 100, 4) for edge in random_edges: - with self.assertRaises(ValueError): + with pytest.raises(ValueError): undirected_graph.add_edge(edge[0], edge[1]) - with self.assertRaises(ValueError): + with pytest.raises(ValueError): directed_graph.add_edge(edge[0], edge[1]) def test_remove_edge_exception_check(self) -> None: @@ -560,9 +558,9 @@ def test_remove_edge_exception_check(self) -> None: more_random_edges.append(edge) for edge in more_random_edges: - with self.assertRaises(ValueError): + with pytest.raises(ValueError): undirected_graph.remove_edge(edge[0], edge[1]) - with self.assertRaises(ValueError): + with pytest.raises(ValueError): directed_graph.remove_edge(edge[0], edge[1]) def test_contains_edge_exception_check(self) -> None: @@ -574,14 +572,14 @@ def test_contains_edge_exception_check(self) -> None: ) = self.__generate_graphs(20, 0, 100, 4) for vertex in random_vertices: - with self.assertRaises(ValueError): + with pytest.raises(ValueError): undirected_graph.contains_edge(vertex, 102) - with self.assertRaises(ValueError): + with pytest.raises(ValueError): directed_graph.contains_edge(vertex, 102) - with self.assertRaises(ValueError): + with pytest.raises(ValueError): undirected_graph.contains_edge(103, 102) - with self.assertRaises(ValueError): + with pytest.raises(ValueError): directed_graph.contains_edge(103, 102) diff --git a/graphs/graph_adjacency_matrix.py b/graphs/graph_adjacency_matrix.py index 4d2e02f737f9..cdef388d9098 100644 --- a/graphs/graph_adjacency_matrix.py +++ b/graphs/graph_adjacency_matrix.py @@ -22,6 +22,8 @@ from pprint import pformat from typing import Generic, TypeVar +import pytest + T = TypeVar("T") @@ -203,9 +205,9 @@ def __assert_graph_edge_exists_check( directed_graph: GraphAdjacencyMatrix, edge: list[int], ) -> None: - self.assertTrue(undirected_graph.contains_edge(edge[0], edge[1])) - self.assertTrue(undirected_graph.contains_edge(edge[1], edge[0])) - self.assertTrue(directed_graph.contains_edge(edge[0], edge[1])) + assert undirected_graph.contains_edge(edge[0], edge[1]) + assert undirected_graph.contains_edge(edge[1], edge[0]) + assert directed_graph.contains_edge(edge[0], edge[1]) def __assert_graph_edge_does_not_exist_check( self, @@ -213,9 +215,9 @@ def __assert_graph_edge_does_not_exist_check( directed_graph: GraphAdjacencyMatrix, edge: list[int], ) -> None: - self.assertFalse(undirected_graph.contains_edge(edge[0], edge[1])) - self.assertFalse(undirected_graph.contains_edge(edge[1], edge[0])) - self.assertFalse(directed_graph.contains_edge(edge[0], edge[1])) + assert not undirected_graph.contains_edge(edge[0], edge[1]) + assert not undirected_graph.contains_edge(edge[1], edge[0]) + assert not directed_graph.contains_edge(edge[0], edge[1]) def __assert_graph_vertex_exists_check( self, @@ -223,8 +225,8 @@ def __assert_graph_vertex_exists_check( directed_graph: GraphAdjacencyMatrix, vertex: int, ) -> None: - self.assertTrue(undirected_graph.contains_vertex(vertex)) - self.assertTrue(directed_graph.contains_vertex(vertex)) + assert undirected_graph.contains_vertex(vertex) + assert directed_graph.contains_vertex(vertex) def __assert_graph_vertex_does_not_exist_check( self, @@ -232,13 +234,13 @@ def __assert_graph_vertex_does_not_exist_check( directed_graph: GraphAdjacencyMatrix, vertex: int, ) -> None: - self.assertFalse(undirected_graph.contains_vertex(vertex)) - self.assertFalse(directed_graph.contains_vertex(vertex)) + assert not undirected_graph.contains_vertex(vertex) + assert not directed_graph.contains_vertex(vertex) def __generate_random_edges( self, vertices: list[int], edge_pick_count: int ) -> list[list[int]]: - self.assertTrue(edge_pick_count <= len(vertices)) + assert edge_pick_count <= len(vertices) random_source_vertices: list[int] = random.sample( vertices[0 : int(len(vertices) / 2)], edge_pick_count @@ -300,8 +302,8 @@ def test_init_check(self) -> None: undirected_graph, directed_graph, edge ) - self.assertFalse(undirected_graph.directed) - self.assertTrue(directed_graph.directed) + assert not undirected_graph.directed + assert directed_graph.directed def test_contains_vertex(self) -> None: random_vertices: list[int] = random.sample(range(101), 20) @@ -316,12 +318,8 @@ def test_contains_vertex(self) -> None: # Test contains_vertex for num in range(101): - self.assertEqual( - num in random_vertices, undirected_graph.contains_vertex(num) - ) - self.assertEqual( - num in random_vertices, directed_graph.contains_vertex(num) - ) + assert (num in random_vertices) == undirected_graph.contains_vertex(num) + assert (num in random_vertices) == directed_graph.contains_vertex(num) def test_add_vertices(self) -> None: random_vertices: list[int] = random.sample(range(101), 20) @@ -526,9 +524,9 @@ def test_add_vertex_exception_check(self) -> None: ) = self.__generate_graphs(20, 0, 100, 4) for vertex in random_vertices: - with self.assertRaises(ValueError): + with pytest.raises(ValueError): undirected_graph.add_vertex(vertex) - with self.assertRaises(ValueError): + with pytest.raises(ValueError): directed_graph.add_vertex(vertex) def test_remove_vertex_exception_check(self) -> None: @@ -541,9 +539,9 @@ def test_remove_vertex_exception_check(self) -> None: for i in range(101): if i not in random_vertices: - with self.assertRaises(ValueError): + with pytest.raises(ValueError): undirected_graph.remove_vertex(i) - with self.assertRaises(ValueError): + with pytest.raises(ValueError): directed_graph.remove_vertex(i) def test_add_edge_exception_check(self) -> None: @@ -555,9 +553,9 @@ def test_add_edge_exception_check(self) -> None: ) = self.__generate_graphs(20, 0, 100, 4) for edge in random_edges: - with self.assertRaises(ValueError): + with pytest.raises(ValueError): undirected_graph.add_edge(edge[0], edge[1]) - with self.assertRaises(ValueError): + with pytest.raises(ValueError): directed_graph.add_edge(edge[0], edge[1]) def test_remove_edge_exception_check(self) -> None: @@ -579,9 +577,9 @@ def test_remove_edge_exception_check(self) -> None: more_random_edges.append(edge) for edge in more_random_edges: - with self.assertRaises(ValueError): + with pytest.raises(ValueError): undirected_graph.remove_edge(edge[0], edge[1]) - with self.assertRaises(ValueError): + with pytest.raises(ValueError): directed_graph.remove_edge(edge[0], edge[1]) def test_contains_edge_exception_check(self) -> None: @@ -593,14 +591,14 @@ def test_contains_edge_exception_check(self) -> None: ) = self.__generate_graphs(20, 0, 100, 4) for vertex in random_vertices: - with self.assertRaises(ValueError): + with pytest.raises(ValueError): undirected_graph.contains_edge(vertex, 102) - with self.assertRaises(ValueError): + with pytest.raises(ValueError): directed_graph.contains_edge(vertex, 102) - with self.assertRaises(ValueError): + with pytest.raises(ValueError): undirected_graph.contains_edge(103, 102) - with self.assertRaises(ValueError): + with pytest.raises(ValueError): directed_graph.contains_edge(103, 102) diff --git a/hashes/sha256.py b/hashes/sha256.py index ba9aff8dbf41..bcc83edca480 100644 --- a/hashes/sha256.py +++ b/hashes/sha256.py @@ -203,7 +203,7 @@ def test_match_hashes(self) -> None: import hashlib msg = bytes("Test String", "utf-8") - self.assertEqual(SHA256(msg).hash, hashlib.sha256(msg).hexdigest()) + assert SHA256(msg).hash == hashlib.sha256(msg).hexdigest() def main() -> None: diff --git a/knapsack/tests/test_greedy_knapsack.py b/knapsack/tests/test_greedy_knapsack.py index b7b62d5d80b4..e6a40084109e 100644 --- a/knapsack/tests/test_greedy_knapsack.py +++ b/knapsack/tests/test_greedy_knapsack.py @@ -1,5 +1,7 @@ import unittest +import pytest + from knapsack import greedy_knapsack as kp @@ -16,7 +18,7 @@ def test_sorted(self): profit = [10, 20, 30, 40, 50, 60] weight = [2, 4, 6, 8, 10, 12] max_weight = 100 - self.assertEqual(kp.calc_profit(profit, weight, max_weight), 210) + assert kp.calc_profit(profit, weight, max_weight) == 210 def test_negative_max_weight(self): """ @@ -26,7 +28,7 @@ def test_negative_max_weight(self): # profit = [10, 20, 30, 40, 50, 60] # weight = [2, 4, 6, 8, 10, 12] # max_weight = -15 - self.assertRaisesRegex(ValueError, "max_weight must greater than zero.") + pytest.raises(ValueError, match="max_weight must greater than zero.") def test_negative_profit_value(self): """ @@ -36,7 +38,7 @@ def test_negative_profit_value(self): # profit = [10, -20, 30, 40, 50, 60] # weight = [2, 4, 6, 8, 10, 12] # max_weight = 15 - self.assertRaisesRegex(ValueError, "Weight can not be negative.") + pytest.raises(ValueError, match="Weight can not be negative.") def test_negative_weight_value(self): """ @@ -46,7 +48,7 @@ def test_negative_weight_value(self): # profit = [10, 20, 30, 40, 50, 60] # weight = [2, -4, 6, -8, 10, 12] # max_weight = 15 - self.assertRaisesRegex(ValueError, "Profit can not be negative.") + pytest.raises(ValueError, match="Profit can not be negative.") def test_null_max_weight(self): """ @@ -56,7 +58,7 @@ def test_null_max_weight(self): # profit = [10, 20, 30, 40, 50, 60] # weight = [2, 4, 6, 8, 10, 12] # max_weight = null - self.assertRaisesRegex(ValueError, "max_weight must greater than zero.") + pytest.raises(ValueError, match="max_weight must greater than zero.") def test_unequal_list_length(self): """ @@ -66,9 +68,7 @@ def test_unequal_list_length(self): # profit = [10, 20, 30, 40, 50] # weight = [2, 4, 6, 8, 10, 12] # max_weight = 100 - self.assertRaisesRegex( - IndexError, "The length of profit and weight must be same." - ) + pytest.raises(IndexError, match="The length of profit and weight must be same.") if __name__ == "__main__": diff --git a/knapsack/tests/test_knapsack.py b/knapsack/tests/test_knapsack.py index 248855fbce53..6932bbb3536b 100644 --- a/knapsack/tests/test_knapsack.py +++ b/knapsack/tests/test_knapsack.py @@ -20,12 +20,12 @@ def test_base_case(self): val = [0] w = [0] c = len(val) - self.assertEqual(k.knapsack(cap, w, val, c), 0) + assert k.knapsack(cap, w, val, c) == 0 val = [60] w = [10] c = len(val) - self.assertEqual(k.knapsack(cap, w, val, c), 0) + assert k.knapsack(cap, w, val, c) == 0 def test_easy_case(self): """ @@ -35,7 +35,7 @@ def test_easy_case(self): val = [1, 2, 3] w = [3, 2, 1] c = len(val) - self.assertEqual(k.knapsack(cap, w, val, c), 5) + assert k.knapsack(cap, w, val, c) == 5 def test_knapsack(self): """ @@ -45,7 +45,7 @@ def test_knapsack(self): val = [60, 100, 120] w = [10, 20, 30] c = len(val) - self.assertEqual(k.knapsack(cap, w, val, c), 220) + assert k.knapsack(cap, w, val, c) == 220 if __name__ == "__main__": diff --git a/linear_algebra/src/lib.py b/linear_algebra/src/lib.py index e3556e74c3f3..5074faf31d1d 100644 --- a/linear_algebra/src/lib.py +++ b/linear_algebra/src/lib.py @@ -200,7 +200,8 @@ def unit_basis_vector(dimension: int, pos: int) -> Vector: at index 'pos' (indexing at 0) """ # precondition - assert isinstance(dimension, int) and (isinstance(pos, int)) + assert isinstance(dimension, int) + assert isinstance(pos, int) ans = [0] * dimension ans[pos] = 1 return Vector(ans) @@ -213,11 +214,9 @@ def axpy(scalar: float, x: Vector, y: Vector) -> Vector: computes the axpy operation """ # precondition - assert ( - isinstance(x, Vector) - and isinstance(y, Vector) - and (isinstance(scalar, (int, float))) - ) + assert isinstance(x, Vector) + assert isinstance(y, Vector) + assert isinstance(scalar, (int, float)) return x * scalar + y diff --git a/linear_algebra/src/schur_complement.py b/linear_algebra/src/schur_complement.py index 750f4de5e397..1cc084043856 100644 --- a/linear_algebra/src/schur_complement.py +++ b/linear_algebra/src/schur_complement.py @@ -1,6 +1,7 @@ import unittest import numpy as np +import pytest def schur_complement( @@ -70,14 +71,14 @@ def test_schur_complement(self) -> None: det_a = np.linalg.det(a) det_s = np.linalg.det(s) - self.assertAlmostEqual(det_x, det_a * det_s) + assert np.is_close(det_x, det_a * det_s) def test_improper_a_b_dimensions(self) -> None: a = np.array([[1, 2, 1], [2, 1, 2], [3, 2, 4]]) b = np.array([[0, 3], [3, 0], [2, 3]]) c = np.array([[2, 1], [6, 3]]) - with self.assertRaises(ValueError): + with pytest.raises(ValueError): schur_complement(a, b, c) def test_improper_b_c_dimensions(self) -> None: @@ -85,7 +86,7 @@ def test_improper_b_c_dimensions(self) -> None: b = np.array([[0, 3], [3, 0], [2, 3]]) c = np.array([[2, 1, 3], [6, 3, 5]]) - with self.assertRaises(ValueError): + with pytest.raises(ValueError): schur_complement(a, b, c) diff --git a/linear_algebra/src/test_linear_algebra.py b/linear_algebra/src/test_linear_algebra.py index 50d079572e0f..95ab408b3d86 100644 --- a/linear_algebra/src/test_linear_algebra.py +++ b/linear_algebra/src/test_linear_algebra.py @@ -8,6 +8,8 @@ """ import unittest +import pytest + from .lib import ( Matrix, Vector, @@ -24,8 +26,8 @@ def test_component(self) -> None: test for method component() """ x = Vector([1, 2, 3]) - self.assertEqual(x.component(0), 1) - self.assertEqual(x.component(2), 3) + assert x.component(0) == 1 + assert x.component(2) == 3 _ = Vector() def test_str(self) -> None: @@ -33,14 +35,14 @@ def test_str(self) -> None: test for method toString() """ x = Vector([0, 0, 0, 0, 0, 1]) - self.assertEqual(str(x), "(0,0,0,0,0,1)") + assert str(x) == "(0,0,0,0,0,1)" def test_size(self) -> None: """ test for method size() """ x = Vector([1, 2, 3, 4]) - self.assertEqual(len(x), 4) + assert len(x) == 4 def test_euclidean_length(self) -> None: """ @@ -50,10 +52,10 @@ def test_euclidean_length(self) -> None: y = Vector([1, 2, 3, 4, 5]) z = Vector([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) w = Vector([1, -1, 1, -1, 2, -3, 4, -5]) - self.assertAlmostEqual(x.euclidean_length(), 2.236, 3) - self.assertAlmostEqual(y.euclidean_length(), 7.416, 3) - self.assertEqual(z.euclidean_length(), 0) - self.assertAlmostEqual(w.euclidean_length(), 7.616, 3) + assert x.euclidean_length() == pytest.approx(2.236, abs=1e-3) + assert y.euclidean_length() == pytest.approx(7.416, abs=1e-3) + assert z.euclidean_length() == 0 + assert w.euclidean_length() == pytest.approx(7.616, abs=1e-3) def test_add(self) -> None: """ @@ -61,9 +63,9 @@ def test_add(self) -> None: """ x = Vector([1, 2, 3]) y = Vector([1, 1, 1]) - self.assertEqual((x + y).component(0), 2) - self.assertEqual((x + y).component(1), 3) - self.assertEqual((x + y).component(2), 4) + assert (x + y).component(0) == 2 + assert (x + y).component(1) == 3 + assert (x + y).component(2) == 4 def test_sub(self) -> None: """ @@ -71,9 +73,9 @@ def test_sub(self) -> None: """ x = Vector([1, 2, 3]) y = Vector([1, 1, 1]) - self.assertEqual((x - y).component(0), 0) - self.assertEqual((x - y).component(1), 1) - self.assertEqual((x - y).component(2), 2) + assert (x - y).component(0) == 0 + assert (x - y).component(1) == 1 + assert (x - y).component(2) == 2 def test_mul(self) -> None: """ @@ -82,20 +84,20 @@ def test_mul(self) -> None: x = Vector([1, 2, 3]) a = Vector([2, -1, 4]) # for test of dot product b = Vector([1, -2, -1]) - self.assertEqual(str(x * 3.0), "(3.0,6.0,9.0)") - self.assertEqual((a * b), 0) + assert str(x * 3.0) == "(3.0,6.0,9.0)" + assert a * b == 0 def test_zero_vector(self) -> None: """ test for global function zero_vector() """ - self.assertEqual(str(zero_vector(10)).count("0"), 10) + assert str(zero_vector(10)).count("0") == 10 def test_unit_basis_vector(self) -> None: """ test for global function unit_basis_vector() """ - self.assertEqual(str(unit_basis_vector(3, 1)), "(0,1,0)") + assert str(unit_basis_vector(3, 1)) == "(0,1,0)" def test_axpy(self) -> None: """ @@ -103,7 +105,7 @@ def test_axpy(self) -> None: """ x = Vector([1, 2, 3]) y = Vector([1, 0, 1]) - self.assertEqual(str(axpy(2, x, y)), "(3,4,7)") + assert str(axpy(2, x, y)) == "(3,4,7)" def test_copy(self) -> None: """ @@ -111,7 +113,7 @@ def test_copy(self) -> None: """ x = Vector([1, 0, 0, 0, 0, 0]) y = x.copy() - self.assertEqual(str(x), str(y)) + assert str(x) == str(y) def test_change_component(self) -> None: """ @@ -120,14 +122,14 @@ def test_change_component(self) -> None: x = Vector([1, 0, 0]) x.change_component(0, 0) x.change_component(1, 1) - self.assertEqual(str(x), "(0,1,0)") + assert str(x) == "(0,1,0)" def test_str_matrix(self) -> None: """ test for Matrix method str() """ a = Matrix([[1, 2, 3], [2, 4, 5], [6, 7, 8]], 3, 3) - self.assertEqual("|1,2,3|\n|2,4,5|\n|6,7,8|\n", str(a)) + assert str(a) == "|1,2,3|\n|2,4,5|\n|6,7,8|\n" def test_minor(self) -> None: """ @@ -137,7 +139,7 @@ def test_minor(self) -> None: minors = [[-3, -14, -10], [-5, -10, -5], [-2, -1, 0]] for x in range(a.height()): for y in range(a.width()): - self.assertEqual(minors[x][y], a.minor(x, y)) + assert minors[x][y] == a.minor(x, y) def test_cofactor(self) -> None: """ @@ -147,14 +149,14 @@ def test_cofactor(self) -> None: cofactors = [[-3, 14, -10], [5, -10, 5], [-2, 1, 0]] for x in range(a.height()): for y in range(a.width()): - self.assertEqual(cofactors[x][y], a.cofactor(x, y)) + assert cofactors[x][y] == a.cofactor(x, y) def test_determinant(self) -> None: """ test for Matrix method determinant() """ a = Matrix([[1, 2, 3], [2, 4, 5], [6, 7, 8]], 3, 3) - self.assertEqual(-5, a.determinant()) + assert a.determinant() == -5 def test__mul__matrix(self) -> None: """ @@ -162,8 +164,8 @@ def test__mul__matrix(self) -> None: """ a = Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]], 3, 3) x = Vector([1, 2, 3]) - self.assertEqual("(14,32,50)", str(a * x)) - self.assertEqual("|2,4,6|\n|8,10,12|\n|14,16,18|\n", str(a * 2)) + assert str(a * x) == "(14,32,50)" + assert str(a * 2) == "|2,4,6|\n|8,10,12|\n|14,16,18|\n" def test_change_component_matrix(self) -> None: """ @@ -171,14 +173,14 @@ def test_change_component_matrix(self) -> None: """ a = Matrix([[1, 2, 3], [2, 4, 5], [6, 7, 8]], 3, 3) a.change_component(0, 2, 5) - self.assertEqual("|1,2,5|\n|2,4,5|\n|6,7,8|\n", str(a)) + assert str(a) == "|1,2,5|\n|2,4,5|\n|6,7,8|\n" def test_component_matrix(self) -> None: """ test for Matrix method component() """ a = Matrix([[1, 2, 3], [2, 4, 5], [6, 7, 8]], 3, 3) - self.assertEqual(7, a.component(2, 1), 0.01) + assert a.component(2, 1) == 7, 0.01 def test__add__matrix(self) -> None: """ @@ -186,7 +188,7 @@ def test__add__matrix(self) -> None: """ a = Matrix([[1, 2, 3], [2, 4, 5], [6, 7, 8]], 3, 3) b = Matrix([[1, 2, 7], [2, 4, 5], [6, 7, 10]], 3, 3) - self.assertEqual("|2,4,10|\n|4,8,10|\n|12,14,18|\n", str(a + b)) + assert str(a + b) == "|2,4,10|\n|4,8,10|\n|12,14,18|\n" def test__sub__matrix(self) -> None: """ @@ -194,15 +196,14 @@ def test__sub__matrix(self) -> None: """ a = Matrix([[1, 2, 3], [2, 4, 5], [6, 7, 8]], 3, 3) b = Matrix([[1, 2, 7], [2, 4, 5], [6, 7, 10]], 3, 3) - self.assertEqual("|0,0,-4|\n|0,0,0|\n|0,0,-2|\n", str(a - b)) + assert str(a - b) == "|0,0,-4|\n|0,0,0|\n|0,0,-2|\n" def test_square_zero_matrix(self) -> None: """ test for global function square_zero_matrix() """ - self.assertEqual( - "|0,0,0,0,0|\n|0,0,0,0,0|\n|0,0,0,0,0|\n|0,0,0,0,0|\n|0,0,0,0,0|\n", - str(square_zero_matrix(5)), + assert str(square_zero_matrix(5)) == ( + "|0,0,0,0,0|\n|0,0,0,0,0|\n|0,0,0,0,0|\n|0,0,0,0,0|\n|0,0,0,0,0|\n" ) diff --git a/machine_learning/dimensionality_reduction.py b/machine_learning/dimensionality_reduction.py index d2046f81af04..50d442ecc3de 100644 --- a/machine_learning/dimensionality_reduction.py +++ b/machine_learning/dimensionality_reduction.py @@ -169,7 +169,7 @@ def test_linear_discriminant_analysis() -> None: dimensions = 2 # Assert that the function raises an AssertionError if dimensions > classes - with pytest.raises(AssertionError) as error_info: + with pytest.raises(AssertionError) as error_info: # noqa: PT012 projected_data = linear_discriminant_analysis( features, labels, classes, dimensions ) @@ -185,7 +185,7 @@ def test_principal_component_analysis() -> None: dimensions = 2 expected_output = np.array([[6.92820323, 8.66025404, 10.39230485], [3.0, 3.0, 3.0]]) - with pytest.raises(AssertionError) as error_info: + with pytest.raises(AssertionError) as error_info: # noqa: PT012 output = principal_component_analysis(features, dimensions) if not np.allclose(expected_output, output): raise AssertionError diff --git a/machine_learning/k_means_clust.py b/machine_learning/k_means_clust.py index 3fe151442e2e..ebad66ac8e8f 100644 --- a/machine_learning/k_means_clust.py +++ b/machine_learning/k_means_clust.py @@ -128,7 +128,7 @@ def plot_heterogeneity(heterogeneity, k): def kmeans( data, k, initial_centroids, maxiter=500, record_heterogeneity=None, verbose=False ): - """This function runs k-means on given data and initial set of centroids. + """Runs k-means on given data and initial set of centroids. maxiter: maximum number of iterations to run.(default=500) record_heterogeneity: (optional) a list, to store the history of heterogeneity as function of iterations @@ -195,20 +195,20 @@ def kmeans( def report_generator( - df: pd.DataFrame, clustering_variables: np.ndarray, fill_missing_report=None + predicted: pd.DataFrame, clustering_variables: np.ndarray, fill_missing_report=None ) -> pd.DataFrame: """ - Generates a clustering report. This function takes 2 arguments as input: - df - dataframe with predicted cluster column + Generate a clustering report given these two arguments: + predicted - dataframe with predicted cluster column fill_missing_report - dictionary of rules on how we are going to fill in missing values for final generated report (not included in modelling); - >>> data = pd.DataFrame() - >>> data['numbers'] = [1, 2, 3] - >>> data['col1'] = [0.5, 2.5, 4.5] - >>> data['col2'] = [100, 200, 300] - >>> data['col3'] = [10, 20, 30] - >>> data['Cluster'] = [1, 1, 2] - >>> report_generator(data, ['col1', 'col2'], 0) + >>> predicted = pd.DataFrame() + >>> predicted['numbers'] = [1, 2, 3] + >>> predicted['col1'] = [0.5, 2.5, 4.5] + >>> predicted['col2'] = [100, 200, 300] + >>> predicted['col3'] = [10, 20, 30] + >>> predicted['Cluster'] = [1, 1, 2] + >>> report_generator(predicted, ['col1', 'col2'], 0) Features Type Mark 1 2 0 # of Customers ClusterSize False 2.000000 1.000000 1 % of Customers ClusterProportion False 0.666667 0.333333 @@ -226,11 +226,11 @@ def report_generator( """ # Fill missing values with given rules if fill_missing_report: - df = df.fillna(value=fill_missing_report) - df["dummy"] = 1 - numeric_cols = df.select_dtypes(np.number).columns + predicted = predicted.fillna(value=fill_missing_report) + predicted["dummy"] = 1 + numeric_cols = predicted.select_dtypes(np.number).columns report = ( - df.groupby(["Cluster"])[ # construct report dataframe + predicted.groupby(["Cluster"])[ # construct report dataframe numeric_cols ] # group by cluster number .agg( @@ -267,46 +267,43 @@ def report_generator( .rename(index=str, columns={"level_0": "Features", "level_1": "Type"}) ) # rename columns # calculate the size of cluster(count of clientID's) + # avoid SettingWithCopyWarning clustersize = report[ (report["Features"] == "dummy") & (report["Type"] == "count") - ].copy() # avoid SettingWithCopyWarning - clustersize.Type = ( - "ClusterSize" # rename created cluster df to match report column names - ) + ].copy() + # rename created predicted cluster to match report column names + clustersize.Type = "ClusterSize" clustersize.Features = "# of Customers" + # calculating the proportion of cluster clusterproportion = pd.DataFrame( - clustersize.iloc[:, 2:].values - / clustersize.iloc[:, 2:].values.sum() # calculating the proportion of cluster + clustersize.iloc[:, 2:].to_numpy() / clustersize.iloc[:, 2:].to_numpy().sum() ) - clusterproportion[ - "Type" - ] = "% of Customers" # rename created cluster df to match report column names + # rename created predicted cluster to match report column names + clusterproportion["Type"] = "% of Customers" clusterproportion["Features"] = "ClusterProportion" cols = clusterproportion.columns.tolist() cols = cols[-2:] + cols[:-2] clusterproportion = clusterproportion[cols] # rearrange columns to match report clusterproportion.columns = report.columns + # generating dataframe with count of nan values a = pd.DataFrame( abs( - report[report["Type"] == "count"].iloc[:, 2:].values - - clustersize.iloc[:, 2:].values + report[report["Type"] == "count"].iloc[:, 2:].to_numpy() + - clustersize.iloc[:, 2:].to_numpy() ) - ) # generating df with count of nan values + ) a["Features"] = 0 a["Type"] = "# of nan" - a.Features = report[ - report["Type"] == "count" - ].Features.tolist() # filling values in order to match report + # filling values in order to match report + a.Features = report[report["Type"] == "count"].Features.tolist() cols = a.columns.tolist() cols = cols[-2:] + cols[:-2] a = a[cols] # rearrange columns to match report a.columns = report.columns # rename columns to match report - report = report.drop( - report[report.Type == "count"].index - ) # drop count values except for cluster size - report = pd.concat( - [report, a, clustersize, clusterproportion], axis=0 - ) # concat report with cluster size and nan values + # drop count values except for cluster size + report = report.drop(report[report.Type == "count"].index) + # concat report with cluster size and nan values + report = pd.concat([report, a, clustersize, clusterproportion], axis=0) report["Mark"] = report["Features"].isin(clustering_variables) cols = report.columns.tolist() cols = cols[0:2] + cols[-1:] + cols[2:-1] diff --git a/maths/least_common_multiple.py b/maths/least_common_multiple.py index 4f28da8ab2a7..a5c4bf8e3625 100644 --- a/maths/least_common_multiple.py +++ b/maths/least_common_multiple.py @@ -67,8 +67,8 @@ def test_lcm_function(self): slow_result = least_common_multiple_slow(first_num, second_num) fast_result = least_common_multiple_fast(first_num, second_num) with self.subTest(i=i): - self.assertEqual(slow_result, self.expected_results[i]) - self.assertEqual(fast_result, self.expected_results[i]) + assert slow_result == self.expected_results[i] + assert fast_result == self.expected_results[i] if __name__ == "__main__": diff --git a/maths/modular_division.py b/maths/modular_division.py index a9d0f65c5b27..260d5683705d 100644 --- a/maths/modular_division.py +++ b/maths/modular_division.py @@ -28,7 +28,9 @@ def modular_division(a: int, b: int, n: int) -> int: 4 """ - assert n > 1 and a > 0 and greatest_common_divisor(a, n) == 1 + assert n > 1 + assert a > 0 + assert greatest_common_divisor(a, n) == 1 (d, t, s) = extended_gcd(n, a) # Implemented below x = (b * s) % n return x @@ -86,7 +88,8 @@ def extended_gcd(a: int, b: int) -> tuple[int, int, int]: ** extended_gcd function is used when d = gcd(a,b) is required in output """ - assert a >= 0 and b >= 0 + assert a >= 0 + assert b >= 0 if b == 0: d, x, y = a, 1, 0 @@ -95,7 +98,8 @@ def extended_gcd(a: int, b: int) -> tuple[int, int, int]: x = q y = p - q * (a // b) - assert a % d == 0 and b % d == 0 + assert a % d == 0 + assert b % d == 0 assert d == a * x + b * y return (d, x, y) diff --git a/maths/prime_check.py b/maths/prime_check.py index 80ab8bc5d2cd..c17877a57705 100644 --- a/maths/prime_check.py +++ b/maths/prime_check.py @@ -3,6 +3,8 @@ import math import unittest +import pytest + def is_prime(number: int) -> bool: """Checks to see if a number is a prime in O(sqrt(n)). @@ -50,33 +52,31 @@ def is_prime(number: int) -> bool: class Test(unittest.TestCase): def test_primes(self): - self.assertTrue(is_prime(2)) - self.assertTrue(is_prime(3)) - self.assertTrue(is_prime(5)) - self.assertTrue(is_prime(7)) - self.assertTrue(is_prime(11)) - self.assertTrue(is_prime(13)) - self.assertTrue(is_prime(17)) - self.assertTrue(is_prime(19)) - self.assertTrue(is_prime(23)) - self.assertTrue(is_prime(29)) + assert is_prime(2) + assert is_prime(3) + assert is_prime(5) + assert is_prime(7) + assert is_prime(11) + assert is_prime(13) + assert is_prime(17) + assert is_prime(19) + assert is_prime(23) + assert is_prime(29) def test_not_primes(self): - with self.assertRaises(AssertionError): + with pytest.raises(AssertionError): is_prime(-19) - self.assertFalse( - is_prime(0), - "Zero doesn't have any positive factors, primes must have exactly two.", - ) - self.assertFalse( - is_prime(1), - "One only has 1 positive factor, primes must have exactly two.", - ) - self.assertFalse(is_prime(2 * 2)) - self.assertFalse(is_prime(2 * 3)) - self.assertFalse(is_prime(3 * 3)) - self.assertFalse(is_prime(3 * 5)) - self.assertFalse(is_prime(3 * 5 * 7)) + assert not is_prime( + 0 + ), "Zero doesn't have any positive factors, primes must have exactly two." + assert not is_prime( + 1 + ), "One only has 1 positive factor, primes must have exactly two." + assert not is_prime(2 * 2) + assert not is_prime(2 * 3) + assert not is_prime(3 * 3) + assert not is_prime(3 * 5) + assert not is_prime(3 * 5 * 7) if __name__ == "__main__": diff --git a/matrix/sherman_morrison.py b/matrix/sherman_morrison.py index b6e50f70fdcf..7f10ae706e85 100644 --- a/matrix/sherman_morrison.py +++ b/matrix/sherman_morrison.py @@ -114,7 +114,8 @@ def __add__(self, another: Matrix) -> Matrix: # Validation assert isinstance(another, Matrix) - assert self.row == another.row and self.column == another.column + assert self.row == another.row + assert self.column == another.column # Add result = Matrix(self.row, self.column) @@ -225,7 +226,8 @@ def sherman_morrison(self, u: Matrix, v: Matrix) -> Any: """ # Size validation - assert isinstance(u, Matrix) and isinstance(v, Matrix) + assert isinstance(u, Matrix) + assert isinstance(v, Matrix) assert self.row == self.column == u.row == v.row # u, v should be column vector assert u.column == v.column == 1 # u, v should be column vector diff --git a/matrix/tests/test_matrix_operation.py b/matrix/tests/test_matrix_operation.py index 65b35fd7e78b..638f97daa2ed 100644 --- a/matrix/tests/test_matrix_operation.py +++ b/matrix/tests/test_matrix_operation.py @@ -31,14 +31,14 @@ logger.addHandler(stream_handler) -@pytest.mark.mat_ops +@pytest.mark.mat_ops() @pytest.mark.parametrize( ("mat1", "mat2"), [(mat_a, mat_b), (mat_c, mat_d), (mat_d, mat_e), (mat_f, mat_h)] ) def test_addition(mat1, mat2): if (np.array(mat1)).shape < (2, 2) or (np.array(mat2)).shape < (2, 2): + logger.info(f"\n\t{test_addition.__name__} returned integer") with pytest.raises(TypeError): - logger.info(f"\n\t{test_addition.__name__} returned integer") matop.add(mat1, mat2) elif (np.array(mat1)).shape == (np.array(mat2)).shape: logger.info(f"\n\t{test_addition.__name__} with same matrix dims") @@ -46,19 +46,19 @@ def test_addition(mat1, mat2): theo = matop.add(mat1, mat2) assert theo == act else: + logger.info(f"\n\t{test_addition.__name__} with different matrix dims") with pytest.raises(ValueError): - logger.info(f"\n\t{test_addition.__name__} with different matrix dims") matop.add(mat1, mat2) -@pytest.mark.mat_ops +@pytest.mark.mat_ops() @pytest.mark.parametrize( ("mat1", "mat2"), [(mat_a, mat_b), (mat_c, mat_d), (mat_d, mat_e), (mat_f, mat_h)] ) def test_subtraction(mat1, mat2): if (np.array(mat1)).shape < (2, 2) or (np.array(mat2)).shape < (2, 2): + logger.info(f"\n\t{test_subtraction.__name__} returned integer") with pytest.raises(TypeError): - logger.info(f"\n\t{test_subtraction.__name__} returned integer") matop.subtract(mat1, mat2) elif (np.array(mat1)).shape == (np.array(mat2)).shape: logger.info(f"\n\t{test_subtraction.__name__} with same matrix dims") @@ -66,12 +66,12 @@ def test_subtraction(mat1, mat2): theo = matop.subtract(mat1, mat2) assert theo == act else: + logger.info(f"\n\t{test_subtraction.__name__} with different matrix dims") with pytest.raises(ValueError): - logger.info(f"\n\t{test_subtraction.__name__} with different matrix dims") assert matop.subtract(mat1, mat2) -@pytest.mark.mat_ops +@pytest.mark.mat_ops() @pytest.mark.parametrize( ("mat1", "mat2"), [(mat_a, mat_b), (mat_c, mat_d), (mat_d, mat_e), (mat_f, mat_h)] ) @@ -86,33 +86,33 @@ def test_multiplication(mat1, mat2): theo = matop.multiply(mat1, mat2) assert theo == act else: + logger.info( + f"\n\t{test_multiplication.__name__} does not meet dim requirements" + ) with pytest.raises(ValueError): - logger.info( - f"\n\t{test_multiplication.__name__} does not meet dim requirements" - ) assert matop.subtract(mat1, mat2) -@pytest.mark.mat_ops +@pytest.mark.mat_ops() def test_scalar_multiply(): act = (3.5 * np.array(mat_a)).tolist() theo = matop.scalar_multiply(mat_a, 3.5) assert theo == act -@pytest.mark.mat_ops +@pytest.mark.mat_ops() def test_identity(): act = (np.identity(5)).tolist() theo = matop.identity(5) assert theo == act -@pytest.mark.mat_ops +@pytest.mark.mat_ops() @pytest.mark.parametrize("mat", [mat_a, mat_b, mat_c, mat_d, mat_e, mat_f]) def test_transpose(mat): if (np.array(mat)).shape < (2, 2): + logger.info(f"\n\t{test_transpose.__name__} returned integer") with pytest.raises(TypeError): - logger.info(f"\n\t{test_transpose.__name__} returned integer") matop.transpose(mat) else: act = (np.transpose(mat)).tolist() diff --git a/project_euler/problem_054/test_poker_hand.py b/project_euler/problem_054/test_poker_hand.py index 5735bfc37947..ba5e0c8a2643 100644 --- a/project_euler/problem_054/test_poker_hand.py +++ b/project_euler/problem_054/test_poker_hand.py @@ -147,39 +147,39 @@ 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) +@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) +@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) +@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) +@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) +@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) +@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()) +@pytest.mark.parametrize(("hand", "other", "expected"), generate_random_hands()) def test_compare_random(hand, other, expected): assert PokerHand(hand).compare_with(PokerHand(other)) == expected diff --git a/pyproject.toml b/pyproject.toml index 75da7a04513e..fe5f2f09c4ec 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,6 +19,8 @@ ignore = [ # `ruff rule S101` for a description of that rule "PLW0120", # `else` clause on loop without a `break` statement -- FIX ME "PLW060", # Using global for `{name}` but no assignment is done -- DO NOT FIX "PLW2901", # PLW2901: Redefined loop variable -- FIX ME + "PT011", # `pytest.raises(Exception)` is too broad, set the `match` parameter or use a more specific exception + "PT018", # Assertion should be broken down into multiple parts "RUF00", # Ambiguous unicode character and other rules "RUF100", # Unused `noqa` directive -- FIX ME "S101", # Use of `assert` detected -- DO NOT FIX @@ -37,6 +39,7 @@ select = [ # https://beta.ruff.rs/docs/rules "BLE", # flake8-blind-except "C4", # flake8-comprehensions "C90", # McCabe cyclomatic complexity + "DJ", # flake8-django "DTZ", # flake8-datetimez "E", # pycodestyle "EM", # flake8-errmsg @@ -52,9 +55,11 @@ select = [ # https://beta.ruff.rs/docs/rules "ISC", # flake8-implicit-str-concat "N", # pep8-naming "NPY", # NumPy-specific rules + "PD", # pandas-vet "PGH", # pygrep-hooks "PIE", # flake8-pie "PL", # Pylint + "PT", # flake8-pytest-style "PYI", # flake8-pyi "RSE", # flake8-raise "RUF", # Ruff-specific rules @@ -70,11 +75,8 @@ select = [ # https://beta.ruff.rs/docs/rules # "ANN", # flake8-annotations # FIX ME? # "COM", # flake8-commas # "D", # pydocstyle -- FIX ME? - # "DJ", # flake8-django # "ERA", # eradicate -- DO NOT FIX # "FBT", # flake8-boolean-trap # FIX ME - # "PD", # pandas-vet - # "PT", # flake8-pytest-style # "PTH", # flake8-use-pathlib # FIX ME # "Q", # flake8-quotes # "RET", # flake8-return # FIX ME? diff --git a/strings/knuth_morris_pratt.py b/strings/knuth_morris_pratt.py index 8a04eb2532c0..5120779c571e 100644 --- a/strings/knuth_morris_pratt.py +++ b/strings/knuth_morris_pratt.py @@ -71,7 +71,8 @@ def get_failure_array(pattern: str) -> list[int]: pattern = "abc1abc12" text1 = "alskfjaldsabc1abc1abc12k23adsfabcabc" text2 = "alskfjaldsk23adsfabcabc" - assert knuth_morris_pratt(text1, pattern) and knuth_morris_pratt(text2, pattern) + assert knuth_morris_pratt(text1, pattern) + assert knuth_morris_pratt(text2, pattern) # Test 2) pattern = "ABABX" diff --git a/strings/rabin_karp.py b/strings/rabin_karp.py index 532c689f8a97..9c0d0fe5c739 100644 --- a/strings/rabin_karp.py +++ b/strings/rabin_karp.py @@ -60,7 +60,8 @@ def test_rabin_karp() -> None: pattern = "abc1abc12" text1 = "alskfjaldsabc1abc1abc12k23adsfabcabc" text2 = "alskfjaldsk23adsfabcabc" - assert rabin_karp(pattern, text1) and not rabin_karp(pattern, text2) + assert rabin_karp(pattern, text1) + assert not rabin_karp(pattern, text2) # Test 2) pattern = "ABABX" From 92fbe60082b782d8b85e9667bd6d7832b5383fa3 Mon Sep 17 00:00:00 2001 From: Vipin Karthic <143083087+vipinkarthic@users.noreply.github.com> Date: Thu, 12 Oct 2023 00:35:24 +0530 Subject: [PATCH 337/757] Added doctests to carmichael_number.py (#10210) Co-authored-by: Tianyi Zheng --- maths/carmichael_number.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/maths/carmichael_number.py b/maths/carmichael_number.py index 08b5c70e8fe7..c73908545702 100644 --- a/maths/carmichael_number.py +++ b/maths/carmichael_number.py @@ -16,11 +16,9 @@ def power(x: int, y: int, mod: int) -> int: """ - Examples: >>> power(2, 15, 3) 2 - >>> power(5, 1, 30) 5 """ @@ -36,14 +34,19 @@ def power(x: int, y: int, mod: int) -> int: def is_carmichael_number(n: int) -> bool: """ - Examples: - >>> is_carmichael_number(562) + >>> is_carmichael_number(4) False - >>> is_carmichael_number(561) True - + >>> is_carmichael_number(562) + False + >>> is_carmichael_number(900) + False + >>> is_carmichael_number(1105) + True + >>> is_carmichael_number(8911) + True >>> is_carmichael_number(5.1) Traceback (most recent call last): ... From 09ce6b23d7529aa0e02a6b5cfef1a9b831a3c9ad Mon Sep 17 00:00:00 2001 From: Siddharth Warrier <117698635+siddwarr@users.noreply.github.com> Date: Thu, 12 Oct 2023 14:38:55 +0530 Subject: [PATCH 338/757] Count pairs with given sum (#10282) * added power_of_4 * deleted power_of_4 * added pairs_with_given_sum * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * updated the comment * updated return hint * updated type hints * updated the variable * updated annotation * updated code * updated code * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * added the problem link and used defaultdict * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * corrected import formatting * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update pairs_with_given_sum.py * Update data_structures/arrays/pairs_with_given_sum.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- .../arrays/pairs_with_given_sum.py | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 data_structures/arrays/pairs_with_given_sum.py diff --git a/data_structures/arrays/pairs_with_given_sum.py b/data_structures/arrays/pairs_with_given_sum.py new file mode 100644 index 000000000000..c4a5ceeae456 --- /dev/null +++ b/data_structures/arrays/pairs_with_given_sum.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 + +""" +Given an array of integers and an integer req_sum, find the number of pairs of array +elements whose sum is equal to req_sum. + +https://practice.geeksforgeeks.org/problems/count-pairs-with-given-sum5022/0 +""" +from itertools import combinations + + +def pairs_with_sum(arr: list, req_sum: int) -> int: + """ + Return the no. of pairs with sum "sum" + >>> pairs_with_sum([1, 5, 7, 1], 6) + 2 + >>> pairs_with_sum([1, 1, 1, 1, 1, 1, 1, 1], 2) + 28 + >>> pairs_with_sum([1, 7, 6, 2, 5, 4, 3, 1, 9, 8], 7) + 4 + """ + return len([1 for a, b in combinations(arr, 2) if a + b == req_sum]) + + +if __name__ == "__main__": + from doctest import testmod + + testmod() From 7ea812996c8ee1fa2eb9fbc72b7caaae8eb8ff0e Mon Sep 17 00:00:00 2001 From: Poojan Smart <44301271+PoojanSmart@users.noreply.github.com> Date: Thu, 12 Oct 2023 19:24:07 +0530 Subject: [PATCH 339/757] Adds exponential moving average algorithm (#10273) * Adds exponential moving average algorithm * code clean up * spell correction * Modifies I/O types of function * Replaces generator function * Resolved mypy type error * readibility of code and documentation * Update exponential_moving_average.py --------- Co-authored-by: Christian Clauss --- financial/exponential_moving_average.py | 73 +++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 financial/exponential_moving_average.py diff --git a/financial/exponential_moving_average.py b/financial/exponential_moving_average.py new file mode 100644 index 000000000000..0b6cea3b4c91 --- /dev/null +++ b/financial/exponential_moving_average.py @@ -0,0 +1,73 @@ +""" + Calculate the exponential moving average (EMA) on the series of stock prices. + Wikipedia Reference: https://en.wikipedia.org/wiki/Exponential_smoothing + https://www.investopedia.com/terms/e/ema.asp#toc-what-is-an-exponential + -moving-average-ema + + Exponential moving average is used in finance to analyze changes stock prices. + EMA is used in conjunction with Simple moving average (SMA), EMA reacts to the + changes in the value quicker than SMA, which is one of the advantages of using EMA. +""" + +from collections.abc import Iterator + + +def exponential_moving_average( + stock_prices: Iterator[float], window_size: int +) -> Iterator[float]: + """ + Yields exponential moving averages of the given stock prices. + >>> tuple(exponential_moving_average(iter([2, 5, 3, 8.2, 6, 9, 10]), 3)) + (2, 3.5, 3.25, 5.725, 5.8625, 7.43125, 8.715625) + + :param stock_prices: A stream of stock prices + :param window_size: The number of stock prices that will trigger a new calculation + of the exponential average (window_size > 0) + :return: Yields a sequence of exponential moving averages + + Formula: + + st = alpha * xt + (1 - alpha) * st_prev + + Where, + st : Exponential moving average at timestamp t + xt : stock price in from the stock prices at timestamp t + st_prev : Exponential moving average at timestamp t-1 + alpha : 2/(1 + window_size) - smoothing factor + + Exponential moving average (EMA) is a rule of thumb technique for + smoothing time series data using an exponential window function. + """ + + if window_size <= 0: + raise ValueError("window_size must be > 0") + + # Calculating smoothing factor + alpha = 2 / (1 + window_size) + + # Exponential average at timestamp t + moving_average = 0.0 + + for i, stock_price in enumerate(stock_prices): + if i <= window_size: + # Assigning simple moving average till the window_size for the first time + # is reached + moving_average = (moving_average + stock_price) * 0.5 if i else stock_price + else: + # Calculating exponential moving average based on current timestamp data + # point and previous exponential average value + moving_average = (alpha * stock_price) + ((1 - alpha) * moving_average) + yield moving_average + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + + stock_prices = [2.0, 5, 3, 8.2, 6, 9, 10] + window_size = 3 + result = tuple(exponential_moving_average(iter(stock_prices), window_size)) + print(f"{stock_prices = }") + print(f"{window_size = }") + print(f"{result = }") From ecf21bfc87c1d1cd4730e628279b609151bc6c57 Mon Sep 17 00:00:00 2001 From: Daniela Large <133594563+dannylarge144@users.noreply.github.com> Date: Thu, 12 Oct 2023 16:51:06 +0100 Subject: [PATCH 340/757] Added imply gate to boolean algebra (#9849) * Add files via upload * Update imply_gate.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update boolean_algebra/imply_gate.py Co-authored-by: Tianyi Zheng * Update imply_gate.py Made changes requested * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update imply_gate.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Tianyi Zheng --- boolean_algebra/imply_gate.py | 40 +++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 boolean_algebra/imply_gate.py diff --git a/boolean_algebra/imply_gate.py b/boolean_algebra/imply_gate.py new file mode 100644 index 000000000000..151a7ad6439a --- /dev/null +++ b/boolean_algebra/imply_gate.py @@ -0,0 +1,40 @@ +""" +An IMPLY Gate is a logic gate in boolean algebra which results to 1 if +either input 1 is 0, or if input 1 is 1, then the output is 1 only if input 2 is 1. +It is true if input 1 implies input 2. + +Following is the truth table of an IMPLY Gate: + ------------------------------ + | Input 1 | Input 2 | Output | + ------------------------------ + | 0 | 0 | 1 | + | 0 | 1 | 1 | + | 1 | 0 | 0 | + | 1 | 1 | 1 | + ------------------------------ + +Refer - https://en.wikipedia.org/wiki/IMPLY_gate +""" + + +def imply_gate(input_1: int, input_2: int) -> int: + """ + Calculate IMPLY of the input values + + >>> imply_gate(0, 0) + 1 + >>> imply_gate(0, 1) + 1 + >>> imply_gate(1, 0) + 0 + >>> imply_gate(1, 1) + 1 + """ + return int(input_1 == 0 or input_2 == 1) + + +if __name__ == "__main__": + print(imply_gate(0, 0)) + print(imply_gate(0, 1)) + print(imply_gate(1, 0)) + print(imply_gate(1, 1)) From b94cdbab1a7f3793e63526cd29a8f415ff0b55ac Mon Sep 17 00:00:00 2001 From: Pranavkumar Mallela <87595299+pranav-mallela@users.noreply.github.com> Date: Fri, 13 Oct 2023 01:21:53 +0530 Subject: [PATCH 341/757] add find triplets with 0 sum (3sum) (#10040) * add find triplets with 0 sum (3sum) * Update find_triplets_with_0_sum.py * Update find_triplets_with_0_sum.py --------- Co-authored-by: Christian Clauss --- .../arrays/find_triplets_with_0_sum.py | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 data_structures/arrays/find_triplets_with_0_sum.py diff --git a/data_structures/arrays/find_triplets_with_0_sum.py b/data_structures/arrays/find_triplets_with_0_sum.py new file mode 100644 index 000000000000..8217ff857e3d --- /dev/null +++ b/data_structures/arrays/find_triplets_with_0_sum.py @@ -0,0 +1,24 @@ +from itertools import combinations + + +def find_triplets_with_0_sum(nums: list[int]) -> list[list[int]]: + """ + Given a list of integers, return elements a, b, c such that a + b + c = 0. + Args: + nums: list of integers + Returns: + list of lists of integers where sum(each_list) == 0 + Examples: + >>> find_triplets_with_0_sum([-1, 0, 1, 2, -1, -4]) + [[-1, -1, 2], [-1, 0, 1]] + >>> find_triplets_with_0_sum([]) + [] + >>> find_triplets_with_0_sum([0, 0, 0]) + [[0, 0, 0]] + >>> find_triplets_with_0_sum([1, 2, 3, 0, -1, -2, -3]) + [[-3, 0, 3], [-3, 1, 2], [-2, -1, 3], [-2, 0, 2], [-1, 0, 1]] + """ + return [ + list(x) + for x in sorted({abc for abc in combinations(sorted(nums), 3) if not sum(abc)}) + ] From 24f6f8c137a6ba9784c06da3694a1d36781b7a88 Mon Sep 17 00:00:00 2001 From: Daniela Large <133594563+dannylarge144@users.noreply.github.com> Date: Fri, 13 Oct 2023 05:29:39 +0100 Subject: [PATCH 342/757] Added nimply gate to boolean_algebra (#10344) * Add files via upload * Update imply_gate.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update boolean_algebra/imply_gate.py Co-authored-by: Tianyi Zheng * Update imply_gate.py Made changes requested * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update imply_gate.py * Added nimply gate * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Tianyi Zheng --- boolean_algebra/nimply_gate.py | 40 ++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 boolean_algebra/nimply_gate.py diff --git a/boolean_algebra/nimply_gate.py b/boolean_algebra/nimply_gate.py new file mode 100644 index 000000000000..6e34332d9112 --- /dev/null +++ b/boolean_algebra/nimply_gate.py @@ -0,0 +1,40 @@ +""" +An NIMPLY Gate is a logic gate in boolean algebra which results to 0 if +either input 1 is 0, or if input 1 is 1, then it is 0 only if input 2 is 1. +It is false if input 1 implies input 2. It is the negated form of imply + +Following is the truth table of an NIMPLY Gate: + ------------------------------ + | Input 1 | Input 2 | Output | + ------------------------------ + | 0 | 0 | 0 | + | 0 | 1 | 0 | + | 1 | 0 | 1 | + | 1 | 1 | 0 | + ------------------------------ + +Refer - https://en.wikipedia.org/wiki/NIMPLY_gate +""" + + +def nimply_gate(input_1: int, input_2: int) -> int: + """ + Calculate NIMPLY of the input values + + >>> nimply_gate(0, 0) + 0 + >>> nimply_gate(0, 1) + 0 + >>> nimply_gate(1, 0) + 1 + >>> nimply_gate(1, 1) + 0 + """ + return int(input_1 == 1 and input_2 == 0) + + +if __name__ == "__main__": + print(nimply_gate(0, 0)) + print(nimply_gate(0, 1)) + print(nimply_gate(1, 0)) + print(nimply_gate(1, 1)) From ebe66935d2842a0e0cbea58dcc647428f357f15e Mon Sep 17 00:00:00 2001 From: Saahil Mahato <115351000+saahil-mahato@users.noreply.github.com> Date: Fri, 13 Oct 2023 11:49:48 +0545 Subject: [PATCH 343/757] Add Solovay-Strassen Primality test (#10335) * Add Solovay-Strassen Primality test * fix: resolve comments * refactor: docs change --- maths/solovay_strassen_primality_test.py | 107 +++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 maths/solovay_strassen_primality_test.py diff --git a/maths/solovay_strassen_primality_test.py b/maths/solovay_strassen_primality_test.py new file mode 100644 index 000000000000..1d11d458369a --- /dev/null +++ b/maths/solovay_strassen_primality_test.py @@ -0,0 +1,107 @@ +""" +This script implements the Solovay-Strassen Primality test. + +This probabilistic primality test is based on Euler's criterion. It is similar +to the Fermat test but uses quadratic residues. It can quickly identify +composite numbers but may occasionally classify composite numbers as prime. + +More details and concepts about this can be found on: +https://en.wikipedia.org/wiki/Solovay%E2%80%93Strassen_primality_test +""" + + +import random + + +def jacobi_symbol(random_a: int, number: int) -> int: + """ + Calculate the Jacobi symbol. The Jacobi symbol is a generalization + of the Legendre symbol, which can be used to simplify computations involving + quadratic residues. The Jacobi symbol is used in primality tests, like the + Solovay-Strassen test, because it helps determine if an integer is a + quadratic residue modulo a given modulus, providing valuable information + about the number's potential primality or compositeness. + + Parameters: + random_a: A randomly chosen integer from 2 to n-2 (inclusive) + number: The number that is tested for primality + + Returns: + jacobi_symbol: The Jacobi symbol is a mathematical function + used to determine whether an integer is a quadratic residue modulo + another integer (usually prime) or not. + + >>> jacobi_symbol(2, 13) + -1 + >>> jacobi_symbol(5, 19) + 1 + >>> jacobi_symbol(7, 14) + 0 + """ + + if random_a in (0, 1): + return random_a + + random_a %= number + t = 1 + + while random_a != 0: + while random_a % 2 == 0: + random_a //= 2 + r = number % 8 + if r in (3, 5): + t = -t + + random_a, number = number, random_a + + if random_a % 4 == number % 4 == 3: + t = -t + + random_a %= number + + return t if number == 1 else 0 + + +def solovay_strassen(number: int, iterations: int) -> bool: + """ + Check whether the input number is prime or not using + the Solovay-Strassen Primality test + + Parameters: + number: The number that is tested for primality + iterations: The number of times that the test is run + which effects the accuracy + + Returns: + result: True if number is probably prime and false + if not + + >>> random.seed(10) + >>> solovay_strassen(13, 5) + True + >>> solovay_strassen(9, 10) + False + >>> solovay_strassen(17, 15) + True + """ + + if number <= 1: + return False + if number <= 3: + return True + + for _ in range(iterations): + a = random.randint(2, number - 2) + x = jacobi_symbol(a, number) + y = pow(a, (number - 1) // 2, number) + + if x == 0 or y != x % number: + return False + + return True + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From c85506262d8fc6fcf154651ce8affdfb96b57ece Mon Sep 17 00:00:00 2001 From: Saahil Mahato <115351000+saahil-mahato@users.noreply.github.com> Date: Fri, 13 Oct 2023 19:03:52 +0545 Subject: [PATCH 344/757] Add Damerau-Levenshtein distance algorithm (#10159) * Add Damerau-Levenshtein distance algorithm * fix: precommit check * fix: doc correction * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * refactor: use variable for length and doc correction * Update damerau_levenshtein_distance.py * Update damerau_levenshtein_distance.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- strings/damerau_levenshtein_distance.py | 71 +++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 strings/damerau_levenshtein_distance.py diff --git a/strings/damerau_levenshtein_distance.py b/strings/damerau_levenshtein_distance.py new file mode 100644 index 000000000000..72de019499e2 --- /dev/null +++ b/strings/damerau_levenshtein_distance.py @@ -0,0 +1,71 @@ +""" +This script is a implementation of the Damerau-Levenshtein distance algorithm. + +It's an algorithm that measures the edit distance between two string sequences + +More information about this algorithm can be found in this wikipedia article: +https://en.wikipedia.org/wiki/Damerau%E2%80%93Levenshtein_distance +""" + + +def damerau_levenshtein_distance(first_string: str, second_string: str) -> int: + """ + Implements the Damerau-Levenshtein distance algorithm that measures + the edit distance between two strings. + + Parameters: + first_string: The first string to compare + second_string: The second string to compare + + Returns: + distance: The edit distance between the first and second strings + + >>> damerau_levenshtein_distance("cat", "cut") + 1 + >>> damerau_levenshtein_distance("kitten", "sitting") + 3 + >>> damerau_levenshtein_distance("hello", "world") + 4 + >>> damerau_levenshtein_distance("book", "back") + 2 + >>> damerau_levenshtein_distance("container", "containment") + 3 + >>> damerau_levenshtein_distance("container", "containment") + 3 + """ + # Create a dynamic programming matrix to store the distances + dp_matrix = [[0] * (len(second_string) + 1) for _ in range(len(first_string) + 1)] + + # Initialize the matrix + for i in range(len(first_string) + 1): + dp_matrix[i][0] = i + for j in range(len(second_string) + 1): + dp_matrix[0][j] = j + + # Fill the matrix + for i, first_char in enumerate(first_string, start=1): + for j, second_char in enumerate(second_string, start=1): + cost = int(first_char != second_char) + + dp_matrix[i][j] = min( + dp_matrix[i - 1][j] + 1, # Deletion + dp_matrix[i][j - 1] + 1, # Insertion + dp_matrix[i - 1][j - 1] + cost, # Substitution + ) + + if ( + i > 1 + and j > 1 + and first_string[i - 1] == second_string[j - 2] + and first_string[i - 2] == second_string[j - 1] + ): + # Transposition + dp_matrix[i][j] = min(dp_matrix[i][j], dp_matrix[i - 2][j - 2] + cost) + + return dp_matrix[-1][-1] + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 1117a50665b053ef7716cf1e80b29e11d30886c7 Mon Sep 17 00:00:00 2001 From: Saurabh Mahapatra <98408932+its-100rabh@users.noreply.github.com> Date: Fri, 13 Oct 2023 21:25:32 +0530 Subject: [PATCH 345/757] Modified comments on lower.py (#10369) --- strings/lower.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/strings/lower.py b/strings/lower.py index 9ae419123ceb..49256b0169ef 100644 --- a/strings/lower.py +++ b/strings/lower.py @@ -14,9 +14,9 @@ def lower(word: str) -> str: 'what' """ - # 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 + # Converting to ASCII value, obtaining the integer representation + # and checking to see if the character is a capital letter. + # If it is a capital letter, it is shifted by 32, making it a lowercase letter. return "".join(chr(ord(char) + 32) if "A" <= char <= "Z" else char for char in word) From d96029e13d181229c692b8e4cafe2661cdae919e Mon Sep 17 00:00:00 2001 From: SalmanSi <114280969+SalmanSi@users.noreply.github.com> Date: Fri, 13 Oct 2023 22:48:31 +0500 Subject: [PATCH 346/757] added doctests for dynamicprogramming/minimum_partition (#10033) * added doctests * added doctests * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Add doctests to integer_partition.py * Update minimum_partition.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- dynamic_programming/integer_partition.py | 24 +++++++++++++++ dynamic_programming/minimum_partition.py | 38 ++++++++++++++++++++---- 2 files changed, 57 insertions(+), 5 deletions(-) diff --git a/dynamic_programming/integer_partition.py b/dynamic_programming/integer_partition.py index 8ed2e51bd4bd..145bc29d0fca 100644 --- a/dynamic_programming/integer_partition.py +++ b/dynamic_programming/integer_partition.py @@ -3,10 +3,34 @@ partitions into exactly k parts plus the number of partitions into at least k-1 parts. Subtracting 1 from each part of a partition of n into k parts gives a partition of n-k into k parts. These two facts together are used for this algorithm. +* https://en.wikipedia.org/wiki/Partition_(number_theory) +* https://en.wikipedia.org/wiki/Partition_function_(number_theory) """ def partition(m: int) -> int: + """ + >>> partition(5) + 7 + >>> partition(7) + 15 + >>> partition(100) + 190569292 + >>> partition(1_000) + 24061467864032622473692149727991 + >>> partition(-7) + Traceback (most recent call last): + ... + IndexError: list index out of range + >>> partition(0) + Traceback (most recent call last): + ... + IndexError: list assignment index out of range + >>> partition(7.8) + Traceback (most recent call last): + ... + TypeError: 'float' object cannot be interpreted as an integer + """ memo: list[list[int]] = [[0 for _ in range(m)] for _ in range(m + 1)] for i in range(m + 1): memo[i][0] = 1 diff --git a/dynamic_programming/minimum_partition.py b/dynamic_programming/minimum_partition.py index e6188cb33b3a..748c0599efb0 100644 --- a/dynamic_programming/minimum_partition.py +++ b/dynamic_programming/minimum_partition.py @@ -3,7 +3,7 @@ """ -def find_min(arr: list[int]) -> int: +def find_min(numbers: list[int]) -> int: """ >>> find_min([1, 2, 3, 4, 5]) 1 @@ -15,9 +15,37 @@ def find_min(arr: list[int]) -> int: 3 >>> find_min([]) 0 + >>> find_min([1, 2, 3, 4]) + 0 + >>> find_min([0, 0, 0, 0]) + 0 + >>> find_min([-1, -5, 5, 1]) + 0 + >>> find_min([-1, -5, 5, 1]) + 0 + >>> find_min([9, 9, 9, 9, 9]) + 9 + >>> find_min([1, 5, 10, 3]) + 1 + >>> find_min([-1, 0, 1]) + 0 + >>> find_min(range(10, 0, -1)) + 1 + >>> find_min([-1]) + Traceback (most recent call last): + -- + IndexError: list assignment index out of range + >>> find_min([0, 0, 0, 1, 2, -4]) + Traceback (most recent call last): + ... + IndexError: list assignment index out of range + >>> find_min([-1, -5, -10, -3]) + Traceback (most recent call last): + ... + IndexError: list assignment index out of range """ - n = len(arr) - s = sum(arr) + n = len(numbers) + s = sum(numbers) dp = [[False for x in range(s + 1)] for y in range(n + 1)] @@ -31,8 +59,8 @@ def find_min(arr: list[int]) -> int: for j in range(1, s + 1): dp[i][j] = dp[i - 1][j] - if arr[i - 1] <= j: - dp[i][j] = dp[i][j] or dp[i - 1][j - arr[i - 1]] + if numbers[i - 1] <= j: + dp[i][j] = dp[i][j] or dp[i - 1][j - numbers[i - 1]] for j in range(int(s / 2), -1, -1): if dp[n][j] is True: From 9fb0cd271efec0fc651a5143aedda42f3dc93ea8 Mon Sep 17 00:00:00 2001 From: Dale Dai <145884899+CouldNot@users.noreply.github.com> Date: Fri, 13 Oct 2023 23:47:08 -0700 Subject: [PATCH 347/757] Expand euler phi function doctest (#10401) --- maths/basic_maths.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/maths/basic_maths.py b/maths/basic_maths.py index 26c52c54983e..c9e3d00fa23b 100644 --- a/maths/basic_maths.py +++ b/maths/basic_maths.py @@ -98,7 +98,17 @@ def euler_phi(n: int) -> int: """Calculate Euler's Phi Function. >>> euler_phi(100) 40 + >>> euler_phi(0) + Traceback (most recent call last): + ... + ValueError: Only positive numbers are accepted + >>> euler_phi(-10) + Traceback (most recent call last): + ... + ValueError: Only positive numbers are accepted """ + if n <= 0: + raise ValueError("Only positive numbers are accepted") s = n for x in set(prime_factors(n)): s *= (x - 1) / x From 0b2c9fb6f164468b51baa4866c1b8c4f01ec8b64 Mon Sep 17 00:00:00 2001 From: Baron105 <76466796+Baron105@users.noreply.github.com> Date: Sat, 14 Oct 2023 12:31:23 +0530 Subject: [PATCH 348/757] Adding avg and mps speed formulae for ideal gases (#10229) * avg and mps speed formulae added * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * avg and mps speed formulae added * fixed_spacing * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * spacing * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * ws * added amicable numbers * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * added amicable numbers * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * spacing * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * removed * changed name of file and added code improvements * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * issues fixed due to pi * requested changes added --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- physics/speeds_of_gas_molecules.py | 111 +++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 physics/speeds_of_gas_molecules.py diff --git a/physics/speeds_of_gas_molecules.py b/physics/speeds_of_gas_molecules.py new file mode 100644 index 000000000000..a50d1c0f6d76 --- /dev/null +++ b/physics/speeds_of_gas_molecules.py @@ -0,0 +1,111 @@ +""" +The root-mean-square, average and most probable speeds of gas molecules are +derived from the Maxwell-Boltzmann distribution. The Maxwell-Boltzmann +distribution is a probability distribution that describes the distribution of +speeds of particles in an ideal gas. + +The distribution is given by the following equation: + + ------------------------------------------------- + | f(v) = (M/2πRT)^(3/2) * 4πv^2 * e^(-Mv^2/2RT) | + ------------------------------------------------- + +where: + f(v) is the fraction of molecules with a speed v + M is the molar mass of the gas in kg/mol + R is the gas constant + T is the absolute temperature + +More information about the Maxwell-Boltzmann distribution can be found here: +https://en.wikipedia.org/wiki/Maxwell%E2%80%93Boltzmann_distribution + +The average speed can be calculated by integrating the Maxwell-Boltzmann distribution +from 0 to infinity and dividing by the total number of molecules. The result is: + + --------------------- + | vavg = √(8RT/πM) | + --------------------- + +The most probable speed is the speed at which the Maxwell-Boltzmann distribution +is at its maximum. This can be found by differentiating the Maxwell-Boltzmann +distribution with respect to v and setting the result equal to zero. The result is: + + --------------------- + | vmp = √(2RT/M) | + --------------------- + +The root-mean-square speed is another measure of the average speed +of the molecules in a gas. It is calculated by taking the square root +of the average of the squares of the speeds of the molecules. The result is: + + --------------------- + | vrms = √(3RT/M) | + --------------------- + +Here we have defined functions to calculate the average and +most probable speeds of molecules in a gas given the +temperature and molar mass of the gas. +""" + +# import the constants R and pi from the scipy.constants library +from scipy.constants import R, pi + + +def avg_speed_of_molecule(temperature: float, molar_mass: float) -> float: + """ + Takes the temperature (in K) and molar mass (in kg/mol) of a gas + and returns the average speed of a molecule in the gas (in m/s). + + Examples: + >>> avg_speed_of_molecule(273, 0.028) # nitrogen at 273 K + 454.3488755020387 + >>> avg_speed_of_molecule(300, 0.032) # oxygen at 300 K + 445.52572733919885 + >>> avg_speed_of_molecule(-273, 0.028) # invalid temperature + Traceback (most recent call last): + ... + Exception: Absolute temperature cannot be less than 0 K + >>> avg_speed_of_molecule(273, 0) # invalid molar mass + Traceback (most recent call last): + ... + Exception: Molar mass should be greater than 0 kg/mol + """ + + if temperature < 0: + raise Exception("Absolute temperature cannot be less than 0 K") + if molar_mass <= 0: + raise Exception("Molar mass should be greater than 0 kg/mol") + return (8 * R * temperature / (pi * molar_mass)) ** 0.5 + + +def mps_speed_of_molecule(temperature: float, molar_mass: float) -> float: + """ + Takes the temperature (in K) and molar mass (in kg/mol) of a gas + and returns the most probable speed of a molecule in the gas (in m/s). + + Examples: + >>> mps_speed_of_molecule(273, 0.028) # nitrogen at 273 K + 402.65620701908966 + >>> mps_speed_of_molecule(300, 0.032) # oxygen at 300 K + 394.836895549922 + >>> mps_speed_of_molecule(-273, 0.028) # invalid temperature + Traceback (most recent call last): + ... + Exception: Absolute temperature cannot be less than 0 K + >>> mps_speed_of_molecule(273, 0) # invalid molar mass + Traceback (most recent call last): + ... + Exception: Molar mass should be greater than 0 kg/mol + """ + + if temperature < 0: + raise Exception("Absolute temperature cannot be less than 0 K") + if molar_mass <= 0: + raise Exception("Molar mass should be greater than 0 kg/mol") + return (2 * R * temperature / molar_mass) ** 0.5 + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 37cae3f56169348e97262b1b8f7671785be77a5b Mon Sep 17 00:00:00 2001 From: Muhammad Umer Farooq <115654418+Muhammadummerr@users.noreply.github.com> Date: Sat, 14 Oct 2023 13:31:43 +0500 Subject: [PATCH 349/757] Updated test cases of power_sum.py (#9978) * Updated test cases of power_sum.py * updated * updated. * remove extra comment and used ** instead of pow * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update backtracking/power_sum.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Tianyi Zheng --- backtracking/power_sum.py | 42 +++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/backtracking/power_sum.py b/backtracking/power_sum.py index fcf1429f8570..ee2eac426ec7 100644 --- a/backtracking/power_sum.py +++ b/backtracking/power_sum.py @@ -6,8 +6,6 @@ The only solution is 2^2+3^2. Constraints: 1<=X<=1000, 2<=N<=10. """ -from math import pow - def backtrack( needed_sum: int, @@ -19,25 +17,25 @@ def backtrack( """ >>> backtrack(13, 2, 1, 0, 0) (0, 1) - >>> backtrack(100, 2, 1, 0, 0) - (0, 3) - >>> backtrack(100, 3, 1, 0, 0) + >>> backtrack(10, 2, 1, 0, 0) + (0, 1) + >>> backtrack(10, 3, 1, 0, 0) + (0, 0) + >>> backtrack(20, 2, 1, 0, 0) (0, 1) - >>> backtrack(800, 2, 1, 0, 0) - (0, 561) - >>> backtrack(1000, 10, 1, 0, 0) + >>> backtrack(15, 10, 1, 0, 0) (0, 0) - >>> backtrack(400, 2, 1, 0, 0) - (0, 55) - >>> backtrack(50, 1, 1, 0, 0) - (0, 3658) + >>> backtrack(16, 2, 1, 0, 0) + (0, 1) + >>> backtrack(20, 1, 1, 0, 0) + (0, 64) """ if current_sum == needed_sum: # If the sum of the powers is equal to needed_sum, then we have a solution. solutions_count += 1 return current_sum, solutions_count - i_to_n = int(pow(current_number, power)) + i_to_n = current_number**power if current_sum + i_to_n <= needed_sum: # If the sum of the powers is less than needed_sum, then continue adding powers. current_sum += i_to_n @@ -57,17 +55,17 @@ def solve(needed_sum: int, power: int) -> int: """ >>> solve(13, 2) 1 - >>> solve(100, 2) - 3 - >>> solve(100, 3) + >>> solve(10, 2) 1 - >>> solve(800, 2) - 561 - >>> solve(1000, 10) + >>> solve(10, 3) 0 - >>> solve(400, 2) - 55 - >>> solve(50, 1) + >>> solve(20, 2) + 1 + >>> solve(15, 10) + 0 + >>> solve(16, 2) + 1 + >>> solve(20, 1) Traceback (most recent call last): ... ValueError: Invalid input From 71b372f5e2fd313268018df237d401efd7795464 Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Sat, 14 Oct 2023 09:34:05 -0400 Subject: [PATCH 350/757] Remove doctest in `xgboost_regressor.py` main function (#10422) * updating DIRECTORY.md * updating DIRECTORY.md * updating DIRECTORY.md * updating DIRECTORY.md * Update xgboost_regressor.py --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- machine_learning/xgboost_regressor.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/machine_learning/xgboost_regressor.py b/machine_learning/xgboost_regressor.py index a540e3ab03eb..52e041c55ea2 100644 --- a/machine_learning/xgboost_regressor.py +++ b/machine_learning/xgboost_regressor.py @@ -39,13 +39,13 @@ def xgboost( def main() -> None: """ - >>> main() - Mean Absolute Error : 0.30957163379906033 - Mean Square Error : 0.22611560196662744 - The URL for this algorithm https://xgboost.readthedocs.io/en/stable/ California house price dataset is used to demonstrate the algorithm. + + Expected error values: + Mean Absolute Error: 0.30957163379906033 + Mean Square Error: 0.22611560196662744 """ # Load California house price dataset california = fetch_california_housing() @@ -55,8 +55,8 @@ def main() -> None: ) predictions = xgboost(x_train, y_train, x_test) # Error printing - print(f"Mean Absolute Error : {mean_absolute_error(y_test, predictions)}") - print(f"Mean Square Error : {mean_squared_error(y_test, predictions)}") + print(f"Mean Absolute Error: {mean_absolute_error(y_test, predictions)}") + print(f"Mean Square Error: {mean_squared_error(y_test, predictions)}") if __name__ == "__main__": From 212cdfe36c3599804027c79c26ee814e53a12703 Mon Sep 17 00:00:00 2001 From: Dean Bring Date: Sat, 14 Oct 2023 08:35:12 -0700 Subject: [PATCH 351/757] Added validate sudoku board function (#9881) * Added algorithm to deeply clone a graph * Fixed file name and removed a function call * Removed nested function and fixed class parameter types * Fixed doctests * bug fix * Added class decorator * Updated doctests and fixed precommit errors * Cleaned up code * Simplified doctest * Added doctests * Code simplification * Created function which validates sudoku boards * Update matrix/validate_sudoku_board.py * Fixed precommit errors * Removed file accidentally included * Improved readability and simplicity * Add timeit benchmarks * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update validate_sudoku_board.py --------- Co-authored-by: Christian Clauss Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- matrix/validate_sudoku_board.py | 107 ++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 matrix/validate_sudoku_board.py diff --git a/matrix/validate_sudoku_board.py b/matrix/validate_sudoku_board.py new file mode 100644 index 000000000000..0ee7b3df0b83 --- /dev/null +++ b/matrix/validate_sudoku_board.py @@ -0,0 +1,107 @@ +""" +LeetCode 36. Valid Sudoku +https://leetcode.com/problems/valid-sudoku/ +https://en.wikipedia.org/wiki/Sudoku + +Determine if a 9 x 9 Sudoku board is valid. Only the filled cells need to be +validated according to the following rules: + +- Each row must contain the digits 1-9 without repetition. +- Each column must contain the digits 1-9 without repetition. +- Each of the nine 3 x 3 sub-boxes of the grid must contain the digits 1-9 + without repetition. + +Note: + +A Sudoku board (partially filled) could be valid but is not necessarily +solvable. + +Only the filled cells need to be validated according to the mentioned rules. +""" + +from collections import defaultdict + +NUM_SQUARES = 9 +EMPTY_CELL = "." + + +def is_valid_sudoku_board(sudoku_board: list[list[str]]) -> bool: + """ + This function validates (but does not solve) a sudoku board. + The board may be valid but unsolvable. + + >>> is_valid_sudoku_board([ + ... ["5","3",".",".","7",".",".",".","."] + ... ,["6",".",".","1","9","5",".",".","."] + ... ,[".","9","8",".",".",".",".","6","."] + ... ,["8",".",".",".","6",".",".",".","3"] + ... ,["4",".",".","8",".","3",".",".","1"] + ... ,["7",".",".",".","2",".",".",".","6"] + ... ,[".","6",".",".",".",".","2","8","."] + ... ,[".",".",".","4","1","9",".",".","5"] + ... ,[".",".",".",".","8",".",".","7","9"] + ... ]) + True + >>> is_valid_sudoku_board([ + ... ["8","3",".",".","7",".",".",".","."] + ... ,["6",".",".","1","9","5",".",".","."] + ... ,[".","9","8",".",".",".",".","6","."] + ... ,["8",".",".",".","6",".",".",".","3"] + ... ,["4",".",".","8",".","3",".",".","1"] + ... ,["7",".",".",".","2",".",".",".","6"] + ... ,[".","6",".",".",".",".","2","8","."] + ... ,[".",".",".","4","1","9",".",".","5"] + ... ,[".",".",".",".","8",".",".","7","9"] + ... ]) + False + >>> is_valid_sudoku_board([["1", "2", "3", "4", "5", "6", "7", "8", "9"]]) + Traceback (most recent call last): + ... + ValueError: Sudoku boards must be 9x9 squares. + >>> is_valid_sudoku_board( + ... [["1"], ["2"], ["3"], ["4"], ["5"], ["6"], ["7"], ["8"], ["9"]] + ... ) + Traceback (most recent call last): + ... + ValueError: Sudoku boards must be 9x9 squares. + """ + if len(sudoku_board) != NUM_SQUARES or ( + any(len(row) != NUM_SQUARES for row in sudoku_board) + ): + error_message = f"Sudoku boards must be {NUM_SQUARES}x{NUM_SQUARES} squares." + raise ValueError(error_message) + + row_values: defaultdict[int, set[str]] = defaultdict(set) + col_values: defaultdict[int, set[str]] = defaultdict(set) + box_values: defaultdict[tuple[int, int], set[str]] = defaultdict(set) + + for row in range(NUM_SQUARES): + for col in range(NUM_SQUARES): + value = sudoku_board[row][col] + + if value == EMPTY_CELL: + continue + + box = (row // 3, col // 3) + + if ( + value in row_values[row] + or value in col_values[col] + or value in box_values[box] + ): + return False + + row_values[row].add(value) + col_values[col].add(value) + box_values[box].add(value) + + return True + + +if __name__ == "__main__": + from doctest import testmod + from timeit import timeit + + testmod() + print(timeit("is_valid_sudoku_board(valid_board)", globals=globals())) + print(timeit("is_valid_sudoku_board(invalid_board)", globals=globals())) From 3ba23384794bc5ce61a300b96d2b721d9d58eccd Mon Sep 17 00:00:00 2001 From: Aakash Giri Date: Sat, 14 Oct 2023 21:47:11 +0530 Subject: [PATCH 352/757] Add Title Case Conversion (#10439) [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci added more test case and type hint [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci updated naming convention --- strings/title.py | 57 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 strings/title.py diff --git a/strings/title.py b/strings/title.py new file mode 100644 index 000000000000..1ec2df548e2d --- /dev/null +++ b/strings/title.py @@ -0,0 +1,57 @@ +def to_title_case(word: str) -> str: + """ + Converts a string to capitalized case, preserving the input as is + + >>> to_title_case("Aakash") + 'Aakash' + + >>> to_title_case("aakash") + 'Aakash' + + >>> to_title_case("AAKASH") + 'Aakash' + + >>> to_title_case("aAkAsH") + 'Aakash' + """ + + """ + Convert the first character to uppercase if it's lowercase + """ + if "a" <= word[0] <= "z": + word = chr(ord(word[0]) - 32) + word[1:] + + """ + Convert the remaining characters to lowercase if they are uppercase + """ + for i in range(1, len(word)): + if "A" <= word[i] <= "Z": + word = word[:i] + chr(ord(word[i]) + 32) + word[i + 1 :] + + return word + + +def sentence_to_title_case(input_str: str) -> str: + """ + Converts a string to title case, preserving the input as is + + >>> sentence_to_title_case("Aakash Giri") + 'Aakash Giri' + + >>> sentence_to_title_case("aakash giri") + 'Aakash Giri' + + >>> sentence_to_title_case("AAKASH GIRI") + 'Aakash Giri' + + >>> sentence_to_title_case("aAkAsH gIrI") + 'Aakash Giri' + """ + + return " ".join(to_title_case(word) for word in input_str.split()) + + +if __name__ == "__main__": + from doctest import testmod + + testmod() From 1969259868451684ab05663cc208f06af20d483f Mon Sep 17 00:00:00 2001 From: Manpreet Singh <63737630+ManpreetSingh2004@users.noreply.github.com> Date: Sat, 14 Oct 2023 23:05:01 +0530 Subject: [PATCH 353/757] Performance: 80% faster Project Euler 145 (#10445) * Performance: 80% faster Project Euler145 * Added timeit benchmark * >>> slow_solution() doctest --- project_euler/problem_145/sol1.py | 70 +++++++++++++++++++++++++++---- 1 file changed, 63 insertions(+), 7 deletions(-) diff --git a/project_euler/problem_145/sol1.py b/project_euler/problem_145/sol1.py index e9fc1a199161..71b851178fdb 100644 --- a/project_euler/problem_145/sol1.py +++ b/project_euler/problem_145/sol1.py @@ -17,17 +17,17 @@ ODD_DIGITS = [1, 3, 5, 7, 9] -def reversible_numbers( +def slow_reversible_numbers( remaining_length: int, remainder: int, digits: list[int], length: int ) -> int: """ Count the number of reversible numbers of given length. Iterate over possible digits considering parity of current sum remainder. - >>> reversible_numbers(1, 0, [0], 1) + >>> slow_reversible_numbers(1, 0, [0], 1) 0 - >>> reversible_numbers(2, 0, [0] * 2, 2) + >>> slow_reversible_numbers(2, 0, [0] * 2, 2) 20 - >>> reversible_numbers(3, 0, [0] * 3, 3) + >>> slow_reversible_numbers(3, 0, [0] * 3, 3) 100 """ if remaining_length == 0: @@ -51,7 +51,7 @@ def reversible_numbers( result = 0 for digit in range(10): digits[length // 2] = digit - result += reversible_numbers( + result += slow_reversible_numbers( 0, (remainder + 2 * digit) // 10, digits, length ) return result @@ -67,7 +67,7 @@ def reversible_numbers( for digit2 in other_parity_digits: digits[(length - remaining_length) // 2] = digit2 - result += reversible_numbers( + result += slow_reversible_numbers( remaining_length - 2, (remainder + digit1 + digit2) // 10, digits, @@ -76,6 +76,42 @@ def reversible_numbers( return result +def slow_solution(max_power: int = 9) -> int: + """ + To evaluate the solution, use solution() + >>> slow_solution(3) + 120 + >>> slow_solution(6) + 18720 + >>> slow_solution(7) + 68720 + """ + result = 0 + for length in range(1, max_power + 1): + result += slow_reversible_numbers(length, 0, [0] * length, length) + return result + + +def reversible_numbers( + remaining_length: int, remainder: int, digits: list[int], length: int +) -> int: + """ + Count the number of reversible numbers of given length. + Iterate over possible digits considering parity of current sum remainder. + >>> reversible_numbers(1, 0, [0], 1) + 0 + >>> reversible_numbers(2, 0, [0] * 2, 2) + 20 + >>> reversible_numbers(3, 0, [0] * 3, 3) + 100 + """ + # There exist no reversible 1, 5, 9, 13 (ie. 4k+1) digit numbers + if (length - 1) % 4 == 0: + return 0 + + return slow_reversible_numbers(length, 0, [0] * length, length) + + def solution(max_power: int = 9) -> int: """ To evaluate the solution, use solution() @@ -92,5 +128,25 @@ def solution(max_power: int = 9) -> int: return result +def benchmark() -> None: + """ + Benchmarks + """ + # Running performance benchmarks... + # slow_solution : 292.9300301000003 + # solution : 54.90970860000016 + + from timeit import timeit + + print("Running performance benchmarks...") + + print(f"slow_solution : {timeit('slow_solution()', globals=globals(), number=10)}") + print(f"solution : {timeit('solution()', globals=globals(), number=10)}") + + if __name__ == "__main__": - print(f"{solution() = }") + print(f"Solution : {solution()}") + benchmark() + + # for i in range(1, 15): + # print(f"{i}. {reversible_numbers(i, 0, [0]*i, i)}") From f968dda5e9b81bd7dd3c5e9b7a69a9a08ed3ead7 Mon Sep 17 00:00:00 2001 From: Saurabh Mahapatra <98408932+its-100rabh@users.noreply.github.com> Date: Sun, 15 Oct 2023 00:32:37 +0530 Subject: [PATCH 354/757] Updated Comments on upper.py (#10442) * Updated Comments on upper.py * Update upper.py * Update upper.py --------- Co-authored-by: Christian Clauss --- strings/upper.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/strings/upper.py b/strings/upper.py index 5edd40b79808..0f68a27b99c6 100644 --- a/strings/upper.py +++ b/strings/upper.py @@ -1,6 +1,8 @@ def upper(word: str) -> str: """ - Will convert the entire string to uppercase letters + Convert an entire string to ASCII uppercase letters by looking for lowercase ASCII + letters and subtracting 32 from their integer representation to get the uppercase + letter. >>> upper("wow") 'WOW' @@ -11,10 +13,6 @@ def upper(word: str) -> str: >>> upper("wh[]32") 'WH[]32' """ - - # Converting to ascii value int value and checking to see if char is a lower letter - # if it is a lowercase letter it is getting shift by 32 which makes it an uppercase - # case letter return "".join(chr(ord(char) - 32) if "a" <= char <= "z" else char for char in word) From c9ba5e1b6f319e34815660542d8ca0c777c8008a Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Sat, 14 Oct 2023 16:08:52 -0400 Subject: [PATCH 355/757] Disable unused dependencies (#10467) Comment out dependencies in requirements.txt that are only used by currently-disabled files --- requirements.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/requirements.txt b/requirements.txt index 25dba6f5a250..1e64818bbb6a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,19 +1,19 @@ beautifulsoup4 fake_useragent imageio -keras +keras ; python_version < '3.12' lxml matplotlib numpy opencv-python pandas pillow -projectq +# projectq # uncomment once quantum/quantum_random.py is fixed qiskit ; python_version < '3.12' qiskit-aer ; python_version < '3.12' requests rich -scikit-fuzzy +# scikit-fuzzy # uncomment once fuzzy_logic/fuzzy_operations.py is fixed scikit-learn statsmodels sympy @@ -21,4 +21,4 @@ tensorflow ; python_version < '3.12' texttable tweepy xgboost -yulewalker +# yulewalker # uncomment once audio_filters/equal_loudness_filter.py is fixed From 3ecad36f92d26676cc73276553cd99763b025b33 Mon Sep 17 00:00:00 2001 From: Manpreet Singh <63737630+ManpreetSingh2004@users.noreply.github.com> Date: Sun, 15 Oct 2023 10:15:44 +0530 Subject: [PATCH 356/757] fix: incorrect range detection in find_missing_number (#10361) * Fix incorrect range detection in find_missing_number * Support consecutive decreasing numbers Added support for consecutive decreasing numbers in the find_missing_number function. * Support unordered numbers --- bit_manipulation/missing_number.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/bit_manipulation/missing_number.py b/bit_manipulation/missing_number.py index 92502a778ace..32b949daa717 100644 --- a/bit_manipulation/missing_number.py +++ b/bit_manipulation/missing_number.py @@ -11,11 +11,18 @@ def find_missing_number(nums: list[int]) -> int: Example: >>> find_missing_number([0, 1, 3, 4]) 2 + >>> find_missing_number([1, 3, 4, 5, 6]) + 2 + >>> find_missing_number([6, 5, 4, 2, 1]) + 3 + >>> find_missing_number([6, 1, 5, 3, 4]) + 2 """ - n = len(nums) - missing_number = n + low = min(nums) + high = max(nums) + missing_number = high - for i in range(n): - missing_number ^= i ^ nums[i] + for i in range(low, high): + missing_number ^= i ^ nums[i - low] return missing_number From 7dbc30181826aa26600f8d24c92b1587b31677c6 Mon Sep 17 00:00:00 2001 From: Ravi Kumar <119737193+ravi-ivar-7@users.noreply.github.com> Date: Sun, 15 Oct 2023 14:37:29 +0530 Subject: [PATCH 357/757] added rkf45 method (#10438) * added rkf45 method * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Updated rkf45.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Updated rkf45.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update rkf45.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update rkf45.py with suggestions * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Improved Code Quality rkf45.py * Added more test cases and exception rkf45.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update rkf45.py * corrected some spellings. rkf45.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update rkf45.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update rkf45.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- maths/rkf45.py | 112 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 maths/rkf45.py diff --git a/maths/rkf45.py b/maths/rkf45.py new file mode 100644 index 000000000000..29fd447b61b8 --- /dev/null +++ b/maths/rkf45.py @@ -0,0 +1,112 @@ +""" +Use the Runge-Kutta-Fehlberg method to solve Ordinary Differential Equations. +""" + +from collections.abc import Callable + +import numpy as np + + +def runge_futta_fehlberg_45( + func: Callable, + x_initial: float, + y_initial: float, + step_size: float, + x_final: float, +) -> np.ndarray: + """ + Solve an Ordinary Differential Equations using Runge-Kutta-Fehlberg Method (rkf45) + of order 5. + + https://en.wikipedia.org/wiki/Runge%E2%80%93Kutta%E2%80%93Fehlberg_method + + args: + func: An ordinary differential equation (ODE) as function of x and y. + x_initial: The initial value of x. + y_initial: The initial value of y. + step_size: The increment value of x. + x_final: The final value of x. + + Returns: + Solution of y at each nodal point + + # exact value of y[1] is tan(0.2) = 0.2027100937470787 + >>> def f(x, y): + ... return 1 + y**2 + >>> y = runge_futta_fehlberg_45(f, 0, 0, 0.2, 1) + >>> y[1] + 0.2027100937470787 + >>> def f(x,y): + ... return x + >>> y = runge_futta_fehlberg_45(f, -1, 0, 0.2, 0) + >>> y[1] + -0.18000000000000002 + >>> y = runge_futta_fehlberg_45(5, 0, 0, 0.1, 1) + Traceback (most recent call last): + ... + TypeError: 'int' object is not callable + >>> def f(x, y): + ... return x + y + >>> y = runge_futta_fehlberg_45(f, 0, 0, 0.2, -1) + Traceback (most recent call last): + ... + ValueError: The final value x must be greater than initial value of x. + >>> def f(x, y): + ... return x + >>> y = runge_futta_fehlberg_45(f, -1, 0, -0.2, 0) + Traceback (most recent call last): + ... + ValueError: Step size must be positive. + """ + if x_initial >= x_final: + raise ValueError("The final value x must be greater than initial value of x.") + + if step_size <= 0: + raise ValueError("Step size must be positive.") + + n = int((x_final - x_initial) / step_size) + y = np.zeros( + (n + 1), + ) + x = np.zeros(n + 1) + y[0] = y_initial + x[0] = x_initial + for i in range(n): + k1 = step_size * func(x[i], y[i]) + k2 = step_size * func(x[i] + step_size / 4, y[i] + k1 / 4) + k3 = step_size * func( + x[i] + (3 / 8) * step_size, y[i] + (3 / 32) * k1 + (9 / 32) * k2 + ) + k4 = step_size * func( + x[i] + (12 / 13) * step_size, + y[i] + (1932 / 2197) * k1 - (7200 / 2197) * k2 + (7296 / 2197) * k3, + ) + k5 = step_size * func( + x[i] + step_size, + y[i] + (439 / 216) * k1 - 8 * k2 + (3680 / 513) * k3 - (845 / 4104) * k4, + ) + k6 = step_size * func( + x[i] + step_size / 2, + y[i] + - (8 / 27) * k1 + + 2 * k2 + - (3544 / 2565) * k3 + + (1859 / 4104) * k4 + - (11 / 40) * k5, + ) + y[i + 1] = ( + y[i] + + (16 / 135) * k1 + + (6656 / 12825) * k3 + + (28561 / 56430) * k4 + - (9 / 50) * k5 + + (2 / 55) * k6 + ) + x[i + 1] = step_size + x[i] + return y + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 1ebae5d43e2ce23ef98a0804bf1fa077d2fa5daf Mon Sep 17 00:00:00 2001 From: Manpreet Singh <63737630+ManpreetSingh2004@users.noreply.github.com> Date: Sun, 15 Oct 2023 14:47:22 +0530 Subject: [PATCH 358/757] Performance: 75% faster Project Euler 187 (#10503) * Add comments and wikipedia link in calculate_prime_numbers * Add improved calculate_prime_numbers * Separate slow_solution and new_solution * Use for loops in solution * Separate while_solution and new solution * Add performance benchmark * Add doctest for calculate_prime_numbers * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Removed white space --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- project_euler/problem_187/sol1.py | 118 ++++++++++++++++++++++++++++-- 1 file changed, 111 insertions(+), 7 deletions(-) diff --git a/project_euler/problem_187/sol1.py b/project_euler/problem_187/sol1.py index 12f03e2a7023..8944776fef50 100644 --- a/project_euler/problem_187/sol1.py +++ b/project_euler/problem_187/sol1.py @@ -14,29 +14,89 @@ from math import isqrt -def calculate_prime_numbers(max_number: int) -> list[int]: +def slow_calculate_prime_numbers(max_number: int) -> list[int]: """ - Returns prime numbers below max_number + Returns prime numbers below max_number. + See: https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes - >>> calculate_prime_numbers(10) + >>> slow_calculate_prime_numbers(10) [2, 3, 5, 7] + + >>> slow_calculate_prime_numbers(2) + [] """ + # List containing a bool value for every number below max_number/2 is_prime = [True] * max_number + for i in range(2, isqrt(max_number - 1) + 1): if is_prime[i]: + # Mark all multiple of i as not prime for j in range(i**2, max_number, i): is_prime[j] = False return [i for i in range(2, max_number) if is_prime[i]] -def solution(max_number: int = 10**8) -> int: +def calculate_prime_numbers(max_number: int) -> list[int]: + """ + Returns prime numbers below max_number. + See: https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes + + >>> calculate_prime_numbers(10) + [2, 3, 5, 7] + + >>> calculate_prime_numbers(2) + [] + """ + + if max_number <= 2: + return [] + + # List containing a bool value for every odd number below max_number/2 + is_prime = [True] * (max_number // 2) + + for i in range(3, isqrt(max_number - 1) + 1, 2): + if is_prime[i // 2]: + # Mark all multiple of i as not prime using list slicing + is_prime[i**2 // 2 :: i] = [False] * ( + # Same as: (max_number - (i**2)) // (2 * i) + 1 + # but faster than len(is_prime[i**2 // 2 :: i]) + len(range(i**2 // 2, max_number // 2, i)) + ) + + return [2] + [2 * i + 1 for i in range(1, max_number // 2) if is_prime[i]] + + +def slow_solution(max_number: int = 10**8) -> int: """ Returns the number of composite integers below max_number have precisely two, - not necessarily distinct, prime factors + not necessarily distinct, prime factors. - >>> solution(30) + >>> slow_solution(30) + 10 + """ + + prime_numbers = slow_calculate_prime_numbers(max_number // 2) + + semiprimes_count = 0 + left = 0 + right = len(prime_numbers) - 1 + while left <= right: + while prime_numbers[left] * prime_numbers[right] >= max_number: + right -= 1 + semiprimes_count += right - left + 1 + left += 1 + + return semiprimes_count + + +def while_solution(max_number: int = 10**8) -> int: + """ + Returns the number of composite integers below max_number have precisely two, + not necessarily distinct, prime factors. + + >>> while_solution(30) 10 """ @@ -54,5 +114,49 @@ def solution(max_number: int = 10**8) -> int: return semiprimes_count +def solution(max_number: int = 10**8) -> int: + """ + Returns the number of composite integers below max_number have precisely two, + not necessarily distinct, prime factors. + + >>> solution(30) + 10 + """ + + prime_numbers = calculate_prime_numbers(max_number // 2) + + semiprimes_count = 0 + right = len(prime_numbers) - 1 + for left in range(len(prime_numbers)): + if left > right: + break + for r in range(right, left - 2, -1): + if prime_numbers[left] * prime_numbers[r] < max_number: + break + right = r + semiprimes_count += right - left + 1 + + return semiprimes_count + + +def benchmark() -> None: + """ + Benchmarks + """ + # Running performance benchmarks... + # slow_solution : 108.50874730000032 + # while_sol : 28.09581200000048 + # solution : 25.063097400000515 + + from timeit import timeit + + print("Running performance benchmarks...") + + print(f"slow_solution : {timeit('slow_solution()', globals=globals(), number=10)}") + print(f"while_sol : {timeit('while_solution()', globals=globals(), number=10)}") + print(f"solution : {timeit('solution()', globals=globals(), number=10)}") + + if __name__ == "__main__": - print(f"{solution() = }") + print(f"Solution: {solution()}") + benchmark() From 85cdb93a0d7a306633faa03a134d0d39da7076a8 Mon Sep 17 00:00:00 2001 From: Kosuri L Indu <118645569+kosuri-indu@users.noreply.github.com> Date: Sun, 15 Oct 2023 15:48:28 +0530 Subject: [PATCH 359/757] [Add] : Job Sequence program under GREEDY methods (#10482) * to add job seq program * to add job seq program * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * to add definitions in parameters * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * to add definitions in parameters * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * to add definitions in parameters * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * changes as recommended * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * type hint error resolved * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * removed lambda * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * import stmts order * Update and rename job_sequence.py to job_sequence_with_deadline.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- scheduling/job_sequence_with_deadline.py | 62 ++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 scheduling/job_sequence_with_deadline.py diff --git a/scheduling/job_sequence_with_deadline.py b/scheduling/job_sequence_with_deadline.py new file mode 100644 index 000000000000..fccb49cd88e8 --- /dev/null +++ b/scheduling/job_sequence_with_deadline.py @@ -0,0 +1,62 @@ +""" +Given a list of tasks, each with a deadline and reward, calculate which tasks can be +completed to yield the maximum reward. Each task takes one unit of time to complete, +and we can only work on one task at a time. Once a task has passed its deadline, it +can no longer be scheduled. + +Example : +tasks_info = [(4, 20), (1, 10), (1, 40), (1, 30)] +max_tasks will return (2, [2, 0]) - +Scheduling these tasks would result in a reward of 40 + 20 + +This problem can be solved using the concept of "GREEDY ALGORITHM". +Time Complexity - O(n log n) +https://medium.com/@nihardudhat2000/job-sequencing-with-deadline-17ddbb5890b5 +""" +from dataclasses import dataclass +from operator import attrgetter + + +@dataclass +class Task: + task_id: int + deadline: int + reward: int + + +def max_tasks(tasks_info: list[tuple[int, int]]) -> list[int]: + """ + Create a list of Task objects that are sorted so the highest rewards come first. + Return a list of those task ids that can be completed before i becomes too high. + >>> max_tasks([(4, 20), (1, 10), (1, 40), (1, 30)]) + [2, 0] + >>> max_tasks([(1, 10), (2, 20), (3, 30), (2, 40)]) + [3, 2] + >>> max_tasks([(9, 10)]) + [0] + >>> max_tasks([(-9, 10)]) + [] + >>> max_tasks([]) + [] + >>> max_tasks([(0, 10), (0, 20), (0, 30), (0, 40)]) + [] + >>> max_tasks([(-1, 10), (-2, 20), (-3, 30), (-4, 40)]) + [] + """ + tasks = sorted( + ( + Task(task_id, deadline, reward) + for task_id, (deadline, reward) in enumerate(tasks_info) + ), + key=attrgetter("reward"), + reverse=True, + ) + return [task.task_id for i, task in enumerate(tasks, start=1) if task.deadline >= i] + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + print(f"{max_tasks([(4, 20), (1, 10), (1, 40), (1, 30)]) = }") + print(f"{max_tasks([(1, 10), (2, 20), (3, 30), (2, 40)]) = }") From 777eca813a8030e7a674072c79da144e92dde07a Mon Sep 17 00:00:00 2001 From: Ravi Kumar <119737193+ravi-ivar-7@users.noreply.github.com> Date: Sun, 15 Oct 2023 16:25:56 +0530 Subject: [PATCH 360/757] Corrected typo in function name and doctests. rkf45.py (#10518) * Corrected typo in function name and doctests. rkf45.py There was a mistake in name of function (runge_futta_fehlberg instead of runge_kutta_fehlberg) . I have corrected this in function name and also doctest. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Rename rkf45.py to runge_kutta_fehlberg_45.py * Update runge_kutta_fehlberg_45.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- maths/{rkf45.py => runge_kutta_fehlberg_45.py} | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) rename maths/{rkf45.py => runge_kutta_fehlberg_45.py} (84%) diff --git a/maths/rkf45.py b/maths/runge_kutta_fehlberg_45.py similarity index 84% rename from maths/rkf45.py rename to maths/runge_kutta_fehlberg_45.py index 29fd447b61b8..8181fe3015fc 100644 --- a/maths/rkf45.py +++ b/maths/runge_kutta_fehlberg_45.py @@ -7,7 +7,7 @@ import numpy as np -def runge_futta_fehlberg_45( +def runge_kutta_fehlberg_45( func: Callable, x_initial: float, y_initial: float, @@ -33,33 +33,35 @@ def runge_futta_fehlberg_45( # exact value of y[1] is tan(0.2) = 0.2027100937470787 >>> def f(x, y): ... return 1 + y**2 - >>> y = runge_futta_fehlberg_45(f, 0, 0, 0.2, 1) + >>> y = runge_kutta_fehlberg_45(f, 0, 0, 0.2, 1) >>> y[1] 0.2027100937470787 >>> def f(x,y): ... return x - >>> y = runge_futta_fehlberg_45(f, -1, 0, 0.2, 0) + >>> y = runge_kutta_fehlberg_45(f, -1, 0, 0.2, 0) >>> y[1] -0.18000000000000002 - >>> y = runge_futta_fehlberg_45(5, 0, 0, 0.1, 1) + >>> y = runge_kutta_fehlberg_45(5, 0, 0, 0.1, 1) Traceback (most recent call last): ... TypeError: 'int' object is not callable >>> def f(x, y): ... return x + y - >>> y = runge_futta_fehlberg_45(f, 0, 0, 0.2, -1) + >>> y = runge_kutta_fehlberg_45(f, 0, 0, 0.2, -1) Traceback (most recent call last): ... - ValueError: The final value x must be greater than initial value of x. + ValueError: The final value of x must be greater than initial value of x. >>> def f(x, y): ... return x - >>> y = runge_futta_fehlberg_45(f, -1, 0, -0.2, 0) + >>> y = runge_kutta_fehlberg_45(f, -1, 0, -0.2, 0) Traceback (most recent call last): ... ValueError: Step size must be positive. """ if x_initial >= x_final: - raise ValueError("The final value x must be greater than initial value of x.") + raise ValueError( + "The final value of x must be greater than initial value of x." + ) if step_size <= 0: raise ValueError("Step size must be positive.") From 79a91cca956b99acf5e4bd785ff0640c9e591b89 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Sun, 15 Oct 2023 16:57:08 +0200 Subject: [PATCH 361/757] Fix typo in filename: ciphers/trifid_cipher.py (#10516) * Update and rename trafid_cipher.py to trifid_cipher.py * updating DIRECTORY.md --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 14 +++++++++++++- ciphers/{trafid_cipher.py => trifid_cipher.py} | 0 2 files changed, 13 insertions(+), 1 deletion(-) rename ciphers/{trafid_cipher.py => trifid_cipher.py} (100%) diff --git a/DIRECTORY.md b/DIRECTORY.md index 2c6000c94ed4..ceee9972dd97 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -63,7 +63,9 @@ ## Boolean Algebra * [And Gate](boolean_algebra/and_gate.py) + * [Imply Gate](boolean_algebra/imply_gate.py) * [Nand Gate](boolean_algebra/nand_gate.py) + * [Nimply Gate](boolean_algebra/nimply_gate.py) * [Nor Gate](boolean_algebra/nor_gate.py) * [Not Gate](boolean_algebra/not_gate.py) * [Or Gate](boolean_algebra/or_gate.py) @@ -119,9 +121,9 @@ * [Shuffled Shift Cipher](ciphers/shuffled_shift_cipher.py) * [Simple Keyword Cypher](ciphers/simple_keyword_cypher.py) * [Simple Substitution Cipher](ciphers/simple_substitution_cipher.py) - * [Trafid Cipher](ciphers/trafid_cipher.py) * [Transposition Cipher](ciphers/transposition_cipher.py) * [Transposition Cipher Encrypt Decrypt File](ciphers/transposition_cipher_encrypt_decrypt_file.py) + * [Trifid Cipher](ciphers/trifid_cipher.py) * [Vigenere Cipher](ciphers/vigenere_cipher.py) * [Xor Cipher](ciphers/xor_cipher.py) @@ -174,7 +176,9 @@ ## Data Structures * Arrays * [Equilibrium Index In Array](data_structures/arrays/equilibrium_index_in_array.py) + * [Find Triplets With 0 Sum](data_structures/arrays/find_triplets_with_0_sum.py) * [Median Two Array](data_structures/arrays/median_two_array.py) + * [Pairs With Given Sum](data_structures/arrays/pairs_with_given_sum.py) * [Permutations](data_structures/arrays/permutations.py) * [Prefix Sum](data_structures/arrays/prefix_sum.py) * [Product Sum](data_structures/arrays/product_sum.py) @@ -385,6 +389,7 @@ ## Financial * [Equated Monthly Installments](financial/equated_monthly_installments.py) + * [Exponential Moving Average](financial/exponential_moving_average.py) * [Interest](financial/interest.py) * [Present Value](financial/present_value.py) * [Price Plus Tax](financial/price_plus_tax.py) @@ -670,6 +675,7 @@ * [Radians](maths/radians.py) * [Radix2 Fft](maths/radix2_fft.py) * [Remove Digit](maths/remove_digit.py) + * [Rkf45](maths/rkf45.py) * [Runge Kutta](maths/runge_kutta.py) * [Segmented Sieve](maths/segmented_sieve.py) * Series @@ -688,6 +694,7 @@ * [Sin](maths/sin.py) * [Sock Merchant](maths/sock_merchant.py) * [Softmax](maths/softmax.py) + * [Solovay Strassen Primality Test](maths/solovay_strassen_primality_test.py) * [Square Root](maths/square_root.py) * [Sum Of Arithmetic Series](maths/sum_of_arithmetic_series.py) * [Sum Of Digits](maths/sum_of_digits.py) @@ -728,6 +735,7 @@ * [Spiral Print](matrix/spiral_print.py) * Tests * [Test Matrix Operation](matrix/tests/test_matrix_operation.py) + * [Validate Sudoku Board](matrix/validate_sudoku_board.py) ## Networking Flow * [Ford Fulkerson](networking_flow/ford_fulkerson.py) @@ -803,6 +811,7 @@ * [Rms Speed Of Molecule](physics/rms_speed_of_molecule.py) * [Shear Stress](physics/shear_stress.py) * [Speed Of Sound](physics/speed_of_sound.py) + * [Speeds Of Gas Molecules](physics/speeds_of_gas_molecules.py) ## Project Euler * Problem 001 @@ -1106,6 +1115,7 @@ ## Scheduling * [First Come First Served](scheduling/first_come_first_served.py) * [Highest Response Ratio Next](scheduling/highest_response_ratio_next.py) + * [Job Sequence With Deadline](scheduling/job_sequence_with_deadline.py) * [Job Sequencing With Deadline](scheduling/job_sequencing_with_deadline.py) * [Multi Level Feedback Queue](scheduling/multi_level_feedback_queue.py) * [Non Preemptive Shortest Job First](scheduling/non_preemptive_shortest_job_first.py) @@ -1193,6 +1203,7 @@ * [Capitalize](strings/capitalize.py) * [Check Anagrams](strings/check_anagrams.py) * [Credit Card Validator](strings/credit_card_validator.py) + * [Damerau Levenshtein Distance](strings/damerau_levenshtein_distance.py) * [Detecting English Programmatically](strings/detecting_english_programmatically.py) * [Dna](strings/dna.py) * [Frequency Finder](strings/frequency_finder.py) @@ -1225,6 +1236,7 @@ * [String Switch Case](strings/string_switch_case.py) * [Strip](strings/strip.py) * [Text Justification](strings/text_justification.py) + * [Title](strings/title.py) * [Top K Frequent Words](strings/top_k_frequent_words.py) * [Upper](strings/upper.py) * [Wave](strings/wave.py) diff --git a/ciphers/trafid_cipher.py b/ciphers/trifid_cipher.py similarity index 100% rename from ciphers/trafid_cipher.py rename to ciphers/trifid_cipher.py From b5474ab68a0e1eea6bbfba445feca39db471c62f Mon Sep 17 00:00:00 2001 From: Rahul Jangra <106389897+leonado10000@users.noreply.github.com> Date: Sun, 15 Oct 2023 20:33:03 +0530 Subject: [PATCH 362/757] [ADD] : maths joint probabilty distribution (#10508) * Create joint_probability_distribution.py * Update joint_probability_distribution.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update joint_probability_distribution.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update joint_probability_distribution.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update joint_probability_distribution.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update joint_probability_distribution.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update joint_probability_distribution.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update joint_probability_distribution.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update joint_probability_distribution.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update joint_probability_distribution.py * Update joint_probability_distribution.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update joint_probability_distribution.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update joint_probability_distribution.py * Update joint_probability_distribution.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update maclaurin_series.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Revert changes to maclaurin_series.py * Revert changes to maclaurin_series.py * Update joint_probability_distribution.py * Update joint_probability_distribution.py * Update joint_probability_distribution.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- maths/joint_probability_distribution.py | 124 ++++++++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 maths/joint_probability_distribution.py diff --git a/maths/joint_probability_distribution.py b/maths/joint_probability_distribution.py new file mode 100644 index 000000000000..6fbcea40c358 --- /dev/null +++ b/maths/joint_probability_distribution.py @@ -0,0 +1,124 @@ +""" +Calculate joint probability distribution +https://en.wikipedia.org/wiki/Joint_probability_distribution +""" + + +def joint_probability_distribution( + x_values: list[int], + y_values: list[int], + x_probabilities: list[float], + y_probabilities: list[float], +) -> dict: + """ + >>> joint_distribution = joint_probability_distribution( + ... [1, 2], [-2, 5, 8], [0.7, 0.3], [0.3, 0.5, 0.2] + ... ) + >>> from math import isclose + >>> isclose(joint_distribution.pop((1, 8)), 0.14) + True + >>> joint_distribution + {(1, -2): 0.21, (1, 5): 0.35, (2, -2): 0.09, (2, 5): 0.15, (2, 8): 0.06} + """ + return { + (x, y): x_prob * y_prob + for x, x_prob in zip(x_values, x_probabilities) + for y, y_prob in zip(y_values, y_probabilities) + } + + +# Function to calculate the expectation (mean) +def expectation(values: list, probabilities: list) -> float: + """ + >>> from math import isclose + >>> isclose(expectation([1, 2], [0.7, 0.3]), 1.3) + True + """ + return sum(x * p for x, p in zip(values, probabilities)) + + +# Function to calculate the variance +def variance(values: list[int], probabilities: list[float]) -> float: + """ + >>> from math import isclose + >>> isclose(variance([1,2],[0.7,0.3]), 0.21) + True + """ + mean = expectation(values, probabilities) + return sum((x - mean) ** 2 * p for x, p in zip(values, probabilities)) + + +# Function to calculate the covariance +def covariance( + x_values: list[int], + y_values: list[int], + x_probabilities: list[float], + y_probabilities: list[float], +) -> float: + """ + >>> covariance([1, 2], [-2, 5, 8], [0.7, 0.3], [0.3, 0.5, 0.2]) + -2.7755575615628914e-17 + """ + mean_x = expectation(x_values, x_probabilities) + mean_y = expectation(y_values, y_probabilities) + return sum( + (x - mean_x) * (y - mean_y) * px * py + for x, px in zip(x_values, x_probabilities) + for y, py in zip(y_values, y_probabilities) + ) + + +# Function to calculate the standard deviation +def standard_deviation(variance: float) -> float: + """ + >>> standard_deviation(0.21) + 0.458257569495584 + """ + return variance**0.5 + + +if __name__ == "__main__": + from doctest import testmod + + testmod() + # Input values for X and Y + x_vals = input("Enter values of X separated by spaces: ").split() + y_vals = input("Enter values of Y separated by spaces: ").split() + + # Convert input values to integers + x_values = [int(x) for x in x_vals] + y_values = [int(y) for y in y_vals] + + # Input probabilities for X and Y + x_probs = input("Enter probabilities for X separated by spaces: ").split() + y_probs = input("Enter probabilities for Y separated by spaces: ").split() + assert len(x_values) == len(x_probs) + assert len(y_values) == len(y_probs) + + # Convert input probabilities to floats + x_probabilities = [float(p) for p in x_probs] + y_probabilities = [float(p) for p in y_probs] + + # Calculate the joint probability distribution + jpd = joint_probability_distribution( + x_values, y_values, x_probabilities, y_probabilities + ) + + # Print the joint probability distribution + print( + "\n".join( + f"P(X={x}, Y={y}) = {probability}" for (x, y), probability in jpd.items() + ) + ) + mean_xy = expectation( + [x * y for x in x_values for y in y_values], + [px * py for px in x_probabilities for py in y_probabilities], + ) + print(f"x mean: {expectation(x_values, x_probabilities) = }") + print(f"y mean: {expectation(y_values, y_probabilities) = }") + print(f"xy mean: {mean_xy}") + print(f"x: {variance(x_values, x_probabilities) = }") + print(f"y: {variance(y_values, y_probabilities) = }") + print(f"{covariance(x_values, y_values, x_probabilities, y_probabilities) = }") + print(f"x: {standard_deviation(variance(x_values, x_probabilities)) = }") + print(f"y: {standard_deviation(variance(y_values, y_probabilities)) = }") From 755659a62f2c976e1e359a4c0af576b2aa8843a8 Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Sun, 15 Oct 2023 11:16:56 -0400 Subject: [PATCH 363/757] Omit `project_euler/` from coverage reports (#10469) * Omit project_euler/ and scripts/ from coverage reports * Add scripts/ back into coverage reports --- pyproject.toml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index fe5f2f09c4ec..9c9262d77748 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -128,7 +128,10 @@ addopts = [ ] [tool.coverage.report] -omit = [".env/*"] +omit = [ + ".env/*", + "project_euler/*" +] sort = "Cover" [tool.codespell] From 52040a7bf1795e32cbf3863729c010aa55020063 Mon Sep 17 00:00:00 2001 From: Aroson <74296409+Aroson1@users.noreply.github.com> Date: Sun, 15 Oct 2023 21:05:02 +0530 Subject: [PATCH 364/757] Added 555 timer duty cycle and freqency in astable mode. (#10456) * Add files via upload * Update wheatstone_bridge.py * Update wheatstone_bridge.py * Create IC_555_Timer.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update IC_555_Timer.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update IC_555_Timer.py * Update and rename IC_555_Timer.py to ic_555_timer.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update ic_555_timer.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update ic_555_timer.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update ic_555_timer.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Cleanup ic_555_timer.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- electronics/ic_555_timer.py | 75 +++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 electronics/ic_555_timer.py diff --git a/electronics/ic_555_timer.py b/electronics/ic_555_timer.py new file mode 100644 index 000000000000..e187e1928dca --- /dev/null +++ b/electronics/ic_555_timer.py @@ -0,0 +1,75 @@ +from __future__ import annotations + +""" + Calculate the frequency and/or duty cycle of an astable 555 timer. + * https://en.wikipedia.org/wiki/555_timer_IC#Astable + + These functions take in the value of the external resistances (in ohms) + and capacitance (in Microfarad), and calculates the following: + + ------------------------------------- + | Freq = 1.44 /[( R1+ 2 x R2) x C1] | ... in Hz + ------------------------------------- + where Freq is the frequency, + R1 is the first resistance in ohms, + R2 is the second resistance in ohms, + C1 is the capacitance in Microfarads. + + ------------------------------------------------ + | Duty Cycle = (R1 + R2) / (R1 + 2 x R2) x 100 | ... in % + ------------------------------------------------ + where R1 is the first resistance in ohms, + R2 is the second resistance in ohms. +""" + + +def astable_frequency( + resistance_1: float, resistance_2: float, capacitance: float +) -> float: + """ + Usage examples: + >>> astable_frequency(resistance_1=45, resistance_2=45, capacitance=7) + 1523.8095238095239 + >>> astable_frequency(resistance_1=356, resistance_2=234, capacitance=976) + 1.7905459175553078 + >>> astable_frequency(resistance_1=2, resistance_2=-1, capacitance=2) + Traceback (most recent call last): + ... + ValueError: All values must be positive + >>> astable_frequency(resistance_1=45, resistance_2=45, capacitance=0) + Traceback (most recent call last): + ... + ValueError: All values must be positive + """ + + if resistance_1 <= 0 or resistance_2 <= 0 or capacitance <= 0: + raise ValueError("All values must be positive") + return (1.44 / ((resistance_1 + 2 * resistance_2) * capacitance)) * 10**6 + + +def astable_duty_cycle(resistance_1: float, resistance_2: float) -> float: + """ + Usage examples: + >>> astable_duty_cycle(resistance_1=45, resistance_2=45) + 66.66666666666666 + >>> astable_duty_cycle(resistance_1=356, resistance_2=234) + 71.60194174757282 + >>> astable_duty_cycle(resistance_1=2, resistance_2=-1) + Traceback (most recent call last): + ... + ValueError: All values must be positive + >>> astable_duty_cycle(resistance_1=0, resistance_2=0) + Traceback (most recent call last): + ... + ValueError: All values must be positive + """ + + if resistance_1 <= 0 or resistance_2 <= 0: + raise ValueError("All values must be positive") + return (resistance_1 + resistance_2) / (resistance_1 + 2 * resistance_2) * 100 + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From b2636d90b3fe697ff64a62b928edfbeccf216e8a Mon Sep 17 00:00:00 2001 From: K Anamithra Date: Sun, 15 Oct 2023 22:11:29 +0530 Subject: [PATCH 365/757] added implementing stack using two queues (#10076) * added implementing stack using two queues * Update Stack using two queues * Update stack_using_two_queues.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update stack_using_two_queues.py * Update stack_using_two_queues.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update stack_using_two_queues.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update stack_using_two_queues.py * Update stack_using_two_queues.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- .../stacks/stack_using_two_queues.py | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 data_structures/stacks/stack_using_two_queues.py diff --git a/data_structures/stacks/stack_using_two_queues.py b/data_structures/stacks/stack_using_two_queues.py new file mode 100644 index 000000000000..4b73246a045c --- /dev/null +++ b/data_structures/stacks/stack_using_two_queues.py @@ -0,0 +1,85 @@ +from __future__ import annotations + +from collections import deque +from dataclasses import dataclass, field + + +@dataclass +class StackWithQueues: + """ + https://www.geeksforgeeks.org/implement-stack-using-queue/ + + >>> stack = StackWithQueues() + >>> stack.push(1) + >>> stack.push(2) + >>> stack.push(3) + >>> stack.peek() + 3 + >>> stack.pop() + 3 + >>> stack.peek() + 2 + >>> stack.pop() + 2 + >>> stack.pop() + 1 + >>> stack.peek() is None + True + >>> stack.pop() + Traceback (most recent call last): + ... + IndexError: pop from an empty deque + """ + + main_queue: deque[int] = field(default_factory=deque) + temp_queue: deque[int] = field(default_factory=deque) + + def push(self, item: int) -> None: + self.temp_queue.append(item) + while self.main_queue: + self.temp_queue.append(self.main_queue.popleft()) + self.main_queue, self.temp_queue = self.temp_queue, self.main_queue + + def pop(self) -> int: + return self.main_queue.popleft() + + def peek(self) -> int | None: + return self.main_queue[0] if self.main_queue else None + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + + stack: StackWithQueues | None = StackWithQueues() + while stack: + print("\nChoose operation:") + print("1. Push") + print("2. Pop") + print("3. Peek") + print("4. Quit") + + choice = input("Enter choice (1/2/3/4): ") + + if choice == "1": + element = int(input("Enter an integer to push: ").strip()) + stack.push(element) + print(f"{element} pushed onto the stack.") + elif choice == "2": + popped_element = stack.pop() + if popped_element is not None: + print(f"Popped element: {popped_element}") + else: + print("Stack is empty.") + elif choice == "3": + peeked_element = stack.peek() + if peeked_element is not None: + print(f"Top element: {peeked_element}") + else: + print("Stack is empty.") + elif choice == "4": + del stack + stack = None + else: + print("Invalid choice. Please try again.") From 68e6d5ad7e9af8929a22a889b1182706abbfcb50 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Sun, 15 Oct 2023 19:11:05 +0200 Subject: [PATCH 366/757] validate_solutions.py: os.getenv('GITHUB_TOKEN', '') (#10546) * validate_solutions.py: os.getenv('GITHUB_TOKEN', '') @tianyizheng02 * updating DIRECTORY.md * f this --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 4 +++- scripts/validate_solutions.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index ceee9972dd97..6213f26b6d93 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -373,6 +373,7 @@ * [Electric Conductivity](electronics/electric_conductivity.py) * [Electric Power](electronics/electric_power.py) * [Electrical Impedance](electronics/electrical_impedance.py) + * [Ic 555 Timer](electronics/ic_555_timer.py) * [Ind Reactance](electronics/ind_reactance.py) * [Ohms Law](electronics/ohms_law.py) * [Real And Reactive Power](electronics/real_and_reactive_power.py) @@ -622,6 +623,7 @@ * [Is Ip V4 Address Valid](maths/is_ip_v4_address_valid.py) * [Is Square Free](maths/is_square_free.py) * [Jaccard Similarity](maths/jaccard_similarity.py) + * [Joint Probability Distribution](maths/joint_probability_distribution.py) * [Juggler Sequence](maths/juggler_sequence.py) * [Karatsuba](maths/karatsuba.py) * [Krishnamurthy Number](maths/krishnamurthy_number.py) @@ -675,8 +677,8 @@ * [Radians](maths/radians.py) * [Radix2 Fft](maths/radix2_fft.py) * [Remove Digit](maths/remove_digit.py) - * [Rkf45](maths/rkf45.py) * [Runge Kutta](maths/runge_kutta.py) + * [Runge Kutta Fehlberg 45](maths/runge_kutta_fehlberg_45.py) * [Segmented Sieve](maths/segmented_sieve.py) * Series * [Arithmetic](maths/series/arithmetic.py) diff --git a/scripts/validate_solutions.py b/scripts/validate_solutions.py index ca4af5261a8f..f27ec9ca60aa 100755 --- a/scripts/validate_solutions.py +++ b/scripts/validate_solutions.py @@ -55,7 +55,7 @@ def added_solution_file_path() -> list[pathlib.Path]: solution_file_paths = [] headers = { "Accept": "application/vnd.github.v3+json", - "Authorization": "token " + os.environ["GITHUB_TOKEN"], + "Authorization": f"token {os.getenv('GITHUB_TOKEN', '')}", } files = requests.get(get_files_url(), headers=headers).json() for file in files: From 7bdd1cd2beadf494685d1da63fb410343290de98 Mon Sep 17 00:00:00 2001 From: Barun Parua <76466796+Baron105@users.noreply.github.com> Date: Sun, 15 Oct 2023 22:43:40 +0530 Subject: [PATCH 367/757] updated physics/archimedes_principle.py (#10479) * avg and mps speed formulae added * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * avg and mps speed formulae added * fixed_spacing * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * spacing * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * ws * added amicable numbers * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * added amicable numbers * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * spacing * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * removed * changed name of file and added code improvements * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * issues fixed due to pi * requested changes added * added some doctests for exception handling, imported g from scipy and allowed zero gravity * removed_scipy_import * Update and rename archimedes_principle.py to archimedes_principle_of_buoyant_force.py * Update archimedes_principle_of_buoyant_force.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- physics/archimedes_principle.py | 49 --------------- .../archimedes_principle_of_buoyant_force.py | 63 +++++++++++++++++++ 2 files changed, 63 insertions(+), 49 deletions(-) delete mode 100644 physics/archimedes_principle.py create mode 100644 physics/archimedes_principle_of_buoyant_force.py diff --git a/physics/archimedes_principle.py b/physics/archimedes_principle.py deleted file mode 100644 index 6ecfc65e7461..000000000000 --- a/physics/archimedes_principle.py +++ /dev/null @@ -1,49 +0,0 @@ -""" -Calculates buoyant force on object submerged within static fluid. -Discovered by greek mathematician, Archimedes. The principle is named after him. - -Equation for calculating buoyant force: -Fb = ρ * V * g - -Source: -- https://en.wikipedia.org/wiki/Archimedes%27_principle -""" - - -# Acceleration Constant on Earth (unit m/s^2) -g = 9.80665 - - -def archimedes_principle( - fluid_density: float, volume: float, gravity: float = g -) -> float: - """ - Args: - fluid_density: density of fluid (kg/m^3) - volume: volume of object / liquid being displaced by object - gravity: Acceleration from gravity. Gravitational force on system, - Default is Earth Gravity - returns: - buoyant force on object in Newtons - - >>> archimedes_principle(fluid_density=997, volume=0.5, gravity=9.8) - 4885.3 - >>> archimedes_principle(fluid_density=997, volume=0.7) - 6844.061035 - """ - - if fluid_density <= 0: - raise ValueError("Impossible fluid density") - if volume < 0: - raise ValueError("Impossible Object volume") - if gravity <= 0: - raise ValueError("Impossible Gravity") - - return fluid_density * gravity * volume - - -if __name__ == "__main__": - import doctest - - # run doctest - doctest.testmod() diff --git a/physics/archimedes_principle_of_buoyant_force.py b/physics/archimedes_principle_of_buoyant_force.py new file mode 100644 index 000000000000..5f569837220f --- /dev/null +++ b/physics/archimedes_principle_of_buoyant_force.py @@ -0,0 +1,63 @@ +""" +Calculate the buoyant force of any body completely or partially submerged in a static +fluid. This principle was discovered by the Greek mathematician Archimedes. + +Equation for calculating buoyant force: +Fb = ρ * V * g + +https://en.wikipedia.org/wiki/Archimedes%27_principle +""" + + +# Acceleration Constant on Earth (unit m/s^2) +g = 9.80665 # Also available in scipy.constants.g + + +def archimedes_principle( + fluid_density: float, volume: float, gravity: float = g +) -> float: + """ + Args: + fluid_density: density of fluid (kg/m^3) + volume: volume of object/liquid being displaced by the object (m^3) + gravity: Acceleration from gravity. Gravitational force on the system, + The default is Earth Gravity + returns: + the buoyant force on an object in Newtons + + >>> archimedes_principle(fluid_density=500, volume=4, gravity=9.8) + 19600.0 + >>> archimedes_principle(fluid_density=997, volume=0.5, gravity=9.8) + 4885.3 + >>> archimedes_principle(fluid_density=997, volume=0.7) + 6844.061035 + >>> archimedes_principle(fluid_density=997, volume=-0.7) + Traceback (most recent call last): + ... + ValueError: Impossible object volume + >>> archimedes_principle(fluid_density=0, volume=0.7) + Traceback (most recent call last): + ... + ValueError: Impossible fluid density + >>> archimedes_principle(fluid_density=997, volume=0.7, gravity=0) + 0.0 + >>> archimedes_principle(fluid_density=997, volume=0.7, gravity=-9.8) + Traceback (most recent call last): + ... + ValueError: Impossible gravity + """ + + if fluid_density <= 0: + raise ValueError("Impossible fluid density") + if volume <= 0: + raise ValueError("Impossible object volume") + if gravity < 0: + raise ValueError("Impossible gravity") + + return fluid_density * gravity * volume + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 89d12dfe99d51f7df983ddbc6b0c93e1130fc47b Mon Sep 17 00:00:00 2001 From: Kosuri L Indu <118645569+kosuri-indu@users.noreply.github.com> Date: Mon, 16 Oct 2023 00:57:47 +0530 Subject: [PATCH 368/757] [Add] : Wildcard Matching program under DYNAMIC PROGRAMMING (#10403) * To add wildcard_matching.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * changes for doctest errors * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * code changes * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- dynamic_programming/wildcard_matching.py | 62 ++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 dynamic_programming/wildcard_matching.py diff --git a/dynamic_programming/wildcard_matching.py b/dynamic_programming/wildcard_matching.py new file mode 100644 index 000000000000..4ffc4b5d46aa --- /dev/null +++ b/dynamic_programming/wildcard_matching.py @@ -0,0 +1,62 @@ +""" +Given two strings, an input string and a pattern, +this program checks if the input string matches the pattern. + +Example : +input_string = "baaabab" +pattern = "*****ba*****ab" +Output: True + +This problem can be solved using the concept of "DYNAMIC PROGRAMMING". + +We create a 2D boolean matrix, where each entry match_matrix[i][j] is True +if the first i characters in input_string match the first j characters +of pattern. We initialize the first row and first column based on specific +rules, then fill up the rest of the matrix using a bottom-up dynamic +programming approach. + +The amount of match that will be determined is equal to match_matrix[n][m] +where n and m are lengths of the input_string and pattern respectively. + +""" + + +def is_pattern_match(input_string: str, pattern: str) -> bool: + """ + >>> is_pattern_match('baaabab','*****ba*****ba') + False + >>> is_pattern_match('baaabab','*****ba*****ab') + True + >>> is_pattern_match('aa','*') + True + """ + + input_length = len(input_string) + pattern_length = len(pattern) + + match_matrix = [[False] * (pattern_length + 1) for _ in range(input_length + 1)] + + match_matrix[0][0] = True + + for j in range(1, pattern_length + 1): + if pattern[j - 1] == "*": + match_matrix[0][j] = match_matrix[0][j - 1] + + for i in range(1, input_length + 1): + for j in range(1, pattern_length + 1): + if pattern[j - 1] in ("?", input_string[i - 1]): + match_matrix[i][j] = match_matrix[i - 1][j - 1] + elif pattern[j - 1] == "*": + match_matrix[i][j] = match_matrix[i - 1][j] or match_matrix[i][j - 1] + else: + match_matrix[i][j] = False + + return match_matrix[input_length][pattern_length] + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + + print(f"{is_pattern_match('baaabab','*****ba*****ab')}") From 4004b862d583a32cb1a809c4ea54d87635a273eb Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Sun, 15 Oct 2023 21:40:13 +0200 Subject: [PATCH 369/757] Revert "validate_solutions.py: os.getenv('GITHUB_TOKEN', '')" (#10552) * Revert "validate_solutions.py: os.getenv('GITHUB_TOKEN', '') (#10546)" This reverts commit 68e6d5ad7e9af8929a22a889b1182706abbfcb50. * updating DIRECTORY.md --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 4 +++- scripts/validate_solutions.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index 6213f26b6d93..5c63e6316547 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -265,6 +265,7 @@ * [Postfix Evaluation](data_structures/stacks/postfix_evaluation.py) * [Prefix Evaluation](data_structures/stacks/prefix_evaluation.py) * [Stack](data_structures/stacks/stack.py) + * [Stack Using Two Queues](data_structures/stacks/stack_using_two_queues.py) * [Stack With Doubly Linked List](data_structures/stacks/stack_with_doubly_linked_list.py) * [Stack With Singly Linked List](data_structures/stacks/stack_with_singly_linked_list.py) * [Stock Span Problem](data_structures/stacks/stock_span_problem.py) @@ -361,6 +362,7 @@ * [Trapped Water](dynamic_programming/trapped_water.py) * [Tribonacci](dynamic_programming/tribonacci.py) * [Viterbi](dynamic_programming/viterbi.py) + * [Wildcard Matching](dynamic_programming/wildcard_matching.py) * [Word Break](dynamic_programming/word_break.py) ## Electronics @@ -791,7 +793,7 @@ ## Physics * [Altitude Pressure](physics/altitude_pressure.py) - * [Archimedes Principle](physics/archimedes_principle.py) + * [Archimedes Principle Of Buoyant Force](physics/archimedes_principle_of_buoyant_force.py) * [Basic Orbital Capture](physics/basic_orbital_capture.py) * [Casimir Effect](physics/casimir_effect.py) * [Centripetal Force](physics/centripetal_force.py) diff --git a/scripts/validate_solutions.py b/scripts/validate_solutions.py index f27ec9ca60aa..ca4af5261a8f 100755 --- a/scripts/validate_solutions.py +++ b/scripts/validate_solutions.py @@ -55,7 +55,7 @@ def added_solution_file_path() -> list[pathlib.Path]: solution_file_paths = [] headers = { "Accept": "application/vnd.github.v3+json", - "Authorization": f"token {os.getenv('GITHUB_TOKEN', '')}", + "Authorization": "token " + os.environ["GITHUB_TOKEN"], } files = requests.get(get_files_url(), headers=headers).json() for file in files: From 902278f656b38ed68e148cf8c9ac2cbd10fcfb7e Mon Sep 17 00:00:00 2001 From: Aasheesh <126905285+AasheeshLikePanner@users.noreply.github.com> Date: Mon, 16 Oct 2023 01:26:02 +0530 Subject: [PATCH 370/757] Changes the code To return the list in dynamic_programming/subset_generation.py (#10191) * Changing the code to return tuple * Changing the code to return tuple * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update dynamic_programming/subset_generation.py Co-authored-by: Christian Clauss * Adding doctests in subset_generation.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update subset_generation.py * Update subset_generation.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update subset_generation.py * Update subset_generation.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update dynamic_programming/subset_generation.py Co-authored-by: Christian Clauss * Update stock_span_problem.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update subset_generation.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update subset_generation.py * Update subset_generation.py * Update subset_generation.py * Update subset_generation.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- dynamic_programming/subset_generation.py | 90 ++++++++++++++---------- 1 file changed, 53 insertions(+), 37 deletions(-) diff --git a/dynamic_programming/subset_generation.py b/dynamic_programming/subset_generation.py index 819fd8106def..1be412b9374d 100644 --- a/dynamic_programming/subset_generation.py +++ b/dynamic_programming/subset_generation.py @@ -1,44 +1,60 @@ -# Print all subset combinations of n element in given set of r element. - - -def combination_util(arr, n, r, index, data, i): +def subset_combinations(elements: list[int], n: int) -> list: """ - Current combination is ready to be printed, print it - arr[] ---> Input Array - data[] ---> Temporary array to store current combination - start & end ---> Staring and Ending indexes in arr[] - index ---> Current index in data[] - r ---> Size of a combination to be printed + Compute n-element combinations from a given list using dynamic programming. + Args: + elements: The list of elements from which combinations will be generated. + n: The number of elements in each combination. + Returns: + A list of tuples, each representing a combination of n elements. + >>> subset_combinations(elements=[10, 20, 30, 40], n=2) + [(10, 20), (10, 30), (10, 40), (20, 30), (20, 40), (30, 40)] + >>> subset_combinations(elements=[1, 2, 3], n=1) + [(1,), (2,), (3,)] + >>> subset_combinations(elements=[1, 2, 3], n=3) + [(1, 2, 3)] + >>> subset_combinations(elements=[42], n=1) + [(42,)] + >>> subset_combinations(elements=[6, 7, 8, 9], n=4) + [(6, 7, 8, 9)] + >>> subset_combinations(elements=[10, 20, 30, 40, 50], n=0) + [()] + >>> subset_combinations(elements=[1, 2, 3, 4], n=2) + [(1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)] + >>> subset_combinations(elements=[1, 'apple', 3.14], n=2) + [(1, 'apple'), (1, 3.14), ('apple', 3.14)] + >>> subset_combinations(elements=['single'], n=0) + [()] + >>> subset_combinations(elements=[], n=9) + [] + >>> from itertools import combinations + >>> all(subset_combinations(items, n) == list(combinations(items, n)) + ... for items, n in ( + ... ([10, 20, 30, 40], 2), ([1, 2, 3], 1), ([1, 2, 3], 3), ([42], 1), + ... ([6, 7, 8, 9], 4), ([10, 20, 30, 40, 50], 1), ([1, 2, 3, 4], 2), + ... ([1, 'apple', 3.14], 2), (['single'], 0), ([], 9))) + True """ - if index == r: - for j in range(r): - print(data[j], end=" ") - print(" ") - return - # When no more elements are there to put in data[] - if i >= n: - return - # current is included, put next at next location - data[index] = arr[i] - combination_util(arr, n, r, index + 1, data, i + 1) - # current is excluded, replace it with - # next (Note that i+1 is passed, but - # index is not changed) - combination_util(arr, n, r, index, data, i + 1) - # The main function that prints all combinations - # of size r in arr[] of size n. This function - # mainly uses combinationUtil() + r = len(elements) + if n > r: + return [] + + dp: list[list[tuple]] = [[] for _ in range(r + 1)] + dp[0].append(()) -def print_combination(arr, n, r): - # A temporary array to store all combination one by one - data = [0] * r - # Print all combination using temporary array 'data[]' - combination_util(arr, n, r, 0, data, 0) + for i in range(1, r + 1): + for j in range(i, 0, -1): + for prev_combination in dp[j - 1]: + dp[j].append(tuple(prev_combination) + (elements[i - 1],)) + + try: + return sorted(dp[n]) + except TypeError: + return dp[n] if __name__ == "__main__": - # Driver code to check the function above - arr = [10, 20, 30, 40, 50] - print_combination(arr, len(arr), 3) - # This code is contributed by Ambuj sahu + from doctest import testmod + + testmod() + print(f"{subset_combinations(elements=[10, 20, 30, 40], n=2) = }") From 3d6f3c41881da75653b804d7a5964ea90df9d2ad Mon Sep 17 00:00:00 2001 From: hollowcrust <72879387+hollowcrust@users.noreply.github.com> Date: Mon, 16 Oct 2023 04:13:27 +0800 Subject: [PATCH 371/757] Added data_structures/arrays/sparse_table.py (#10437) * Create sparse_table.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Descriptive names for variables * Fix ruff check error * Update sparse_table.py * Add comments, change variable names * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix typo * Update sparse_table.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- data_structures/arrays/sparse_table.py | 94 ++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 data_structures/arrays/sparse_table.py diff --git a/data_structures/arrays/sparse_table.py b/data_structures/arrays/sparse_table.py new file mode 100644 index 000000000000..a15d5649e712 --- /dev/null +++ b/data_structures/arrays/sparse_table.py @@ -0,0 +1,94 @@ +""" + Sparse table is a data structure that allows answering range queries on + a static number list, i.e. the elements do not change throughout all the queries. + + The implementation below will solve the problem of Range Minimum Query: + Finding the minimum value of a subset [L..R] of a static number list. + + Overall time complexity: O(nlogn) + Overall space complexity: O(nlogn) + + Wikipedia link: https://en.wikipedia.org/wiki/Range_minimum_query +""" +from math import log2 + + +def build_sparse_table(number_list: list[int]) -> list[list[int]]: + """ + Precompute range minimum queries with power of two length and store the precomputed + values in a table. + + >>> build_sparse_table([8, 1, 0, 3, 4, 9, 3]) + [[8, 1, 0, 3, 4, 9, 3], [1, 0, 0, 3, 4, 3, 0], [0, 0, 0, 3, 0, 0, 0]] + >>> build_sparse_table([3, 1, 9]) + [[3, 1, 9], [1, 1, 0]] + >>> build_sparse_table([]) + Traceback (most recent call last): + ... + ValueError: empty number list not allowed + """ + if not number_list: + raise ValueError("empty number list not allowed") + + length = len(number_list) + # Initialise sparse_table -- sparse_table[j][i] represents the minimum value of the + # subset of length (2 ** j) of number_list, starting from index i. + + # smallest power of 2 subset length that fully covers number_list + row = int(log2(length)) + 1 + sparse_table = [[0 for i in range(length)] for j in range(row)] + + # minimum of subset of length 1 is that value itself + for i, value in enumerate(number_list): + sparse_table[0][i] = value + j = 1 + + # compute the minimum value for all intervals with size (2 ** j) + while (1 << j) <= length: + i = 0 + # while subset starting from i still have at least (2 ** j) elements + while (i + (1 << j) - 1) < length: + # split range [i, i + 2 ** j] and find minimum of 2 halves + sparse_table[j][i] = min( + sparse_table[j - 1][i + (1 << (j - 1))], sparse_table[j - 1][i] + ) + i += 1 + j += 1 + return sparse_table + + +def query(sparse_table: list[list[int]], left_bound: int, right_bound: int) -> int: + """ + >>> query(build_sparse_table([8, 1, 0, 3, 4, 9, 3]), 0, 4) + 0 + >>> query(build_sparse_table([8, 1, 0, 3, 4, 9, 3]), 4, 6) + 3 + >>> query(build_sparse_table([3, 1, 9]), 2, 2) + 9 + >>> query(build_sparse_table([3, 1, 9]), 0, 1) + 1 + >>> query(build_sparse_table([8, 1, 0, 3, 4, 9, 3]), 0, 11) + Traceback (most recent call last): + ... + IndexError: list index out of range + >>> query(build_sparse_table([]), 0, 0) + Traceback (most recent call last): + ... + ValueError: empty number list not allowed + """ + if left_bound < 0 or right_bound >= len(sparse_table[0]): + raise IndexError("list index out of range") + + # highest subset length of power of 2 that is within range [left_bound, right_bound] + j = int(log2(right_bound - left_bound + 1)) + + # minimum of 2 overlapping smaller subsets: + # [left_bound, left_bound + 2 ** j - 1] and [right_bound - 2 ** j + 1, right_bound] + return min(sparse_table[j][right_bound - (1 << j) + 1], sparse_table[j][left_bound]) + + +if __name__ == "__main__": + from doctest import testmod + + testmod() + print(f"{query(build_sparse_table([3, 1, 9]), 2, 2) = }") From ec952927baea776bcb0f35d282448d32f3721047 Mon Sep 17 00:00:00 2001 From: dhruvtrigotra <72982592+dhruvtrigotra@users.noreply.github.com> Date: Mon, 16 Oct 2023 02:11:39 +0530 Subject: [PATCH 372/757] charging_inductor (#10427) * charging_capacitor * charging_capacitor * Final edits * charging_inductor --------- Co-authored-by: Christian Clauss --- electronics/charging_inductor.py | 96 ++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 electronics/charging_inductor.py diff --git a/electronics/charging_inductor.py b/electronics/charging_inductor.py new file mode 100644 index 000000000000..e5c0126c248a --- /dev/null +++ b/electronics/charging_inductor.py @@ -0,0 +1,96 @@ +# source - The ARRL Handbook for Radio Communications +# https://en.wikipedia.org/wiki/RL_circuit + +""" +Description +----------- +Inductor is a passive electronic device which stores energy but unlike capacitor, it +stores energy in its 'magnetic field' or 'magnetostatic field'. + +When inductor is connected to 'DC' current source nothing happens it just works like a +wire because it's real effect cannot be seen while 'DC' is connected, its not even +going to store energy. Inductor stores energy only when it is working on 'AC' current. + +Connecting a inductor in series with a resistor(when R = 0) to a 'AC' potential source, +from zero to a finite value causes a sudden voltage to induced in inductor which +opposes the current. which results in initially slowly current rise. However it would +cease if there is no further changes in current. With resistance zero current will never +stop rising. + +'Resistance(ohms) / Inductance(henrys)' is known as RL-timeconstant. It also represents +as τ (tau). While the charging of a inductor with a resistor results in +a exponential function. + +when inductor is connected across 'AC' potential source. It starts to store the energy +in its 'magnetic field'.with the help 'RL-time-constant' we can find current at any time +in inductor while it is charging. +""" +from math import exp # value of exp = 2.718281828459… + + +def charging_inductor( + source_voltage: float, # source_voltage should be in volts. + resistance: float, # resistance should be in ohms. + inductance: float, # inductance should be in henrys. + time: float, # time should in seconds. +) -> float: + """ + Find inductor current at any nth second after initiating its charging. + + Examples + -------- + >>> charging_inductor(source_voltage=5.8,resistance=1.5,inductance=2.3,time=2) + 2.817 + + >>> charging_inductor(source_voltage=8,resistance=5,inductance=3,time=2) + 1.543 + + >>> charging_inductor(source_voltage=8,resistance=5*pow(10,2),inductance=3,time=2) + 0.016 + + >>> charging_inductor(source_voltage=-8,resistance=100,inductance=15,time=12) + Traceback (most recent call last): + ... + ValueError: Source voltage must be positive. + + >>> charging_inductor(source_voltage=80,resistance=-15,inductance=100,time=5) + Traceback (most recent call last): + ... + ValueError: Resistance must be positive. + + >>> charging_inductor(source_voltage=12,resistance=200,inductance=-20,time=5) + Traceback (most recent call last): + ... + ValueError: Inductance must be positive. + + >>> charging_inductor(source_voltage=0,resistance=200,inductance=20,time=5) + Traceback (most recent call last): + ... + ValueError: Source voltage must be positive. + + >>> charging_inductor(source_voltage=10,resistance=0,inductance=20,time=5) + Traceback (most recent call last): + ... + ValueError: Resistance must be positive. + + >>> charging_inductor(source_voltage=15, resistance=25, inductance=0, time=5) + Traceback (most recent call last): + ... + ValueError: Inductance must be positive. + """ + + if source_voltage <= 0: + raise ValueError("Source voltage must be positive.") + if resistance <= 0: + raise ValueError("Resistance must be positive.") + if inductance <= 0: + raise ValueError("Inductance must be positive.") + return round( + source_voltage / resistance * (1 - exp((-time * resistance) / inductance)), 3 + ) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From bcda3bf64ea20db11cb4b1b81536e2f05ee584fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ab=C3=ADlio=20Azevedo?= Date: Sun, 15 Oct 2023 18:31:11 -0300 Subject: [PATCH 373/757] test: adding more tests to a star algorithm (#10397) * test: adding more tests to a star algorithm * Apply suggestions from code review * Update a_star.py --------- Co-authored-by: Tianyi Zheng --- graphs/a_star.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/graphs/a_star.py b/graphs/a_star.py index e8735179eab9..06da3b5cd863 100644 --- a/graphs/a_star.py +++ b/graphs/a_star.py @@ -16,6 +16,31 @@ def search( cost: int, heuristic: list[list[int]], ) -> tuple[list[list[int]], list[list[int]]]: + """ + Search for a path on a grid avoiding obstacles. + >>> grid = [[0, 1, 0, 0, 0, 0], + ... [0, 1, 0, 0, 0, 0], + ... [0, 1, 0, 0, 0, 0], + ... [0, 1, 0, 0, 1, 0], + ... [0, 0, 0, 0, 1, 0]] + >>> init = [0, 0] + >>> goal = [len(grid) - 1, len(grid[0]) - 1] + >>> cost = 1 + >>> heuristic = [[0] * len(grid[0]) for _ in range(len(grid))] + >>> heuristic = [[0 for row in range(len(grid[0]))] for col in range(len(grid))] + >>> for i in range(len(grid)): + ... for j in range(len(grid[0])): + ... heuristic[i][j] = abs(i - goal[0]) + abs(j - goal[1]) + ... if grid[i][j] == 1: + ... heuristic[i][j] = 99 + >>> path, action = search(grid, init, goal, cost, heuristic) + >>> path # doctest: +NORMALIZE_WHITESPACE + [[0, 0], [1, 0], [2, 0], [3, 0], [4, 0], [4, 1], [4, 2], [4, 3], [3, 3], + [2, 3], [2, 4], [2, 5], [3, 5], [4, 5]] + >>> action # doctest: +NORMALIZE_WHITESPACE + [[0, 0, 0, 0, 0, 0], [2, 0, 0, 0, 0, 0], [2, 0, 0, 0, 3, 3], + [2, 0, 0, 0, 0, 2], [2, 3, 3, 3, 0, 2]] + """ closed = [ [0 for col in range(len(grid[0]))] for row in range(len(grid)) ] # the reference grid From d00888de7629b093bcf750ae046318be1e9a1fa3 Mon Sep 17 00:00:00 2001 From: Jeel Gajera <83470656+JeelGajera@users.noreply.github.com> Date: Mon, 16 Oct 2023 03:19:53 +0530 Subject: [PATCH 374/757] feat: adding Apriori Algorithm (#10491) * feat: adding Apriori Algorithm * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix: doctest, typo * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix: type error, code refactore * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix: refactore code * fix: doctest * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix: E501, B007 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix: err * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix: arg typ err * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix: typo * fix: typo * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Replace generate_candidates() with itertools.combinations() * mypy * Update apriori_algorithm.py --------- Co-authored-by: Jeel Gajera Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- DIRECTORY.md | 1 + machine_learning/apriori_algorithm.py | 112 ++++++++++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 machine_learning/apriori_algorithm.py diff --git a/DIRECTORY.md b/DIRECTORY.md index 5c63e6316547..55781df03b91 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -554,6 +554,7 @@ * [Word Frequency Functions](machine_learning/word_frequency_functions.py) * [Xgboost Classifier](machine_learning/xgboost_classifier.py) * [Xgboost Regressor](machine_learning/xgboost_regressor.py) + * [Apriori Algorithm](machine_learning/apriori_algorithm.py) ## Maths * [Abs](maths/abs.py) diff --git a/machine_learning/apriori_algorithm.py b/machine_learning/apriori_algorithm.py new file mode 100644 index 000000000000..d9fd1f82ea3c --- /dev/null +++ b/machine_learning/apriori_algorithm.py @@ -0,0 +1,112 @@ +""" +Apriori Algorithm is a Association rule mining technique, also known as market basket +analysis, aims to discover interesting relationships or associations among a set of +items in a transactional or relational database. + +For example, Apriori Algorithm states: "If a customer buys item A and item B, then they +are likely to buy item C." This rule suggests a relationship between items A, B, and C, +indicating that customers who purchased A and B are more likely to also purchase item C. + +WIKI: https://en.wikipedia.org/wiki/Apriori_algorithm +Examples: https://www.kaggle.com/code/earthian/apriori-association-rules-mining +""" +from itertools import combinations + + +def load_data() -> list[list[str]]: + """ + Returns a sample transaction dataset. + + >>> load_data() + [['milk'], ['milk', 'butter'], ['milk', 'bread'], ['milk', 'bread', 'chips']] + """ + return [["milk"], ["milk", "butter"], ["milk", "bread"], ["milk", "bread", "chips"]] + + +def prune(itemset: list, candidates: list, length: int) -> list: + """ + Prune candidate itemsets that are not frequent. + The goal of pruning is to filter out candidate itemsets that are not frequent. This + is done by checking if all the (k-1) subsets of a candidate itemset are present in + the frequent itemsets of the previous iteration (valid subsequences of the frequent + itemsets from the previous iteration). + + Prunes candidate itemsets that are not frequent. + + >>> itemset = ['X', 'Y', 'Z'] + >>> candidates = [['X', 'Y'], ['X', 'Z'], ['Y', 'Z']] + >>> prune(itemset, candidates, 2) + [['X', 'Y'], ['X', 'Z'], ['Y', 'Z']] + + >>> itemset = ['1', '2', '3', '4'] + >>> candidates = ['1', '2', '4'] + >>> prune(itemset, candidates, 3) + [] + """ + pruned = [] + for candidate in candidates: + is_subsequence = True + for item in candidate: + if item not in itemset or itemset.count(item) < length - 1: + is_subsequence = False + break + if is_subsequence: + pruned.append(candidate) + return pruned + + +def apriori(data: list[list[str]], min_support: int) -> list[tuple[list[str], int]]: + """ + Returns a list of frequent itemsets and their support counts. + + >>> data = [['A', 'B', 'C'], ['A', 'B'], ['A', 'C'], ['A', 'D'], ['B', 'C']] + >>> apriori(data, 2) + [(['A', 'B'], 1), (['A', 'C'], 2), (['B', 'C'], 2)] + + >>> data = [['1', '2', '3'], ['1', '2'], ['1', '3'], ['1', '4'], ['2', '3']] + >>> apriori(data, 3) + [] + """ + itemset = [list(transaction) for transaction in data] + frequent_itemsets = [] + length = 1 + + while itemset: + # Count itemset support + counts = [0] * len(itemset) + for transaction in data: + for j, candidate in enumerate(itemset): + if all(item in transaction for item in candidate): + counts[j] += 1 + + # Prune infrequent itemsets + itemset = [item for i, item in enumerate(itemset) if counts[i] >= min_support] + + # Append frequent itemsets (as a list to maintain order) + for i, item in enumerate(itemset): + frequent_itemsets.append((sorted(item), counts[i])) + + length += 1 + itemset = prune(itemset, list(combinations(itemset, length)), length) + + return frequent_itemsets + + +if __name__ == "__main__": + """ + Apriori algorithm for finding frequent itemsets. + + Args: + data: A list of transactions, where each transaction is a list of items. + min_support: The minimum support threshold for frequent itemsets. + + Returns: + A list of frequent itemsets along with their support counts. + """ + import doctest + + doctest.testmod() + + # user-defined threshold or minimum support level + frequent_itemsets = apriori(data=load_data(), min_support=2) + print("\n".join(f"{itemset}: {support}" for itemset, support in frequent_itemsets)) From e6aae1cf66b7e962b886255703b5802d58f27fd3 Mon Sep 17 00:00:00 2001 From: Pooja Sharma <75516191+Shailaputri@users.noreply.github.com> Date: Mon, 16 Oct 2023 05:02:45 +0530 Subject: [PATCH 375/757] Dynamic programming/matrix chain multiplication (#10562) * updating DIRECTORY.md * spell changes * updating DIRECTORY.md * real world applications * updating DIRECTORY.md * Update matrix_chain_multiplication.py Add a non-dp solution with benchmarks. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update matrix_chain_multiplication.py * Update matrix_chain_multiplication.py * Update matrix_chain_multiplication.py --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: Pooja Sharma Co-authored-by: Christian Clauss Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- DIRECTORY.md | 5 +- .../matrix_chain_multiplication.py | 143 ++++++++++++++++++ 2 files changed, 147 insertions(+), 1 deletion(-) create mode 100644 dynamic_programming/matrix_chain_multiplication.py diff --git a/DIRECTORY.md b/DIRECTORY.md index 55781df03b91..cef1e06b78aa 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -182,6 +182,7 @@ * [Permutations](data_structures/arrays/permutations.py) * [Prefix Sum](data_structures/arrays/prefix_sum.py) * [Product Sum](data_structures/arrays/product_sum.py) + * [Sparse Table](data_structures/arrays/sparse_table.py) * Binary Tree * [Avl Tree](data_structures/binary_tree/avl_tree.py) * [Basic Binary Tree](data_structures/binary_tree/basic_binary_tree.py) @@ -340,6 +341,7 @@ * [Longest Increasing Subsequence O(Nlogn)](dynamic_programming/longest_increasing_subsequence_o(nlogn).py) * [Longest Palindromic Subsequence](dynamic_programming/longest_palindromic_subsequence.py) * [Longest Sub Array](dynamic_programming/longest_sub_array.py) + * [Matrix Chain Multiplication](dynamic_programming/matrix_chain_multiplication.py) * [Matrix Chain Order](dynamic_programming/matrix_chain_order.py) * [Max Non Adjacent Sum](dynamic_programming/max_non_adjacent_sum.py) * [Max Product Subarray](dynamic_programming/max_product_subarray.py) @@ -370,6 +372,7 @@ * [Builtin Voltage](electronics/builtin_voltage.py) * [Carrier Concentration](electronics/carrier_concentration.py) * [Charging Capacitor](electronics/charging_capacitor.py) + * [Charging Inductor](electronics/charging_inductor.py) * [Circular Convolution](electronics/circular_convolution.py) * [Coulombs Law](electronics/coulombs_law.py) * [Electric Conductivity](electronics/electric_conductivity.py) @@ -524,6 +527,7 @@ * [Simplex](linear_programming/simplex.py) ## Machine Learning + * [Apriori Algorithm](machine_learning/apriori_algorithm.py) * [Astar](machine_learning/astar.py) * [Data Transformations](machine_learning/data_transformations.py) * [Decision Tree](machine_learning/decision_tree.py) @@ -554,7 +558,6 @@ * [Word Frequency Functions](machine_learning/word_frequency_functions.py) * [Xgboost Classifier](machine_learning/xgboost_classifier.py) * [Xgboost Regressor](machine_learning/xgboost_regressor.py) - * [Apriori Algorithm](machine_learning/apriori_algorithm.py) ## Maths * [Abs](maths/abs.py) diff --git a/dynamic_programming/matrix_chain_multiplication.py b/dynamic_programming/matrix_chain_multiplication.py new file mode 100644 index 000000000000..084254a61f6c --- /dev/null +++ b/dynamic_programming/matrix_chain_multiplication.py @@ -0,0 +1,143 @@ +""" +Find the minimum number of multiplications needed to multiply chain of matrices. +Reference: https://www.geeksforgeeks.org/matrix-chain-multiplication-dp-8/ + +The algorithm has interesting real-world applications. Example: +1. Image transformations in Computer Graphics as images are composed of matrix. +2. Solve complex polynomial equations in the field of algebra using least processing + power. +3. Calculate overall impact of macroeconomic decisions as economic equations involve a + number of variables. +4. Self-driving car navigation can be made more accurate as matrix multiplication can + accurately determine position and orientation of obstacles in short time. + +Python doctests can be run with the following command: +python -m doctest -v matrix_chain_multiply.py + +Given a sequence arr[] that represents chain of 2D matrices such that the dimension of +the ith matrix is arr[i-1]*arr[i]. +So suppose arr = [40, 20, 30, 10, 30] means we have 4 matrices of dimensions +40*20, 20*30, 30*10 and 10*30. + +matrix_chain_multiply() returns an integer denoting minimum number of multiplications to +multiply the chain. + +We do not need to perform actual multiplication here. +We only need to decide the order in which to perform the multiplication. + +Hints: +1. Number of multiplications (ie cost) to multiply 2 matrices +of size m*p and p*n is m*p*n. +2. Cost of matrix multiplication is associative ie (M1*M2)*M3 != M1*(M2*M3) +3. Matrix multiplication is not commutative. So, M1*M2 does not mean M2*M1 can be done. +4. To determine the required order, we can try different combinations. +So, this problem has overlapping sub-problems and can be solved using recursion. +We use Dynamic Programming for optimal time complexity. + +Example input: +arr = [40, 20, 30, 10, 30] +output: 26000 +""" +from collections.abc import Iterator +from contextlib import contextmanager +from functools import cache +from sys import maxsize + + +def matrix_chain_multiply(arr: list[int]) -> int: + """ + Find the minimum number of multiplcations required to multiply the chain of matrices + + Args: + arr: The input array of integers. + + Returns: + Minimum number of multiplications needed to multiply the chain + + Examples: + >>> matrix_chain_multiply([1, 2, 3, 4, 3]) + 30 + >>> matrix_chain_multiply([10]) + 0 + >>> matrix_chain_multiply([10, 20]) + 0 + >>> matrix_chain_multiply([19, 2, 19]) + 722 + >>> matrix_chain_multiply(list(range(1, 100))) + 323398 + + # >>> matrix_chain_multiply(list(range(1, 251))) + # 2626798 + """ + if len(arr) < 2: + return 0 + # initialising 2D dp matrix + n = len(arr) + dp = [[maxsize for j in range(n)] for i in range(n)] + # we want minimum cost of multiplication of matrices + # of dimension (i*k) and (k*j). This cost is arr[i-1]*arr[k]*arr[j]. + for i in range(n - 1, 0, -1): + for j in range(i, n): + if i == j: + dp[i][j] = 0 + continue + for k in range(i, j): + dp[i][j] = min( + dp[i][j], dp[i][k] + dp[k + 1][j] + arr[i - 1] * arr[k] * arr[j] + ) + + return dp[1][n - 1] + + +def matrix_chain_order(dims: list[int]) -> int: + """ + Source: https://en.wikipedia.org/wiki/Matrix_chain_multiplication + The dynamic programming solution is faster than cached the recursive solution and + can handle larger inputs. + >>> matrix_chain_order([1, 2, 3, 4, 3]) + 30 + >>> matrix_chain_order([10]) + 0 + >>> matrix_chain_order([10, 20]) + 0 + >>> matrix_chain_order([19, 2, 19]) + 722 + >>> matrix_chain_order(list(range(1, 100))) + 323398 + + # >>> matrix_chain_order(list(range(1, 251))) # Max before RecursionError is raised + # 2626798 + """ + + @cache + def a(i: int, j: int) -> int: + return min( + (a(i, k) + dims[i] * dims[k] * dims[j] + a(k, j) for k in range(i + 1, j)), + default=0, + ) + + return a(0, len(dims) - 1) + + +@contextmanager +def elapsed_time(msg: str) -> Iterator: + # print(f"Starting: {msg}") + from time import perf_counter_ns + + start = perf_counter_ns() + yield + print(f"Finished: {msg} in {(perf_counter_ns() - start) / 10 ** 9} seconds.") + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + with elapsed_time("matrix_chain_order"): + print(f"{matrix_chain_order(list(range(1, 251))) = }") + with elapsed_time("matrix_chain_multiply"): + print(f"{matrix_chain_multiply(list(range(1, 251))) = }") + with elapsed_time("matrix_chain_order"): + print(f"{matrix_chain_order(list(range(1, 251))) = }") + with elapsed_time("matrix_chain_multiply"): + print(f"{matrix_chain_multiply(list(range(1, 251))) = }") From b6b45eb1cee564e3c563966244f124051c28b8e7 Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Sun, 15 Oct 2023 19:41:45 -0400 Subject: [PATCH 376/757] Fix numpy deprecation warning in `2_hidden_layers_neural_network.py` (#10424) * updating DIRECTORY.md * updating DIRECTORY.md * updating DIRECTORY.md * updating DIRECTORY.md * Fix deprecation warning in 2_hidden_layers_neural_network.py Fix numpy deprecation warning: DeprecationWarning: Conversion of an array with ndim > 0 to a scalar is deprecated, and will error in future. Ensure you extract a single element from your array before performing this operation. (Deprecated NumPy 1.25.) --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- neural_network/2_hidden_layers_neural_network.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/neural_network/2_hidden_layers_neural_network.py b/neural_network/2_hidden_layers_neural_network.py index 9c5772326165..7b374a93d039 100644 --- a/neural_network/2_hidden_layers_neural_network.py +++ b/neural_network/2_hidden_layers_neural_network.py @@ -196,7 +196,7 @@ def predict(self, input_arr: numpy.ndarray) -> int: >>> output_val = numpy.array(([0], [1], [1]), dtype=float) >>> nn = TwoHiddenLayerNeuralNetwork(input_val, output_val) >>> nn.train(output_val, 1000, False) - >>> nn.predict([0,1,0]) in (0, 1) + >>> nn.predict([0, 1, 0]) in (0, 1) True """ @@ -221,7 +221,7 @@ def predict(self, input_arr: numpy.ndarray) -> int: ) ) - return int(self.layer_between_second_hidden_layer_and_output > 0.6) + return int((self.layer_between_second_hidden_layer_and_output > 0.6)[0]) def sigmoid(value: numpy.ndarray) -> numpy.ndarray: From 73ebf7bdb12f4bced39f25766ac4d2cd9b6ab525 Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Sun, 15 Oct 2023 19:42:55 -0400 Subject: [PATCH 377/757] Move and rename `maths/greedy_coin_change.py` (#10418) * updating DIRECTORY.md * updating DIRECTORY.md * updating DIRECTORY.md * updating DIRECTORY.md * Move greedy_coin_change.py to greedy_methods/ and rename file --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- .../minimum_coin_change.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename maths/greedy_coin_change.py => greedy_methods/minimum_coin_change.py (100%) diff --git a/maths/greedy_coin_change.py b/greedy_methods/minimum_coin_change.py similarity index 100% rename from maths/greedy_coin_change.py rename to greedy_methods/minimum_coin_change.py From c2f14e8a78c1700a4101746a1a6e3d70be50aa07 Mon Sep 17 00:00:00 2001 From: Chris O <46587501+ChrisO345@users.noreply.github.com> Date: Mon, 16 Oct 2023 12:44:06 +1300 Subject: [PATCH 378/757] Add note to feature_request.yml about not opening issues for new algorithms (#10142) --- .github/ISSUE_TEMPLATE/feature_request.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index 09a159b2193e..20823bd58ab1 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -6,6 +6,7 @@ body: attributes: value: > Before requesting please search [existing issues](https://github.com/TheAlgorithms/Python/labels/enhancement). + Do not create issues to implement new algorithms as these will be closed. Usage questions such as "How do I...?" belong on the [Discord](https://discord.gg/c7MnfGFGa6) and will be closed. @@ -13,7 +14,6 @@ body: attributes: label: "Feature description" description: > - This could be new algorithms, data structures or improving any existing - implementations. + This could include new topics or improving any existing implementations. validations: required: true From bb8f194957c4308cbb0bf16a4e07acbe34d2087e Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Sun, 15 Oct 2023 20:01:01 -0400 Subject: [PATCH 379/757] Delete `texttable` from dependencies (#10565) * Disable unused dependencies Comment out dependencies in requirements.txt that are only used by currently-disabled files * Delete unused dependency --- requirements.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 1e64818bbb6a..05d9f1e8c545 100644 --- a/requirements.txt +++ b/requirements.txt @@ -18,7 +18,6 @@ scikit-learn statsmodels sympy tensorflow ; python_version < '3.12' -texttable tweepy xgboost # yulewalker # uncomment once audio_filters/equal_loudness_filter.py is fixed From 1a26d76c60422030cf0c57c62623866d3f3229f2 Mon Sep 17 00:00:00 2001 From: "Gabrielly de S. Pinto Dantas" Date: Sun, 15 Oct 2023 21:44:10 -0300 Subject: [PATCH 380/757] add tests for tree_sort (#10015) * add tests for tree_sort * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update tree_sort.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- sorts/tree_sort.py | 99 +++++++++++++++++++++++++++------------------- 1 file changed, 58 insertions(+), 41 deletions(-) diff --git a/sorts/tree_sort.py b/sorts/tree_sort.py index 78c3e893e0ce..e63a3253ba19 100644 --- a/sorts/tree_sort.py +++ b/sorts/tree_sort.py @@ -1,53 +1,70 @@ """ Tree_sort algorithm. -Build a BST and in order traverse. +Build a Binary Search Tree and then iterate thru it to get a sorted list. """ +from __future__ import annotations +from collections.abc import Iterator +from dataclasses import dataclass + +@dataclass class Node: - # BST data structure - def __init__(self, val): - self.val = val - self.left = None - self.right = None - - def insert(self, val): - if self.val: - if val < self.val: - if self.left is None: - self.left = Node(val) - else: - self.left.insert(val) - elif val > self.val: - if self.right is None: - self.right = Node(val) - else: - self.right.insert(val) - else: - self.val = val - - -def inorder(root, res): - # Recursive traversal - if root: - inorder(root.left, res) - res.append(root.val) - inorder(root.right, res) - - -def tree_sort(arr): - # Build BST + val: int + left: Node | None = None + right: Node | None = None + + def __iter__(self) -> Iterator[int]: + if self.left: + yield from self.left + yield self.val + if self.right: + yield from self.right + + def __len__(self) -> int: + return sum(1 for _ in self) + + def insert(self, val: int) -> None: + if val < self.val: + if self.left is None: + self.left = Node(val) + else: + self.left.insert(val) + elif val > self.val: + if self.right is None: + self.right = Node(val) + else: + self.right.insert(val) + + +def tree_sort(arr: list[int]) -> tuple[int, ...]: + """ + >>> tree_sort([]) + () + >>> tree_sort((1,)) + (1,) + >>> tree_sort((1, 2)) + (1, 2) + >>> tree_sort([5, 2, 7]) + (2, 5, 7) + >>> tree_sort((5, -4, 9, 2, 7)) + (-4, 2, 5, 7, 9) + >>> tree_sort([5, 6, 1, -1, 4, 37, 2, 7]) + (-1, 1, 2, 4, 5, 6, 7, 37) + >>> tree_sort(range(10, -10, -1)) == tuple(sorted(range(10, -10, -1))) + True + """ if len(arr) == 0: - return arr + return tuple(arr) root = Node(arr[0]) - for i in range(1, len(arr)): - root.insert(arr[i]) - # Traverse BST in order. - res = [] - inorder(root, res) - return res + for item in arr[1:]: + root.insert(item) + return tuple(root) if __name__ == "__main__": - print(tree_sort([10, 1, 3, 2, 9, 14, 13])) + import doctest + + doctest.testmod() + print(f"{tree_sort([5, 6, 1, -1, 4, 37, -3, 7]) = }") From cc0405d05cb4c5009e8bf826e3f641c427ba70d5 Mon Sep 17 00:00:00 2001 From: Yousha Mahamuni <40205524+yousha806@users.noreply.github.com> Date: Mon, 16 Oct 2023 08:17:27 +0530 Subject: [PATCH 381/757] Update volume.py with volume of Icosahedron (#9628) * Update volume.py with volume of Icosahedron Added function to find volume of a regular Icosahedron * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Apply suggestions from code review * Update volume.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Tianyi Zheng --- maths/volume.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/maths/volume.py b/maths/volume.py index 721974e68b66..b4df4e475783 100644 --- a/maths/volume.py +++ b/maths/volume.py @@ -469,6 +469,35 @@ def vol_torus(torus_radius: float, tube_radius: float) -> float: return 2 * pow(pi, 2) * torus_radius * pow(tube_radius, 2) +def vol_icosahedron(tri_side: float) -> float: + """Calculate the Volume of an Icosahedron. + Wikipedia reference: https://en.wikipedia.org/wiki/Regular_icosahedron + + >>> from math import isclose + >>> isclose(vol_icosahedron(2.5), 34.088984228514256) + True + >>> isclose(vol_icosahedron(10), 2181.694990624912374) + True + >>> isclose(vol_icosahedron(5), 272.711873828114047) + True + >>> isclose(vol_icosahedron(3.49), 92.740688412033628) + True + >>> vol_icosahedron(0) + 0.0 + >>> vol_icosahedron(-1) + Traceback (most recent call last): + ... + ValueError: vol_icosahedron() only accepts non-negative values + >>> vol_icosahedron(-0.2) + Traceback (most recent call last): + ... + ValueError: vol_icosahedron() only accepts non-negative values + """ + if tri_side < 0: + raise ValueError("vol_icosahedron() only accepts non-negative values") + return tri_side**3 * (3 + 5**0.5) * 5 / 12 + + def main(): """Print the Results of Various Volume Calculations.""" print("Volumes:") @@ -489,6 +518,7 @@ def main(): print( f"Hollow Circular Cylinder: {vol_hollow_circular_cylinder(1, 2, 3) = }" ) # ~= 28.3 + print(f"Icosahedron: {vol_icosahedron(2.5) = }") # ~=34.09 if __name__ == "__main__": From f4ff73b1bdaa4349315beaf44e093c59f6c87fd3 Mon Sep 17 00:00:00 2001 From: Akshar Goyal Date: Mon, 16 Oct 2023 03:21:43 -0400 Subject: [PATCH 382/757] Converted tests into doctests (#10572) * Converted tests into doctests * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Removed commented code * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- boolean_algebra/and_gate.py | 18 +++--------------- boolean_algebra/imply_gate.py | 7 +++---- boolean_algebra/nand_gate.py | 17 +++-------------- boolean_algebra/nimply_gate.py | 7 +++---- boolean_algebra/not_gate.py | 13 +++---------- boolean_algebra/or_gate.py | 17 +++-------------- boolean_algebra/xnor_gate.py | 17 +++-------------- boolean_algebra/xor_gate.py | 15 +++------------ 8 files changed, 24 insertions(+), 87 deletions(-) diff --git a/boolean_algebra/and_gate.py b/boolean_algebra/and_gate.py index 834116772ee7..f0fd45c9f81e 100644 --- a/boolean_algebra/and_gate.py +++ b/boolean_algebra/and_gate.py @@ -32,19 +32,7 @@ def and_gate(input_1: int, input_2: int) -> int: return int((input_1, input_2).count(0) == 0) -def test_and_gate() -> None: - """ - Tests the and_gate function - """ - assert and_gate(0, 0) == 0 - assert and_gate(0, 1) == 0 - assert and_gate(1, 0) == 0 - assert and_gate(1, 1) == 1 - - if __name__ == "__main__": - test_and_gate() - print(and_gate(1, 0)) - print(and_gate(0, 0)) - print(and_gate(0, 1)) - print(and_gate(1, 1)) + import doctest + + doctest.testmod() diff --git a/boolean_algebra/imply_gate.py b/boolean_algebra/imply_gate.py index 151a7ad6439a..b64ebaceb306 100644 --- a/boolean_algebra/imply_gate.py +++ b/boolean_algebra/imply_gate.py @@ -34,7 +34,6 @@ def imply_gate(input_1: int, input_2: int) -> int: if __name__ == "__main__": - print(imply_gate(0, 0)) - print(imply_gate(0, 1)) - print(imply_gate(1, 0)) - print(imply_gate(1, 1)) + import doctest + + doctest.testmod() diff --git a/boolean_algebra/nand_gate.py b/boolean_algebra/nand_gate.py index ea3303d16b25..80f9d12db89a 100644 --- a/boolean_algebra/nand_gate.py +++ b/boolean_algebra/nand_gate.py @@ -30,18 +30,7 @@ def nand_gate(input_1: int, input_2: int) -> int: return int((input_1, input_2).count(0) != 0) -def test_nand_gate() -> None: - """ - Tests the nand_gate function - """ - assert nand_gate(0, 0) == 1 - assert nand_gate(0, 1) == 1 - assert nand_gate(1, 0) == 1 - assert nand_gate(1, 1) == 0 - - if __name__ == "__main__": - print(nand_gate(0, 0)) - print(nand_gate(0, 1)) - print(nand_gate(1, 0)) - print(nand_gate(1, 1)) + import doctest + + doctest.testmod() diff --git a/boolean_algebra/nimply_gate.py b/boolean_algebra/nimply_gate.py index 6e34332d9112..68e82c8db8d9 100644 --- a/boolean_algebra/nimply_gate.py +++ b/boolean_algebra/nimply_gate.py @@ -34,7 +34,6 @@ def nimply_gate(input_1: int, input_2: int) -> int: if __name__ == "__main__": - print(nimply_gate(0, 0)) - print(nimply_gate(0, 1)) - print(nimply_gate(1, 0)) - print(nimply_gate(1, 1)) + import doctest + + doctest.testmod() diff --git a/boolean_algebra/not_gate.py b/boolean_algebra/not_gate.py index eb85e9e44cd3..cfa74cf42204 100644 --- a/boolean_algebra/not_gate.py +++ b/boolean_algebra/not_gate.py @@ -24,14 +24,7 @@ def not_gate(input_1: int) -> int: return 1 if input_1 == 0 else 0 -def test_not_gate() -> None: - """ - Tests the not_gate function - """ - assert not_gate(0) == 1 - assert not_gate(1) == 0 - - if __name__ == "__main__": - print(not_gate(0)) - print(not_gate(1)) + import doctest + + doctest.testmod() diff --git a/boolean_algebra/or_gate.py b/boolean_algebra/or_gate.py index aa7e6645e33f..0fd4e5a5dc18 100644 --- a/boolean_algebra/or_gate.py +++ b/boolean_algebra/or_gate.py @@ -29,18 +29,7 @@ def or_gate(input_1: int, input_2: int) -> int: return int((input_1, input_2).count(1) != 0) -def test_or_gate() -> None: - """ - Tests the or_gate function - """ - assert or_gate(0, 0) == 0 - assert or_gate(0, 1) == 1 - assert or_gate(1, 0) == 1 - assert or_gate(1, 1) == 1 - - if __name__ == "__main__": - print(or_gate(0, 1)) - print(or_gate(1, 0)) - print(or_gate(0, 0)) - print(or_gate(1, 1)) + import doctest + + doctest.testmod() diff --git a/boolean_algebra/xnor_gate.py b/boolean_algebra/xnor_gate.py index 45ab2700ec35..05b756da2960 100644 --- a/boolean_algebra/xnor_gate.py +++ b/boolean_algebra/xnor_gate.py @@ -31,18 +31,7 @@ def xnor_gate(input_1: int, input_2: int) -> int: return 1 if input_1 == input_2 else 0 -def test_xnor_gate() -> None: - """ - Tests the xnor_gate function - """ - assert xnor_gate(0, 0) == 1 - assert xnor_gate(0, 1) == 0 - assert xnor_gate(1, 0) == 0 - assert xnor_gate(1, 1) == 1 - - if __name__ == "__main__": - print(xnor_gate(0, 0)) - print(xnor_gate(0, 1)) - print(xnor_gate(1, 0)) - print(xnor_gate(1, 1)) + import doctest + + doctest.testmod() diff --git a/boolean_algebra/xor_gate.py b/boolean_algebra/xor_gate.py index db4f5b45c3c6..f3922e426e3d 100644 --- a/boolean_algebra/xor_gate.py +++ b/boolean_algebra/xor_gate.py @@ -31,16 +31,7 @@ def xor_gate(input_1: int, input_2: int) -> int: return (input_1, input_2).count(0) % 2 -def test_xor_gate() -> None: - """ - Tests the xor_gate function - """ - assert xor_gate(0, 0) == 0 - assert xor_gate(0, 1) == 1 - assert xor_gate(1, 0) == 1 - assert xor_gate(1, 1) == 0 - - if __name__ == "__main__": - print(xor_gate(0, 0)) - print(xor_gate(0, 1)) + import doctest + + doctest.testmod() From 3c14e6ae3aa6506ca8e5baa73321f3a04caf83d0 Mon Sep 17 00:00:00 2001 From: Kamil <32775019+quant12345@users.noreply.github.com> Date: Mon, 16 Oct 2023 12:29:46 +0500 Subject: [PATCH 383/757] Refactoring and optimization of the lu_decomposition algorithm (#9231) * Replacing the generator with numpy vector operations from lu_decomposition. * Revert "Replacing the generator with numpy vector operations from lu_decomposition." This reverts commit ad217c66165898d62b76cc89ba09c2d7049b6448. * Replacing the generator with numpy vector operations from lu_decomposition. --- arithmetic_analysis/lu_decomposition.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/arithmetic_analysis/lu_decomposition.py b/arithmetic_analysis/lu_decomposition.py index eaabce5449c5..094b20abfecc 100644 --- a/arithmetic_analysis/lu_decomposition.py +++ b/arithmetic_analysis/lu_decomposition.py @@ -88,15 +88,19 @@ def lower_upper_decomposition(table: np.ndarray) -> tuple[np.ndarray, np.ndarray lower = np.zeros((rows, columns)) upper = np.zeros((rows, columns)) + + # in 'total', the necessary data is extracted through slices + # and the sum of the products is obtained. + for i in range(columns): for j in range(i): - total = sum(lower[i][k] * upper[k][j] for k in range(j)) + total = np.sum(lower[i, :i] * upper[:i, j]) if upper[j][j] == 0: raise ArithmeticError("No LU decomposition exists") lower[i][j] = (table[i][j] - total) / upper[j][j] lower[i][i] = 1 for j in range(i, columns): - total = sum(lower[i][k] * upper[k][j] for k in range(j)) + total = np.sum(lower[i, :i] * upper[:i, j]) upper[i][j] = table[i][j] - total return lower, upper From e9b3f20cec28b492b2e22e68ea61ec75ce3b9df8 Mon Sep 17 00:00:00 2001 From: hollowcrust <72879387+hollowcrust@users.noreply.github.com> Date: Mon, 16 Oct 2023 16:03:16 +0800 Subject: [PATCH 384/757] Delete dynamic_programming/longest_sub_array.py (#10073) --- dynamic_programming/longest_sub_array.py | 33 ------------------------ 1 file changed, 33 deletions(-) delete mode 100644 dynamic_programming/longest_sub_array.py diff --git a/dynamic_programming/longest_sub_array.py b/dynamic_programming/longest_sub_array.py deleted file mode 100644 index b477acf61e66..000000000000 --- a/dynamic_programming/longest_sub_array.py +++ /dev/null @@ -1,33 +0,0 @@ -""" -Author : Yvonne - -This is a pure Python implementation of Dynamic Programming solution to the - longest_sub_array problem. - -The problem is : -Given an array, to find the longest and continuous sub array and get the max sum of the - sub array in the given array. -""" - - -class SubArray: - def __init__(self, arr): - # we need a list not a string, so do something to change the type - self.array = arr.split(",") - - def solve_sub_array(self): - rear = [int(self.array[0])] * len(self.array) - sum_value = [int(self.array[0])] * len(self.array) - for i in range(1, len(self.array)): - sum_value[i] = max( - int(self.array[i]) + sum_value[i - 1], int(self.array[i]) - ) - rear[i] = max(sum_value[i], rear[i - 1]) - return rear[len(self.array) - 1] - - -if __name__ == "__main__": - whole_array = input("please input some numbers:") - array = SubArray(whole_array) - re = array.solve_sub_array() - print(("the results is:", re)) From 96f81770d7e047f24c3203e913bf346754936330 Mon Sep 17 00:00:00 2001 From: Praful Katare <47990928+Kpraful@users.noreply.github.com> Date: Mon, 16 Oct 2023 13:43:34 +0530 Subject: [PATCH 385/757] Adds Doc test in depth_first_search_2.py (#10094) * Adds Doc test in depth_first_search_2.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fixes depth_first_search_2.py formatting * Cleanup --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Tianyi Zheng --- graphs/depth_first_search_2.py | 80 ++++++++++++++++++++++++++++++---- 1 file changed, 71 insertions(+), 9 deletions(-) diff --git a/graphs/depth_first_search_2.py b/graphs/depth_first_search_2.py index 3072d527c1c7..5ff13af33168 100644 --- a/graphs/depth_first_search_2.py +++ b/graphs/depth_first_search_2.py @@ -9,12 +9,44 @@ def __init__(self): # for printing the Graph vertices def print_graph(self) -> None: + """ + Print the graph vertices. + + Example: + >>> g = Graph() + >>> g.add_edge(0, 1) + >>> g.add_edge(0, 2) + >>> g.add_edge(1, 2) + >>> g.add_edge(2, 0) + >>> g.add_edge(2, 3) + >>> g.add_edge(3, 3) + >>> g.print_graph() + {0: [1, 2], 1: [2], 2: [0, 3], 3: [3]} + 0 -> 1 -> 2 + 1 -> 2 + 2 -> 0 -> 3 + 3 -> 3 + """ print(self.vertex) for i in self.vertex: print(i, " -> ", " -> ".join([str(j) for j in self.vertex[i]])) # for adding the edge between two vertices def add_edge(self, from_vertex: int, to_vertex: int) -> None: + """ + Add an edge between two vertices. + + :param from_vertex: The source vertex. + :param to_vertex: The destination vertex. + + Example: + >>> g = Graph() + >>> g.add_edge(0, 1) + >>> g.add_edge(0, 2) + >>> g.print_graph() + {0: [1, 2]} + 0 -> 1 -> 2 + """ # check if vertex is already present, if from_vertex in self.vertex: self.vertex[from_vertex].append(to_vertex) @@ -23,6 +55,21 @@ def add_edge(self, from_vertex: int, to_vertex: int) -> None: self.vertex[from_vertex] = [to_vertex] def dfs(self) -> None: + """ + Perform depth-first search (DFS) traversal on the graph + and print the visited vertices. + + Example: + >>> g = Graph() + >>> g.add_edge(0, 1) + >>> g.add_edge(0, 2) + >>> g.add_edge(1, 2) + >>> g.add_edge(2, 0) + >>> g.add_edge(2, 3) + >>> g.add_edge(3, 3) + >>> g.dfs() + 0 1 2 3 + """ # visited array for storing already visited nodes visited = [False] * len(self.vertex) @@ -32,18 +79,41 @@ def dfs(self) -> None: self.dfs_recursive(i, visited) def dfs_recursive(self, start_vertex: int, visited: list) -> None: + """ + Perform a recursive depth-first search (DFS) traversal on the graph. + + :param start_vertex: The starting vertex for the traversal. + :param visited: A list to track visited vertices. + + Example: + >>> g = Graph() + >>> g.add_edge(0, 1) + >>> g.add_edge(0, 2) + >>> g.add_edge(1, 2) + >>> g.add_edge(2, 0) + >>> g.add_edge(2, 3) + >>> g.add_edge(3, 3) + >>> visited = [False] * len(g.vertex) + >>> g.dfs_recursive(0, visited) + 0 1 2 3 + """ # mark start vertex as visited visited[start_vertex] = True - print(start_vertex, end=" ") + print(start_vertex, end="") # Recur for all the vertices that are adjacent to this node for i in self.vertex: if not visited[i]: + print(" ", end="") self.dfs_recursive(i, visited) if __name__ == "__main__": + import doctest + + doctest.testmod() + g = Graph() g.add_edge(0, 1) g.add_edge(0, 2) @@ -55,11 +125,3 @@ def dfs_recursive(self, start_vertex: int, visited: list) -> None: g.print_graph() print("DFS:") g.dfs() - - # OUTPUT: - # 0 -> 1 -> 2 - # 1 -> 2 - # 2 -> 0 -> 3 - # 3 -> 3 - # DFS: - # 0 1 2 3 From 69707bf6939d63a93b0d4b278cc367c42a976c6d Mon Sep 17 00:00:00 2001 From: Dwarkadhish Kamthane <72198604+dwarka-9504@users.noreply.github.com> Date: Mon, 16 Oct 2023 13:51:03 +0530 Subject: [PATCH 386/757] Minimization of while loop in Armstrong Numbers (#9976) * Minimization of while loop in Armstrong Numbers The while loop is removed and simple length calculation is used so the task of minimization of while loop is achieved * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- maths/armstrong_numbers.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/maths/armstrong_numbers.py b/maths/armstrong_numbers.py index 26709b428b78..e1c25d4676c3 100644 --- a/maths/armstrong_numbers.py +++ b/maths/armstrong_numbers.py @@ -29,9 +29,7 @@ def armstrong_number(n: int) -> bool: number_of_digits = 0 temp = n # Calculation of digits of the number - while temp > 0: - number_of_digits += 1 - temp //= 10 + number_of_digits = len(str(n)) # Dividing number into separate digits and find Armstrong number temp = n while temp > 0: From 7acf4bf73b5a43bdb375f7a34da227bf6deeaf35 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Mon, 16 Oct 2023 16:16:09 +0200 Subject: [PATCH 387/757] Rename binary_tree_traversals.md to README.md (#10599) --- DIRECTORY.md | 3 +-- .../binary_tree/{binary_tree_traversals.md => README.md} | 0 2 files changed, 1 insertion(+), 2 deletions(-) rename data_structures/binary_tree/{binary_tree_traversals.md => README.md} (100%) diff --git a/DIRECTORY.md b/DIRECTORY.md index cef1e06b78aa..65628be59a92 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -340,7 +340,6 @@ * [Longest Increasing Subsequence](dynamic_programming/longest_increasing_subsequence.py) * [Longest Increasing Subsequence O(Nlogn)](dynamic_programming/longest_increasing_subsequence_o(nlogn).py) * [Longest Palindromic Subsequence](dynamic_programming/longest_palindromic_subsequence.py) - * [Longest Sub Array](dynamic_programming/longest_sub_array.py) * [Matrix Chain Multiplication](dynamic_programming/matrix_chain_multiplication.py) * [Matrix Chain Order](dynamic_programming/matrix_chain_order.py) * [Max Non Adjacent Sum](dynamic_programming/max_non_adjacent_sum.py) @@ -486,6 +485,7 @@ * [Fractional Knapsack](greedy_methods/fractional_knapsack.py) * [Fractional Knapsack 2](greedy_methods/fractional_knapsack_2.py) * [Gas Station](greedy_methods/gas_station.py) + * [Minimum Coin Change](greedy_methods/minimum_coin_change.py) * [Minimum Waiting Time](greedy_methods/minimum_waiting_time.py) * [Optimal Merge Pattern](greedy_methods/optimal_merge_pattern.py) @@ -618,7 +618,6 @@ * [Gcd Of N Numbers](maths/gcd_of_n_numbers.py) * [Germain Primes](maths/germain_primes.py) * [Greatest Common Divisor](maths/greatest_common_divisor.py) - * [Greedy Coin Change](maths/greedy_coin_change.py) * [Hamming Numbers](maths/hamming_numbers.py) * [Hardy Ramanujanalgo](maths/hardy_ramanujanalgo.py) * [Harshad Numbers](maths/harshad_numbers.py) diff --git a/data_structures/binary_tree/binary_tree_traversals.md b/data_structures/binary_tree/README.md similarity index 100% rename from data_structures/binary_tree/binary_tree_traversals.md rename to data_structures/binary_tree/README.md From 3923e590d77979de31fabd4df34e69e8933e690d Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Mon, 16 Oct 2023 16:17:48 +0200 Subject: [PATCH 388/757] Tree_sort.py: Disable slow doctest (#10584) --- sorts/tree_sort.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sorts/tree_sort.py b/sorts/tree_sort.py index e63a3253ba19..dc95856f44c8 100644 --- a/sorts/tree_sort.py +++ b/sorts/tree_sort.py @@ -52,8 +52,9 @@ def tree_sort(arr: list[int]) -> tuple[int, ...]: (-4, 2, 5, 7, 9) >>> tree_sort([5, 6, 1, -1, 4, 37, 2, 7]) (-1, 1, 2, 4, 5, 6, 7, 37) - >>> tree_sort(range(10, -10, -1)) == tuple(sorted(range(10, -10, -1))) - True + + # >>> tree_sort(range(10, -10, -1)) == tuple(sorted(range(10, -10, -1))) + # True """ if len(arr) == 0: return tuple(arr) From c15dda405a26bd9cb1554a43598c4c85a6320d4c Mon Sep 17 00:00:00 2001 From: Saswat Susmoy <72549122+Saswatsusmoy@users.noreply.github.com> Date: Mon, 16 Oct 2023 20:13:53 +0530 Subject: [PATCH 389/757] Update basic_binary_tree.py (#10388) * Update basic_binary_tree.py * Update basic_binary_tree.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: Christian Clauss Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .../binary_tree/basic_binary_tree.py | 197 +++++++++--------- 1 file changed, 103 insertions(+), 94 deletions(-) diff --git a/data_structures/binary_tree/basic_binary_tree.py b/data_structures/binary_tree/basic_binary_tree.py index 65dccf247b51..0439413d95b5 100644 --- a/data_structures/binary_tree/basic_binary_tree.py +++ b/data_structures/binary_tree/basic_binary_tree.py @@ -1,101 +1,110 @@ from __future__ import annotations +from collections.abc import Iterator +from dataclasses import dataclass + +@dataclass class Node: - """ - A Node has data variable and pointers to Nodes to its left and right. - """ - - def __init__(self, data: int) -> None: - self.data = data - self.left: Node | None = None - self.right: Node | None = None - - -def display(tree: Node | None) -> None: # In Order traversal of the tree - """ - >>> root = Node(1) - >>> root.left = Node(0) - >>> root.right = Node(2) - >>> display(root) - 0 - 1 - 2 - >>> display(root.right) - 2 - """ - if tree: - display(tree.left) - print(tree.data) - display(tree.right) - - -def depth_of_tree(tree: Node | None) -> int: - """ - Recursive function that returns the depth of a binary tree. - - >>> root = Node(0) - >>> depth_of_tree(root) - 1 - >>> root.left = Node(0) - >>> depth_of_tree(root) - 2 - >>> root.right = Node(0) - >>> depth_of_tree(root) - 2 - >>> root.left.right = Node(0) - >>> depth_of_tree(root) - 3 - >>> depth_of_tree(root.left) - 2 - """ - return 1 + max(depth_of_tree(tree.left), depth_of_tree(tree.right)) if tree else 0 - - -def is_full_binary_tree(tree: Node) -> bool: - """ - Returns True if this is a full binary tree - - >>> root = Node(0) - >>> is_full_binary_tree(root) - True - >>> root.left = Node(0) - >>> is_full_binary_tree(root) - False - >>> root.right = Node(0) - >>> is_full_binary_tree(root) - True - >>> root.left.left = Node(0) - >>> is_full_binary_tree(root) - False - >>> root.right.right = Node(0) - >>> is_full_binary_tree(root) - False - """ - if not tree: - return True - if tree.left and tree.right: - return is_full_binary_tree(tree.left) and is_full_binary_tree(tree.right) - else: - return not tree.left and not tree.right - - -def main() -> None: # Main function for testing. - tree = Node(1) - tree.left = Node(2) - tree.right = Node(3) - tree.left.left = Node(4) - tree.left.right = Node(5) - tree.left.right.left = Node(6) - tree.right.left = Node(7) - tree.right.left.left = Node(8) - tree.right.left.left.right = Node(9) - - print(is_full_binary_tree(tree)) - print(depth_of_tree(tree)) - print("Tree is: ") - display(tree) + data: int + left: Node | None = None + right: Node | None = None + + def __iter__(self) -> Iterator[int]: + if self.left: + yield from self.left + yield self.data + if self.right: + yield from self.right + + def __len__(self) -> int: + return sum(1 for _ in self) + + def is_full(self) -> bool: + if not self or (not self.left and not self.right): + return True + if self.left and self.right: + return self.left.is_full() and self.right.is_full() + return False + + +@dataclass +class BinaryTree: + root: Node + + def __iter__(self) -> Iterator[int]: + return iter(self.root) + + def __len__(self) -> int: + return len(self.root) + + @classmethod + def small_tree(cls) -> BinaryTree: + """ + Return a small binary tree with 3 nodes. + >>> binary_tree = BinaryTree.small_tree() + >>> len(binary_tree) + 3 + >>> list(binary_tree) + [1, 2, 3] + """ + binary_tree = BinaryTree(Node(2)) + binary_tree.root.left = Node(1) + binary_tree.root.right = Node(3) + return binary_tree + + @classmethod + def medium_tree(cls) -> BinaryTree: + """ + Return a medium binary tree with 3 nodes. + >>> binary_tree = BinaryTree.medium_tree() + >>> len(binary_tree) + 7 + >>> list(binary_tree) + [1, 2, 3, 4, 5, 6, 7] + """ + binary_tree = BinaryTree(Node(4)) + binary_tree.root.left = two = Node(2) + two.left = Node(1) + two.right = Node(3) + binary_tree.root.right = five = Node(5) + five.right = six = Node(6) + six.right = Node(7) + return binary_tree + + def depth(self) -> int: + """ + Returns the depth of the tree + + >>> BinaryTree(Node(1)).depth() + 1 + >>> BinaryTree.small_tree().depth() + 2 + >>> BinaryTree.medium_tree().depth() + 4 + """ + return self._depth(self.root) + + def _depth(self, node: Node | None) -> int: # noqa: UP007 + if not node: + return 0 + return 1 + max(self._depth(node.left), self._depth(node.right)) + + def is_full(self) -> bool: + """ + Returns True if the tree is full + + >>> BinaryTree(Node(1)).is_full() + True + >>> BinaryTree.small_tree().is_full() + True + >>> BinaryTree.medium_tree().is_full() + False + """ + return self.root.is_full() if __name__ == "__main__": - main() + import doctest + + doctest.testmod() From 5a1305b6fe98808bf534c54e12ac64c1e4e4ce0f Mon Sep 17 00:00:00 2001 From: ivan53 Date: Mon, 16 Oct 2023 07:48:26 -0700 Subject: [PATCH 390/757] Fix benchmark to test with the provided number instead on 25 (#10587) --- bit_manipulation/count_number_of_one_bits.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/bit_manipulation/count_number_of_one_bits.py b/bit_manipulation/count_number_of_one_bits.py index a1687503a383..f0c9f927620a 100644 --- a/bit_manipulation/count_number_of_one_bits.py +++ b/bit_manipulation/count_number_of_one_bits.py @@ -70,11 +70,13 @@ def do_benchmark(number: int) -> None: setup = "import __main__ as z" print(f"Benchmark when {number = }:") print(f"{get_set_bits_count_using_modulo_operator(number) = }") - timing = timeit("z.get_set_bits_count_using_modulo_operator(25)", setup=setup) + timing = timeit( + f"z.get_set_bits_count_using_modulo_operator({number})", setup=setup + ) print(f"timeit() runs in {timing} seconds") print(f"{get_set_bits_count_using_brian_kernighans_algorithm(number) = }") timing = timeit( - "z.get_set_bits_count_using_brian_kernighans_algorithm(25)", + f"z.get_set_bits_count_using_brian_kernighans_algorithm({number})", setup=setup, ) print(f"timeit() runs in {timing} seconds") From 778e2010d6ae89c61a93672e49b86041b6ca1108 Mon Sep 17 00:00:00 2001 From: Vinayak Upadhyay Date: Mon, 16 Oct 2023 22:16:44 +0530 Subject: [PATCH 391/757] Added functionality to calculate the diameter of given binary tree (#10526) * Added code to find diameter of given binary tree * Modified diameter_of_binary_tree file * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update diameter_of_binary_tree.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update diameter_of_binary_tree.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- .../binary_tree/diameter_of_binary_tree.py | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 data_structures/binary_tree/diameter_of_binary_tree.py diff --git a/data_structures/binary_tree/diameter_of_binary_tree.py b/data_structures/binary_tree/diameter_of_binary_tree.py new file mode 100644 index 000000000000..bbe70b028d24 --- /dev/null +++ b/data_structures/binary_tree/diameter_of_binary_tree.py @@ -0,0 +1,72 @@ +""" +The diameter/width of a tree is defined as the number of nodes on the longest path +between two end nodes. +""" +from __future__ import annotations + +from dataclasses import dataclass + + +@dataclass +class Node: + data: int + left: Node | None = None + right: Node | None = None + + def depth(self) -> int: + """ + >>> root = Node(1) + >>> root.depth() + 1 + >>> root.left = Node(2) + >>> root.depth() + 2 + >>> root.left.depth() + 1 + >>> root.right = Node(3) + >>> root.depth() + 2 + """ + left_depth = self.left.depth() if self.left else 0 + right_depth = self.right.depth() if self.right else 0 + return max(left_depth, right_depth) + 1 + + def diameter(self) -> int: + """ + >>> root = Node(1) + >>> root.diameter() + 1 + >>> root.left = Node(2) + >>> root.diameter() + 2 + >>> root.left.diameter() + 1 + >>> root.right = Node(3) + >>> root.diameter() + 3 + """ + left_depth = self.left.depth() if self.left else 0 + right_depth = self.right.depth() if self.right else 0 + return left_depth + right_depth + 1 + + +if __name__ == "__main__": + from doctest import testmod + + testmod() + root = Node(1) + root.left = Node(2) + root.right = Node(3) + root.left.left = Node(4) + root.left.right = Node(5) + r""" + Constructed binary tree is + 1 + / \ + 2 3 + / \ + 4 5 + """ + print(f"{root.diameter() = }") # 4 + print(f"{root.left.diameter() = }") # 3 + print(f"{root.right.diameter() = }") # 1 From 1e468c1028e407ea38bd7e9511dc0f3d0d45a6e0 Mon Sep 17 00:00:00 2001 From: halfhearted <99018821+Arunsiva003@users.noreply.github.com> Date: Mon, 16 Oct 2023 22:42:33 +0530 Subject: [PATCH 392/757] Floor and ceil in Binary search tree added (#10432) * earliest deadline first scheduling algo added * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * earliest deadline first scheduling algo added * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * ceil and floor and bst * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * ceil and floor and bst 2 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * ceil and floor and bst 3 * Update and rename floor_ceil_in_bst.py to floor_and_ceiling.py * Delete scheduling/shortest_deadline_first.py --------- Co-authored-by: ArunSiva Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- .../binary_tree/floor_and_ceiling.py | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 data_structures/binary_tree/floor_and_ceiling.py diff --git a/data_structures/binary_tree/floor_and_ceiling.py b/data_structures/binary_tree/floor_and_ceiling.py new file mode 100644 index 000000000000..f8a1adbd967b --- /dev/null +++ b/data_structures/binary_tree/floor_and_ceiling.py @@ -0,0 +1,87 @@ +""" +In a binary search tree (BST): +* The floor of key 'k' is the maximum value that is smaller than or equal to 'k'. +* The ceiling of key 'k' is the minimum value that is greater than or equal to 'k'. + +Reference: +https://bit.ly/46uB0a2 + +Author : Arunkumar +Date : 14th October 2023 +""" +from __future__ import annotations + +from collections.abc import Iterator +from dataclasses import dataclass + + +@dataclass +class Node: + key: int + left: Node | None = None + right: Node | None = None + + def __iter__(self) -> Iterator[int]: + if self.left: + yield from self.left + yield self.key + if self.right: + yield from self.right + + def __len__(self) -> int: + return sum(1 for _ in self) + + +def floor_ceiling(root: Node | None, key: int) -> tuple[int | None, int | None]: + """ + Find the floor and ceiling values for a given key in a Binary Search Tree (BST). + + Args: + root: The root of the binary search tree. + key: The key for which to find the floor and ceiling. + + Returns: + A tuple containing the floor and ceiling values, respectively. + + Examples: + >>> root = Node(10) + >>> root.left = Node(5) + >>> root.right = Node(20) + >>> root.left.left = Node(3) + >>> root.left.right = Node(7) + >>> root.right.left = Node(15) + >>> root.right.right = Node(25) + >>> tuple(root) + (3, 5, 7, 10, 15, 20, 25) + >>> floor_ceiling(root, 8) + (7, 10) + >>> floor_ceiling(root, 14) + (10, 15) + >>> floor_ceiling(root, -1) + (None, 3) + >>> floor_ceiling(root, 30) + (25, None) + """ + floor_val = None + ceiling_val = None + + while root: + if root.key == key: + floor_val = root.key + ceiling_val = root.key + break + + if key < root.key: + ceiling_val = root.key + root = root.left + else: + floor_val = root.key + root = root.right + + return floor_val, ceiling_val + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 922bbee80ce292ca27eee33d38e82ecf73e33dcd Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 16 Oct 2023 20:23:33 +0200 Subject: [PATCH 393/757] [pre-commit.ci] pre-commit autoupdate (#10613) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/abravalheri/validate-pyproject: v0.14 → v0.15](https://github.com/abravalheri/validate-pyproject/compare/v0.14...v0.15) * updating DIRECTORY.md --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- DIRECTORY.md | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 84f4a7770d00..b3def463ded2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -46,7 +46,7 @@ repos: pass_filenames: false - repo: https://github.com/abravalheri/validate-pyproject - rev: v0.14 + rev: v0.15 hooks: - id: validate-pyproject diff --git a/DIRECTORY.md b/DIRECTORY.md index 65628be59a92..d878f1c79a2d 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -192,10 +192,12 @@ * [Binary Tree Node Sum](data_structures/binary_tree/binary_tree_node_sum.py) * [Binary Tree Path Sum](data_structures/binary_tree/binary_tree_path_sum.py) * [Binary Tree Traversals](data_structures/binary_tree/binary_tree_traversals.py) + * [Diameter Of Binary Tree](data_structures/binary_tree/diameter_of_binary_tree.py) * [Diff Views Of Binary Tree](data_structures/binary_tree/diff_views_of_binary_tree.py) * [Distribute Coins](data_structures/binary_tree/distribute_coins.py) * [Fenwick Tree](data_structures/binary_tree/fenwick_tree.py) * [Flatten Binarytree To Linkedlist](data_structures/binary_tree/flatten_binarytree_to_linkedlist.py) + * [Floor And Ceiling](data_structures/binary_tree/floor_and_ceiling.py) * [Inorder Tree Traversal 2022](data_structures/binary_tree/inorder_tree_traversal_2022.py) * [Is Bst](data_structures/binary_tree/is_bst.py) * [Lazy Segment Tree](data_structures/binary_tree/lazy_segment_tree.py) From fcea18c9f0b68e2ba35c8f91bf0702d7c727c4df Mon Sep 17 00:00:00 2001 From: Adarsh Sidnal <97141741+Adarshsidnal@users.noreply.github.com> Date: Tue, 17 Oct 2023 04:26:14 +0530 Subject: [PATCH 394/757] Added an algorithm transfrom bst to greater sum tree (#9777) * Added an algorithm transfrom bst to greater sum tree * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update and rename transform_bst_sum_tree.py to is_sum_tree.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- data_structures/binary_tree/is_sum_tree.py | 161 +++++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 data_structures/binary_tree/is_sum_tree.py diff --git a/data_structures/binary_tree/is_sum_tree.py b/data_structures/binary_tree/is_sum_tree.py new file mode 100644 index 000000000000..3f9cf1d560a6 --- /dev/null +++ b/data_structures/binary_tree/is_sum_tree.py @@ -0,0 +1,161 @@ +""" +Is a binary tree a sum tree where the value of every non-leaf node is equal to the sum +of the values of its left and right subtrees? +https://www.geeksforgeeks.org/check-if-a-given-binary-tree-is-sumtree +""" +from __future__ import annotations + +from collections.abc import Iterator +from dataclasses import dataclass + + +@dataclass +class Node: + data: int + left: Node | None = None + right: Node | None = None + + def __iter__(self) -> Iterator[int]: + """ + >>> root = Node(2) + >>> list(root) + [2] + >>> root.left = Node(1) + >>> tuple(root) + (1, 2) + """ + if self.left: + yield from self.left + yield self.data + if self.right: + yield from self.right + + def __len__(self) -> int: + """ + >>> root = Node(2) + >>> len(root) + 1 + >>> root.left = Node(1) + >>> len(root) + 2 + """ + return sum(1 for _ in self) + + @property + def is_sum_node(self) -> bool: + """ + >>> root = Node(3) + >>> root.is_sum_node + True + >>> root.left = Node(1) + >>> root.is_sum_node + False + >>> root.right = Node(2) + >>> root.is_sum_node + True + """ + if not self.left and not self.right: + return True # leaf nodes are considered sum nodes + left_sum = sum(self.left) if self.left else 0 + right_sum = sum(self.right) if self.right else 0 + return all( + ( + self.data == left_sum + right_sum, + self.left.is_sum_node if self.left else True, + self.right.is_sum_node if self.right else True, + ) + ) + + +@dataclass +class BinaryTree: + root: Node + + def __iter__(self) -> Iterator[int]: + """ + >>> list(BinaryTree.build_a_tree()) + [1, 2, 7, 11, 15, 29, 35, 40] + """ + return iter(self.root) + + def __len__(self) -> int: + """ + >>> len(BinaryTree.build_a_tree()) + 8 + """ + return len(self.root) + + def __str__(self) -> str: + """ + Returns a string representation of the inorder traversal of the binary tree. + + >>> str(list(BinaryTree.build_a_tree())) + '[1, 2, 7, 11, 15, 29, 35, 40]' + """ + return str(list(self)) + + @property + def is_sum_tree(self) -> bool: + """ + >>> BinaryTree.build_a_tree().is_sum_tree + False + >>> BinaryTree.build_a_sum_tree().is_sum_tree + True + """ + return self.root.is_sum_node + + @classmethod + def build_a_tree(cls) -> BinaryTree: + r""" + Create a binary tree with the specified structure: + 11 + / \ + 2 29 + / \ / \ + 1 7 15 40 + \ + 35 + >>> list(BinaryTree.build_a_tree()) + [1, 2, 7, 11, 15, 29, 35, 40] + """ + tree = BinaryTree(Node(11)) + root = tree.root + root.left = Node(2) + root.right = Node(29) + root.left.left = Node(1) + root.left.right = Node(7) + root.right.left = Node(15) + root.right.right = Node(40) + root.right.right.left = Node(35) + return tree + + @classmethod + def build_a_sum_tree(cls) -> BinaryTree: + r""" + Create a binary tree with the specified structure: + 26 + / \ + 10 3 + / \ \ + 4 6 3 + >>> list(BinaryTree.build_a_sum_tree()) + [4, 10, 6, 26, 3, 3] + """ + tree = BinaryTree(Node(26)) + root = tree.root + root.left = Node(10) + root.right = Node(3) + root.left.left = Node(4) + root.left.right = Node(6) + root.right.right = Node(3) + return tree + + +if __name__ == "__main__": + from doctest import testmod + + testmod() + tree = BinaryTree.build_a_tree() + print(f"{tree} has {len(tree)} nodes and {tree.is_sum_tree = }.") + tree = BinaryTree.build_a_sum_tree() + print(f"{tree} has {len(tree)} nodes and {tree.is_sum_tree = }.") From 5f629b60499cfb3ac27f6520bf947764b5b45c28 Mon Sep 17 00:00:00 2001 From: Sandeepa Dilshan Alagiyawanna <108791571+SandeepaDilshanAlagiyawanna@users.noreply.github.com> Date: Tue, 17 Oct 2023 04:47:49 +0530 Subject: [PATCH 395/757] Optimize and_gate and nand_gate (#10591) * Added more optimized sudoku solver algorithm * Added more optimized sudoku solver algorithm and File Renamed * and_gate is Optimized * and_gate is Optimized * and_gate is Optimized * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- boolean_algebra/and_gate.py | 2 +- boolean_algebra/nand_gate.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/boolean_algebra/and_gate.py b/boolean_algebra/and_gate.py index f0fd45c9f81e..6ae66b5b0a77 100644 --- a/boolean_algebra/and_gate.py +++ b/boolean_algebra/and_gate.py @@ -29,7 +29,7 @@ def and_gate(input_1: int, input_2: int) -> int: >>> and_gate(1, 1) 1 """ - return int((input_1, input_2).count(0) == 0) + return int(input_1 and input_2) if __name__ == "__main__": diff --git a/boolean_algebra/nand_gate.py b/boolean_algebra/nand_gate.py index 80f9d12db89a..ea7a6815dcc9 100644 --- a/boolean_algebra/nand_gate.py +++ b/boolean_algebra/nand_gate.py @@ -27,7 +27,7 @@ def nand_gate(input_1: int, input_2: int) -> int: >>> nand_gate(1, 1) 0 """ - return int((input_1, input_2).count(0) != 0) + return int(not (input_1 and input_2)) if __name__ == "__main__": From b5786c87d820cc4d68707731df0812507063bf8b Mon Sep 17 00:00:00 2001 From: aryandgandhi <44215148+aryandgandhi@users.noreply.github.com> Date: Mon, 16 Oct 2023 20:25:07 -0500 Subject: [PATCH 396/757] update segmenttree docstrings Fixes #9943 (#9975) * update docstrings * update docstrings * update docstrings --- data_structures/binary_tree/segment_tree.py | 41 ++++++++++++++++++++- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/data_structures/binary_tree/segment_tree.py b/data_structures/binary_tree/segment_tree.py index 5f822407d8cb..3b0b32946f6e 100644 --- a/data_structures/binary_tree/segment_tree.py +++ b/data_structures/binary_tree/segment_tree.py @@ -3,7 +3,8 @@ class SegmentTree: def __init__(self, a): - self.N = len(a) + self.A = a + self.N = len(self.A) self.st = [0] * ( 4 * self.N ) # approximate the overall size of segment tree with array N @@ -11,14 +12,32 @@ def __init__(self, a): self.build(1, 0, self.N - 1) def left(self, idx): + """ + Returns the left child index for a given index in a binary tree. + + >>> s = SegmentTree([1, 2, 3]) + >>> s.left(1) + 2 + >>> s.left(2) + 4 + """ return idx * 2 def right(self, idx): + """ + Returns the right child index for a given index in a binary tree. + + >>> s = SegmentTree([1, 2, 3]) + >>> s.right(1) + 3 + >>> s.right(2) + 5 + """ return idx * 2 + 1 def build(self, idx, l, r): # noqa: E741 if l == r: - self.st[idx] = A[l] + self.st[idx] = self.A[l] else: mid = (l + r) // 2 self.build(self.left(idx), l, mid) @@ -26,6 +45,15 @@ def build(self, idx, l, r): # noqa: E741 self.st[idx] = max(self.st[self.left(idx)], self.st[self.right(idx)]) def update(self, a, b, val): + """ + Update the values in the segment tree in the range [a,b] with the given value. + + >>> s = SegmentTree([1, 2, 3, 4, 5]) + >>> s.update(2, 4, 10) + True + >>> s.query(1, 5) + 10 + """ return self.update_recursive(1, 0, self.N - 1, a - 1, b - 1, val) def update_recursive(self, idx, l, r, a, b, val): # noqa: E741 @@ -44,6 +72,15 @@ def update_recursive(self, idx, l, r, a, b, val): # noqa: E741 return True def query(self, a, b): + """ + Query the maximum value in the range [a,b]. + + >>> s = SegmentTree([1, 2, 3, 4, 5]) + >>> s.query(1, 3) + 3 + >>> s.query(1, 5) + 5 + """ return self.query_recursive(1, 0, self.N - 1, a - 1, b - 1) def query_recursive(self, idx, l, r, a, b): # noqa: E741 From 00165a5fb2d125c7e6ab33e424bdcac8dec2b5b6 Mon Sep 17 00:00:00 2001 From: Saurabh Mahapatra <98408932+its-100rabh@users.noreply.github.com> Date: Tue, 17 Oct 2023 12:06:12 +0530 Subject: [PATCH 397/757] Added test cases to join.py (#10629) * Added test cases to join.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- strings/join.py | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/strings/join.py b/strings/join.py index 739856c1aa93..5c02f65a20ce 100644 --- a/strings/join.py +++ b/strings/join.py @@ -1,10 +1,21 @@ """ -Program to join a list of strings with a given separator +Program to join a list of strings with a separator """ def join(separator: str, separated: list[str]) -> str: """ + Joins a list of strings using a separator + and returns the result. + + :param separator: Separator to be used + for joining the strings. + :param separated: List of strings to be joined. + + :return: Joined string with the specified separator. + + Examples: + >>> join("", ["a", "b", "c", "d"]) 'abcd' >>> join("#", ["a", "b", "c", "d"]) @@ -13,16 +24,27 @@ def join(separator: str, separated: list[str]) -> str: 'a' >>> join(" ", ["You", "are", "amazing!"]) 'You are amazing!' + + This example should raise an + exception for non-string elements: >>> join("#", ["a", "b", "c", 1]) Traceback (most recent call last): ... - Exception: join() accepts only strings to be joined + Exception: join() accepts only strings + + Additional test case with a different separator: + >>> join("-", ["apple", "banana", "cherry"]) + 'apple-banana-cherry' """ + joined = "" for word_or_phrase in separated: if not isinstance(word_or_phrase, str): - raise Exception("join() accepts only strings to be joined") + raise Exception("join() accepts only strings") joined += word_or_phrase + separator + + # Remove the trailing separator + # by stripping it from the result return joined.strip(separator) From c6c3bd339947eb6f10f77754f34a49915799c82f Mon Sep 17 00:00:00 2001 From: Kushagra Agarwal <94402194+developer-kush@users.noreply.github.com> Date: Tue, 17 Oct 2023 12:40:24 +0530 Subject: [PATCH 398/757] Hacktoberfest: Added Octal Number to Hexadecimal Number Conversion Algorithm (#10533) * Added Octal to Hexadecimal Conversion program under 'conversions' directory * Update conversions/octal_to_hexadecimal.py fix: minor improvement to directly return hexadecimal value Co-authored-by: Tianyi Zheng * Update conversions/octal_to_hexadecimal.py fix: improvement updates to octal to hexadecimal Co-authored-by: Tianyi Zheng * Update conversions/octal_to_hexadecimal.py fix: Readablility improvements to octal to hexadecimal convertor Co-authored-by: Tianyi Zheng * Update conversions/octal_to_hexadecimal.py fix: readability improvements in octal_to_hexadecimal.py Co-authored-by: Tianyi Zheng * Update conversions/octal_to_hexadecimal.py fix: readability improvements in octal_to_hexadecimal.py Co-authored-by: Tianyi Zheng * fix: Fixed all the errors in octal_to_hexadecimal.py after commiting suggested changes * fix: modified the prefix of hex numbers to the '0x' standard in octal_to_hexadecimal.py --------- Co-authored-by: Tianyi Zheng --- conversions/octal_to_hexadecimal.py | 65 +++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 conversions/octal_to_hexadecimal.py diff --git a/conversions/octal_to_hexadecimal.py b/conversions/octal_to_hexadecimal.py new file mode 100644 index 000000000000..0615d79b5c53 --- /dev/null +++ b/conversions/octal_to_hexadecimal.py @@ -0,0 +1,65 @@ +def octal_to_hex(octal: str) -> str: + """ + Convert an Octal number to Hexadecimal number. + For more information: https://en.wikipedia.org/wiki/Octal + + >>> octal_to_hex("100") + '0x40' + >>> octal_to_hex("235") + '0x9D' + >>> octal_to_hex(17) + Traceback (most recent call last): + ... + TypeError: Expected a string as input + >>> octal_to_hex("Av") + Traceback (most recent call last): + ... + ValueError: Not a Valid Octal Number + >>> octal_to_hex("") + Traceback (most recent call last): + ... + ValueError: Empty string was passed to the function + """ + + if not isinstance(octal, str): + raise TypeError("Expected a string as input") + if octal.startswith("0o"): + octal = octal[2:] + if octal == "": + raise ValueError("Empty string was passed to the function") + if any(char not in "01234567" for char in octal): + raise ValueError("Not a Valid Octal Number") + + decimal = 0 + for char in octal: + decimal <<= 3 + decimal |= int(char) + + hex_char = "0123456789ABCDEF" + + revhex = "" + while decimal: + revhex += hex_char[decimal & 15] + decimal >>= 4 + + return "0x" + revhex[::-1] + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + + nums = ["030", "100", "247", "235", "007"] + + ## Main Tests + + for num in nums: + hexadecimal = octal_to_hex(num) + expected = "0x" + hex(int(num, 8))[2:].upper() + + assert hexadecimal == expected + + print(f"Hex of '0o{num}' is: {hexadecimal}") + print(f"Expected was: {expected}") + print("---") From ac3bd1032c02ff5c2f6eb16f2bf5a1b24d106d1c Mon Sep 17 00:00:00 2001 From: ojas wani <52542740+ojas-wani@users.noreply.github.com> Date: Tue, 17 Oct 2023 02:25:25 -0700 Subject: [PATCH 399/757] Add matrix_multiplication (#10045) * added laplacian_filter file * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * updated laplacian.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * updated laplacian_py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * updated laplacian_filter.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * updated laplacian_filter.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * updated laplacian_filter.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * required changes to laplacian file * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * changed laplacian_filter.py * changed laplacian_filter.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * changed laplacian_filter.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * changed laplacian_filter.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * updated laplacian_filter.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * update laplacian_filter.py * update laplacian_filter.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * update laplacian_filter.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * changed laplacian_filter.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * changed laplacian_filter.py * changed laplacian_filter.py * changed laplacian_filter.py * add matrix_multiplication.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * update matrix_multiplication * update matrix_multiplication * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * make changes * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * update * update * updates * resolve conflict * add doctest * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * make changes * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * update laplacian.py * add doctests * more doctest added * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * try to resolve ruff error * try to reslve ruff error * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * update doctest * attemp - resolve ruff error * resolve build error * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * resolve build issue * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * update build * doctest update * update doctest * update doctest * update doctest * fix ruff error * file location changed * Delete digital_image_processing/filters/laplacian_filter.py * Create laplacian_filter.py * Update matrix_multiplication_recursion.py * Update matrix_multiplication_recursion.py * Update matrix_multiplication_recursion.py * Update matrix_multiplication_recursion.py * Update matrix_multiplication_recursion.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- matrix/matrix_multiplication_recursion.py | 180 ++++++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100644 matrix/matrix_multiplication_recursion.py diff --git a/matrix/matrix_multiplication_recursion.py b/matrix/matrix_multiplication_recursion.py new file mode 100644 index 000000000000..287142480ce7 --- /dev/null +++ b/matrix/matrix_multiplication_recursion.py @@ -0,0 +1,180 @@ +# @Author : ojas-wani +# @File : matrix_multiplication_recursion.py +# @Date : 10/06/2023 + + +""" +Perform matrix multiplication using a recursive algorithm. +https://en.wikipedia.org/wiki/Matrix_multiplication +""" +# type Matrix = list[list[int]] # psf/black currenttly fails on this line +Matrix = list[list[int]] + +matrix_1_to_4 = [ + [1, 2], + [3, 4], +] + +matrix_5_to_8 = [ + [5, 6], + [7, 8], +] + +matrix_5_to_9_high = [ + [5, 6], + [7, 8], + [9], +] + +matrix_5_to_9_wide = [ + [5, 6], + [7, 8, 9], +] + +matrix_count_up = [ + [1, 2, 3, 4], + [5, 6, 7, 8], + [9, 10, 11, 12], + [13, 14, 15, 16], +] + +matrix_unordered = [ + [5, 8, 1, 2], + [6, 7, 3, 0], + [4, 5, 9, 1], + [2, 6, 10, 14], +] +matrices = ( + matrix_1_to_4, + matrix_5_to_8, + matrix_5_to_9_high, + matrix_5_to_9_wide, + matrix_count_up, + matrix_unordered, +) + + +def is_square(matrix: Matrix) -> bool: + """ + >>> is_square([]) + True + >>> is_square(matrix_1_to_4) + True + >>> is_square(matrix_5_to_9_high) + False + """ + len_matrix = len(matrix) + return all(len(row) == len_matrix for row in matrix) + + +def matrix_multiply(matrix_a: Matrix, matrix_b: Matrix) -> Matrix: + """ + >>> matrix_multiply(matrix_1_to_4, matrix_5_to_8) + [[19, 22], [43, 50]] + """ + return [ + [sum(a * b for a, b in zip(row, col)) for col in zip(*matrix_b)] + for row in matrix_a + ] + + +def matrix_multiply_recursive(matrix_a: Matrix, matrix_b: Matrix) -> Matrix: + """ + :param matrix_a: A square Matrix. + :param matrix_b: Another square Matrix with the same dimensions as matrix_a. + :return: Result of matrix_a * matrix_b. + :raises ValueError: If the matrices cannot be multiplied. + + >>> matrix_multiply_recursive([], []) + [] + >>> matrix_multiply_recursive(matrix_1_to_4, matrix_5_to_8) + [[19, 22], [43, 50]] + >>> matrix_multiply_recursive(matrix_count_up, matrix_unordered) + [[37, 61, 74, 61], [105, 165, 166, 129], [173, 269, 258, 197], [241, 373, 350, 265]] + >>> matrix_multiply_recursive(matrix_1_to_4, matrix_5_to_9_wide) + Traceback (most recent call last): + ... + ValueError: Invalid matrix dimensions + >>> matrix_multiply_recursive(matrix_1_to_4, matrix_5_to_9_high) + Traceback (most recent call last): + ... + ValueError: Invalid matrix dimensions + >>> matrix_multiply_recursive(matrix_1_to_4, matrix_count_up) + Traceback (most recent call last): + ... + ValueError: Invalid matrix dimensions + """ + if not matrix_a or not matrix_b: + return [] + if not all( + (len(matrix_a) == len(matrix_b), is_square(matrix_a), is_square(matrix_b)) + ): + raise ValueError("Invalid matrix dimensions") + + # Initialize the result matrix with zeros + result = [[0] * len(matrix_b[0]) for _ in range(len(matrix_a))] + + # Recursive multiplication of matrices + def multiply( + i_loop: int, + j_loop: int, + k_loop: int, + matrix_a: Matrix, + matrix_b: Matrix, + result: Matrix, + ) -> None: + """ + :param matrix_a: A square Matrix. + :param matrix_b: Another square Matrix with the same dimensions as matrix_a. + :param result: Result matrix + :param i: Index used for iteration during multiplication. + :param j: Index used for iteration during multiplication. + :param k: Index used for iteration during multiplication. + >>> 0 > 1 # Doctests in inner functions are never run + True + """ + if i_loop >= len(matrix_a): + return + if j_loop >= len(matrix_b[0]): + return multiply(i_loop + 1, 0, 0, matrix_a, matrix_b, result) + if k_loop >= len(matrix_b): + return multiply(i_loop, j_loop + 1, 0, matrix_a, matrix_b, result) + result[i_loop][j_loop] += matrix_a[i_loop][k_loop] * matrix_b[k_loop][j_loop] + return multiply(i_loop, j_loop, k_loop + 1, matrix_a, matrix_b, result) + + # Perform the recursive matrix multiplication + multiply(0, 0, 0, matrix_a, matrix_b, result) + return result + + +if __name__ == "__main__": + from doctest import testmod + + failure_count, test_count = testmod() + if not failure_count: + matrix_a = matrices[0] + for matrix_b in matrices[1:]: + print("Multiplying:") + for row in matrix_a: + print(row) + print("By:") + for row in matrix_b: + print(row) + print("Result:") + try: + result = matrix_multiply_recursive(matrix_a, matrix_b) + for row in result: + print(row) + assert result == matrix_multiply(matrix_a, matrix_b) + except ValueError as e: + print(f"{e!r}") + print() + matrix_a = matrix_b + + print("Benchmark:") + from functools import partial + from timeit import timeit + + mytimeit = partial(timeit, globals=globals(), number=100_000) + for func in ("matrix_multiply", "matrix_multiply_recursive"): + print(f"{func:>25}(): {mytimeit(f'{func}(matrix_count_up, matrix_unordered)')}") From 72bd653e04a944f51ae6c047204b62d8a07db9d4 Mon Sep 17 00:00:00 2001 From: RaymondDashWu <33266041+RaymondDashWu@users.noreply.github.com> Date: Tue, 17 Oct 2023 07:57:33 -0700 Subject: [PATCH 400/757] Test cases for all_combinations (#10633) * [ADD] Test cases for all_combinations * [DEL] documentation reverted b/c redundant * Update all_combinations.py --------- Co-authored-by: Christian Clauss --- backtracking/all_combinations.py | 47 ++++++++++++++++++++++++++------ 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/backtracking/all_combinations.py b/backtracking/all_combinations.py index bde60f0328ba..ecbcc5882ec1 100644 --- a/backtracking/all_combinations.py +++ b/backtracking/all_combinations.py @@ -1,15 +1,40 @@ """ In this problem, we want to determine all possible combinations of k numbers out of 1 ... n. We use backtracking to solve this problem. - Time complexity: O(C(n,k)) which is O(n choose k) = O((n!/(k! * (n - k)!))) + + Time complexity: O(C(n,k)) which is O(n choose k) = O((n!/(k! * (n - k)!))), """ from __future__ import annotations +from itertools import combinations + + +def combination_lists(n: int, k: int) -> list[list[int]]: + """ + >>> combination_lists(n=4, k=2) + [[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]] + """ + return [list(x) for x in combinations(range(1, n + 1), k)] + def generate_all_combinations(n: int, k: int) -> list[list[int]]: """ >>> generate_all_combinations(n=4, k=2) [[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]] + >>> generate_all_combinations(n=0, k=0) + [[]] + >>> generate_all_combinations(n=10, k=-1) + Traceback (most recent call last): + ... + RecursionError: maximum recursion depth exceeded + >>> generate_all_combinations(n=-1, k=10) + [] + >>> generate_all_combinations(n=5, k=4) + [[1, 2, 3, 4], [1, 2, 3, 5], [1, 2, 4, 5], [1, 3, 4, 5], [2, 3, 4, 5]] + >>> from itertools import combinations + >>> all(generate_all_combinations(n, k) == combination_lists(n, k) + ... for n in range(1, 6) for k in range(1, 6)) + True """ result: list[list[int]] = [] @@ -34,13 +59,17 @@ def create_all_state( current_list.pop() -def print_all_state(total_list: list[list[int]]) -> None: - for i in total_list: - print(*i) +if __name__ == "__main__": + from doctest import testmod + testmod() + print(generate_all_combinations(n=4, k=2)) + tests = ((n, k) for n in range(1, 5) for k in range(1, 5)) + for n, k in tests: + print(n, k, generate_all_combinations(n, k) == combination_lists(n, k)) -if __name__ == "__main__": - n = 4 - k = 2 - total_list = generate_all_combinations(n, k) - print_all_state(total_list) + print("Benchmark:") + from timeit import timeit + + for func in ("combination_lists", "generate_all_combinations"): + print(f"{func:>25}(): {timeit(f'{func}(n=4, k = 2)', globals=globals())}") From 09c2b2d006e3ca217f2ef082d62a0c35560667ef Mon Sep 17 00:00:00 2001 From: Anubhavpandey27 <61093307+Anubhavpandey27@users.noreply.github.com> Date: Tue, 17 Oct 2023 22:37:40 +0530 Subject: [PATCH 401/757] Add arrays/sudoku_solver.py (#10623) * Create Sudoku_Solver Each of the digits 1-9 must occur exactly once in each row. Each of the digits 1-9 must occur exactly once in each column. Each of the digits 1-9 must occur exactly once in each of the 9 3x3 sub-boxes of the grid. The '.' character indicates empty cells. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Rename Sudoku_Solver to sudoku_solver.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update sudoku_solver.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- data_structures/arrays/sudoku_solver.py | 220 ++++++++++++++++++++++++ 1 file changed, 220 insertions(+) create mode 100644 data_structures/arrays/sudoku_solver.py diff --git a/data_structures/arrays/sudoku_solver.py b/data_structures/arrays/sudoku_solver.py new file mode 100644 index 000000000000..8d38bd7295ea --- /dev/null +++ b/data_structures/arrays/sudoku_solver.py @@ -0,0 +1,220 @@ +""" +Please do not modify this file! It is published at https://norvig.com/sudoku.html with +only minimal changes to work with modern versions of Python. If you have improvements, +please make them in a separate file. +""" +import random +import time + + +def cross(items_a, items_b): + "Cross product of elements in A and elements in B." + return [a + b for a in items_a for b in items_b] + + +digits = "123456789" +rows = "ABCDEFGHI" +cols = digits +squares = cross(rows, cols) +unitlist = ( + [cross(rows, c) for c in cols] + + [cross(r, cols) for r in rows] + + [cross(rs, cs) for rs in ("ABC", "DEF", "GHI") for cs in ("123", "456", "789")] +) +units = {s: [u for u in unitlist if s in u] for s in squares} +peers = {s: set(sum(units[s], [])) - {s} for s in squares} + + +def test(): + "A set of unit tests." + assert len(squares) == 81 + assert len(unitlist) == 27 + assert all(len(units[s]) == 3 for s in squares) + assert all(len(peers[s]) == 20 for s in squares) + assert units["C2"] == [ + ["A2", "B2", "C2", "D2", "E2", "F2", "G2", "H2", "I2"], + ["C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8", "C9"], + ["A1", "A2", "A3", "B1", "B2", "B3", "C1", "C2", "C3"], + ] + # fmt: off + assert peers["C2"] == { + "A2", "B2", "D2", "E2", "F2", "G2", "H2", "I2", "C1", "C3", + "C4", "C5", "C6", "C7", "C8", "C9", "A1", "A3", "B1", "B3" + } + # fmt: on + print("All tests pass.") + + +def parse_grid(grid): + """Convert grid to a dict of possible values, {square: digits}, or + return False if a contradiction is detected.""" + ## To start, every square can be any digit; then assign values from the grid. + values = {s: digits for s in squares} + for s, d in grid_values(grid).items(): + if d in digits and not assign(values, s, d): + return False ## (Fail if we can't assign d to square s.) + return values + + +def grid_values(grid): + "Convert grid into a dict of {square: char} with '0' or '.' for empties." + chars = [c for c in grid if c in digits or c in "0."] + assert len(chars) == 81 + return dict(zip(squares, chars)) + + +def assign(values, s, d): + """Eliminate all the other values (except d) from values[s] and propagate. + Return values, except return False if a contradiction is detected.""" + other_values = values[s].replace(d, "") + if all(eliminate(values, s, d2) for d2 in other_values): + return values + else: + return False + + +def eliminate(values, s, d): + """Eliminate d from values[s]; propagate when values or places <= 2. + Return values, except return False if a contradiction is detected.""" + if d not in values[s]: + return values ## Already eliminated + values[s] = values[s].replace(d, "") + ## (1) If a square s is reduced to one value d2, then eliminate d2 from the peers. + if len(values[s]) == 0: + return False ## Contradiction: removed last value + elif len(values[s]) == 1: + d2 = values[s] + if not all(eliminate(values, s2, d2) for s2 in peers[s]): + return False + ## (2) If a unit u is reduced to only one place for a value d, then put it there. + for u in units[s]: + dplaces = [s for s in u if d in values[s]] + if len(dplaces) == 0: + return False ## Contradiction: no place for this value + elif len(dplaces) == 1: + # d can only be in one place in unit; assign it there + if not assign(values, dplaces[0], d): + return False + return values + + +def display(values): + "Display these values as a 2-D grid." + width = 1 + max(len(values[s]) for s in squares) + line = "+".join(["-" * (width * 3)] * 3) + for r in rows: + print( + "".join( + values[r + c].center(width) + ("|" if c in "36" else "") for c in cols + ) + ) + if r in "CF": + print(line) + print() + + +def solve(grid): + return search(parse_grid(grid)) + + +def some(seq): + "Return some element of seq that is true." + for e in seq: + if e: + return e + return False + + +def search(values): + "Using depth-first search and propagation, try all possible values." + if values is False: + return False ## Failed earlier + if all(len(values[s]) == 1 for s in squares): + return values ## Solved! + ## Chose the unfilled square s with the fewest possibilities + n, s = min((len(values[s]), s) for s in squares if len(values[s]) > 1) + return some(search(assign(values.copy(), s, d)) for d in values[s]) + + +def solve_all(grids, name="", showif=0.0): + """Attempt to solve a sequence of grids. Report results. + When showif is a number of seconds, display puzzles that take longer. + When showif is None, don't display any puzzles.""" + + def time_solve(grid): + start = time.monotonic() + values = solve(grid) + t = time.monotonic() - start + ## Display puzzles that take long enough + if showif is not None and t > showif: + display(grid_values(grid)) + if values: + display(values) + print("(%.5f seconds)\n" % t) + return (t, solved(values)) + + times, results = zip(*[time_solve(grid) for grid in grids]) + if (n := len(grids)) > 1: + print( + "Solved %d of %d %s puzzles (avg %.2f secs (%d Hz), max %.2f secs)." + % (sum(results), n, name, sum(times) / n, n / sum(times), max(times)) + ) + + +def solved(values): + "A puzzle is solved if each unit is a permutation of the digits 1 to 9." + + def unitsolved(unit): + return {values[s] for s in unit} == set(digits) + + return values is not False and all(unitsolved(unit) for unit in unitlist) + + +def from_file(filename, sep="\n"): + "Parse a file into a list of strings, separated by sep." + return open(filename).read().strip().split(sep) # noqa: SIM115 + + +def random_puzzle(assignments=17): + """Make a random puzzle with N or more assignments. Restart on contradictions. + Note the resulting puzzle is not guaranteed to be solvable, but empirically + about 99.8% of them are solvable. Some have multiple solutions.""" + values = {s: digits for s in squares} + for s in shuffled(squares): + if not assign(values, s, random.choice(values[s])): + break + ds = [values[s] for s in squares if len(values[s]) == 1] + if len(ds) >= assignments and len(set(ds)) >= 8: + return "".join(values[s] if len(values[s]) == 1 else "." for s in squares) + return random_puzzle(assignments) ## Give up and make a new puzzle + + +def shuffled(seq): + "Return a randomly shuffled copy of the input sequence." + seq = list(seq) + random.shuffle(seq) + return seq + + +grid1 = ( + "003020600900305001001806400008102900700000008006708200002609500800203009005010300" +) +grid2 = ( + "4.....8.5.3..........7......2.....6.....8.4......1.......6.3.7.5..2.....1.4......" +) +hard1 = ( + ".....6....59.....82....8....45........3........6..3.54...325..6.................." +) + +if __name__ == "__main__": + test() + # solve_all(from_file("easy50.txt", '========'), "easy", None) + # solve_all(from_file("top95.txt"), "hard", None) + # solve_all(from_file("hardest.txt"), "hardest", None) + solve_all([random_puzzle() for _ in range(99)], "random", 100.0) + for puzzle in (grid1, grid2): # , hard1): # Takes 22 sec to solve on my M1 Mac. + display(parse_grid(puzzle)) + start = time.monotonic() + solve(puzzle) + t = time.monotonic() - start + print("Solved: %.5f sec" % t) From 9de1c49fe13f009e08dcf5009a798bef43f2230b Mon Sep 17 00:00:00 2001 From: Marek Mazij <112333347+Mrk-Mzj@users.noreply.github.com> Date: Tue, 17 Oct 2023 20:24:16 +0200 Subject: [PATCH 402/757] feat: Polish ID (PESEL) checker added (#10618) * feat: Polish ID (PESEL) checker added * refactor: 'sum' variable renamed to 'subtotal' * style: typos * Apply suggestions from code review * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: Christian Clauss Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- strings/is_polish_national_id.py | 92 ++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 strings/is_polish_national_id.py diff --git a/strings/is_polish_national_id.py b/strings/is_polish_national_id.py new file mode 100644 index 000000000000..8b463a24532a --- /dev/null +++ b/strings/is_polish_national_id.py @@ -0,0 +1,92 @@ +def is_polish_national_id(input_str: str) -> bool: + """ + Verification of the correctness of the PESEL number. + www-gov-pl.translate.goog/web/gov/czym-jest-numer-pesel?_x_tr_sl=auto&_x_tr_tl=en + + PESEL can start with 0, that's why we take str as input, + but convert it to int for some calculations. + + + >>> is_polish_national_id(123) + Traceback (most recent call last): + ... + ValueError: Expected str as input, found + + >>> is_polish_national_id("abc") + Traceback (most recent call last): + ... + ValueError: Expected number as input + + >>> is_polish_national_id("02070803628") # correct PESEL + True + + >>> is_polish_national_id("02150803629") # wrong month + False + + >>> is_polish_national_id("02075503622") # wrong day + False + + >>> is_polish_national_id("-99012212349") # wrong range + False + + >>> is_polish_national_id("990122123499999") # wrong range + False + + >>> is_polish_national_id("02070803621") # wrong checksum + False + """ + + # check for invalid input type + if not isinstance(input_str, str): + msg = f"Expected str as input, found {type(input_str)}" + raise ValueError(msg) + + # check if input can be converted to int + try: + input_int = int(input_str) + except ValueError: + msg = "Expected number as input" + raise ValueError(msg) + + # check number range + if not 10100000 <= input_int <= 99923199999: + return False + + # check month correctness + month = int(input_str[2:4]) + + if ( + month not in range(1, 13) # year 1900-1999 + and month not in range(21, 33) # 2000-2099 + and month not in range(41, 53) # 2100-2199 + and month not in range(61, 73) # 2200-2299 + and month not in range(81, 93) # 1800-1899 + ): + return False + + # check day correctness + day = int(input_str[4:6]) + + if day not in range(1, 32): + return False + + # check the checksum + multipliers = [1, 3, 7, 9, 1, 3, 7, 9, 1, 3] + subtotal = 0 + + digits_to_check = str(input_str)[:-1] # cut off the checksum + + for index, digit in enumerate(digits_to_check): + # Multiply corresponding digits and multipliers. + # In case of a double-digit result, add only the last digit. + subtotal += (int(digit) * multipliers[index]) % 10 + + checksum = 10 - subtotal % 10 + + return checksum == input_int % 10 + + +if __name__ == "__main__": + from doctest import testmod + + testmod() From 9da6f48b46f41c6361416c259dcfec531fb39a01 Mon Sep 17 00:00:00 2001 From: Manmita Das <34617961+manmita@users.noreply.github.com> Date: Wed, 18 Oct 2023 04:07:57 +0530 Subject: [PATCH 403/757] Add binary_coded_decimal.py (#10656) * added decimal to bcd sequence * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * updated with fixes * Update and rename bcd_sequence.py to binary_coded_decimal.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update binary_coded_decimal.py * Update binary_coded_decimal.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- bit_manipulation/binary_coded_decimal.py | 29 ++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 bit_manipulation/binary_coded_decimal.py diff --git a/bit_manipulation/binary_coded_decimal.py b/bit_manipulation/binary_coded_decimal.py new file mode 100644 index 000000000000..676fd6d54fc5 --- /dev/null +++ b/bit_manipulation/binary_coded_decimal.py @@ -0,0 +1,29 @@ +def binary_coded_decimal(number: int) -> str: + """ + Find binary coded decimal (bcd) of integer base 10. + Each digit of the number is represented by a 4-bit binary. + Example: + >>> binary_coded_decimal(-2) + '0b0000' + >>> binary_coded_decimal(-1) + '0b0000' + >>> binary_coded_decimal(0) + '0b0000' + >>> binary_coded_decimal(3) + '0b0011' + >>> binary_coded_decimal(2) + '0b0010' + >>> binary_coded_decimal(12) + '0b00010010' + >>> binary_coded_decimal(987) + '0b100110000111' + """ + return "0b" + "".join( + str(bin(int(digit)))[2:].zfill(4) for digit in str(max(0, number)) + ) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 788e4ede9bf4eb180e4b784268d98d657efbd9da Mon Sep 17 00:00:00 2001 From: Jai Vignesh J <108923524+Jaivignesh-afk@users.noreply.github.com> Date: Wed, 18 Oct 2023 04:20:57 +0530 Subject: [PATCH 404/757] Fix doctest power recursion (#10659) * Added doctests to power_using_recursion.py * Added doctest to power_using_recursion.py * Update power_using_recursion.py * Update power_using_recursion.py --------- Co-authored-by: Christian Clauss --- maths/power_using_recursion.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/maths/power_using_recursion.py b/maths/power_using_recursion.py index f82097f6d8ec..e82635ba0005 100644 --- a/maths/power_using_recursion.py +++ b/maths/power_using_recursion.py @@ -15,18 +15,35 @@ def power(base: int, exponent: int) -> float: """ - power(3, 4) + >>> power(3, 4) 81 >>> power(2, 0) 1 >>> all(power(base, exponent) == pow(base, exponent) ... for base in range(-10, 10) for exponent in range(10)) True + >>> power('a', 1) + 'a' + >>> power('a', 2) + Traceback (most recent call last): + ... + TypeError: can't multiply sequence by non-int of type 'str' + >>> power('a', 'b') + Traceback (most recent call last): + ... + TypeError: unsupported operand type(s) for -: 'str' and 'int' + >>> power(2, -1) + Traceback (most recent call last): + ... + RecursionError: maximum recursion depth exceeded """ return base * power(base, (exponent - 1)) if exponent else 1 if __name__ == "__main__": + from doctests import testmod + + testmod() print("Raise base to the power of exponent using recursion...") base = int(input("Enter the base: ").strip()) exponent = int(input("Enter the exponent: ").strip()) From 361f64c21d7d2528828e20e2eedd59b8d69e5c18 Mon Sep 17 00:00:00 2001 From: Poojan Smart <44301271+PoojanSmart@users.noreply.github.com> Date: Wed, 18 Oct 2023 19:39:13 +0530 Subject: [PATCH 405/757] Adds hinge loss function algorithm (#10628) * Adds exponential moving average algorithm * code clean up * spell correction * Modifies I/O types of function * Replaces generator function * Resolved mypy type error * readibility of code and documentation * Update exponential_moving_average.py * Adds hinge loss function * suggested doc and refactoring changes * refactoring --------- Co-authored-by: Christian Clauss --- machine_learning/loss_functions/hinge_loss.py | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 machine_learning/loss_functions/hinge_loss.py diff --git a/machine_learning/loss_functions/hinge_loss.py b/machine_learning/loss_functions/hinge_loss.py new file mode 100644 index 000000000000..5480a8cd62ee --- /dev/null +++ b/machine_learning/loss_functions/hinge_loss.py @@ -0,0 +1,64 @@ +""" +Hinge Loss + +Description: +Compute the Hinge loss used for training SVM (Support Vector Machine). + +Formula: +loss = max(0, 1 - true * pred) + +Reference: https://en.wikipedia.org/wiki/Hinge_loss + +Author: Poojan Smart +Email: smrtpoojan@gmail.com +""" + +import numpy as np + + +def hinge_loss(y_true: np.ndarray, y_pred: np.ndarray) -> float: + """ + Calculate the mean hinge loss for y_true and y_pred for binary classification. + + Args: + y_true: Array of actual values (ground truth) encoded as -1 and 1. + y_pred: Array of predicted values. + + Returns: + The hinge loss between y_true and y_pred. + + Examples: + >>> y_true = np.array([-1, 1, 1, -1, 1]) + >>> pred = np.array([-4, -0.3, 0.7, 5, 10]) + >>> hinge_loss(y_true, pred) + 1.52 + >>> y_true = np.array([-1, 1, 1, -1, 1, 1]) + >>> pred = np.array([-4, -0.3, 0.7, 5, 10]) + >>> hinge_loss(y_true, pred) + Traceback (most recent call last): + ... + ValueError: Length of predicted and actual array must be same. + >>> y_true = np.array([-1, 1, 10, -1, 1]) + >>> pred = np.array([-4, -0.3, 0.7, 5, 10]) + >>> hinge_loss(y_true, pred) + Traceback (most recent call last): + ... + ValueError: y_true can have values -1 or 1 only. + """ + + if len(y_true) != len(y_pred): + raise ValueError("Length of predicted and actual array must be same.") + + # Raise value error when y_true (encoded labels) have any other values + # than -1 and 1 + if np.any((y_true != -1) & (y_true != 1)): + raise ValueError("y_true can have values -1 or 1 only.") + + hinge_losses = np.maximum(0, 1.0 - (y_true * y_pred)) + return np.mean(hinge_losses) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 572de4f15e266057e806a751006156a212a3812e Mon Sep 17 00:00:00 2001 From: Shivansh Bhatnagar Date: Wed, 18 Oct 2023 20:20:18 +0530 Subject: [PATCH 406/757] Added A General Swish Activation Function inNeural Networks (#10415) * Added A General Swish Activation Function inNeural Networks * Added the general swish function in the SiLU function and renamed it as swish.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: Shivansh Bhatnagar Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .../{sigmoid_linear_unit.py => swish.py} | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) rename neural_network/activation_functions/{sigmoid_linear_unit.py => swish.py} (72%) diff --git a/neural_network/activation_functions/sigmoid_linear_unit.py b/neural_network/activation_functions/swish.py similarity index 72% rename from neural_network/activation_functions/sigmoid_linear_unit.py rename to neural_network/activation_functions/swish.py index 0ee09bf82d38..ab3d8fa1203b 100644 --- a/neural_network/activation_functions/sigmoid_linear_unit.py +++ b/neural_network/activation_functions/swish.py @@ -12,6 +12,7 @@ This script is inspired by a corresponding research paper. * https://arxiv.org/abs/1710.05941 +* https://blog.paperspace.com/swish-activation-function/ """ import numpy as np @@ -49,6 +50,25 @@ def sigmoid_linear_unit(vector: np.ndarray) -> np.ndarray: return vector * sigmoid(vector) +def swish(vector: np.ndarray, trainable_parameter: int) -> np.ndarray: + """ + Parameters: + vector (np.ndarray): A numpy array consisting of real values + trainable_parameter: Use to implement various Swish Activation Functions + + Returns: + swish_vec (np.ndarray): The input numpy array, after applying swish + + Examples: + >>> swish(np.array([-1.0, 1.0, 2.0]), 2) + array([-0.11920292, 0.88079708, 1.96402758]) + + >>> swish(np.array([-2]), 1) + array([-0.23840584]) + """ + return vector * sigmoid(trainable_parameter * vector) + + if __name__ == "__main__": import doctest From 9adb7ced16725e3f6cf24cf93ac81a8dcd351665 Mon Sep 17 00:00:00 2001 From: rtang09 <49603415+rtang09@users.noreply.github.com> Date: Thu, 19 Oct 2023 05:02:04 -0700 Subject: [PATCH 407/757] Update primelib.py (#10209) * Update primelib.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Tianyi Zheng --- maths/primelib.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/maths/primelib.py b/maths/primelib.py index 7e33844be12b..e2d432e1846a 100644 --- a/maths/primelib.py +++ b/maths/primelib.py @@ -51,6 +51,10 @@ def is_prime(number: int) -> bool: True >>> is_prime(10) False + >>> is_prime(97) + True + >>> is_prime(9991) + False >>> is_prime(-1) Traceback (most recent call last): ... From 30c8d5573a8b052210238487167a3ec2d7241d06 Mon Sep 17 00:00:00 2001 From: rtang09 <49603415+rtang09@users.noreply.github.com> Date: Thu, 19 Oct 2023 05:15:23 -0700 Subject: [PATCH 408/757] Update binary_exponentiation.py (#10253) * Update binary_exponentiation.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- maths/binary_exponentiation.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/maths/binary_exponentiation.py b/maths/binary_exponentiation.py index 05de939d1bde..7eeca89262a9 100644 --- a/maths/binary_exponentiation.py +++ b/maths/binary_exponentiation.py @@ -5,6 +5,12 @@ def binary_exponentiation(a: int, n: int) -> int: + """ + >>> binary_exponentiation(3, 5) + 243 + >>> binary_exponentiation(10, 3) + 1000 + """ if n == 0: return 1 @@ -17,6 +23,10 @@ def binary_exponentiation(a: int, n: int) -> int: if __name__ == "__main__": + import doctest + + doctest.testmod() + try: BASE = int(input("Enter Base : ").strip()) POWER = int(input("Enter Power : ").strip()) From b301e589e2c68f583bf3a09f6d4ca224175383b9 Mon Sep 17 00:00:00 2001 From: Iyiola Aloko <48067557+ialoko@users.noreply.github.com> Date: Thu, 19 Oct 2023 08:21:48 -0400 Subject: [PATCH 409/757] Update binary_exponentiation.py (#10342) Co-authored-by: Tianyi Zheng --- maths/binary_exponentiation.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/maths/binary_exponentiation.py b/maths/binary_exponentiation.py index 7eeca89262a9..f613767f547e 100644 --- a/maths/binary_exponentiation.py +++ b/maths/binary_exponentiation.py @@ -6,10 +6,21 @@ def binary_exponentiation(a: int, n: int) -> int: """ + Compute a number raised by some quantity + >>> binary_exponentiation(-1, 3) + -1 + >>> binary_exponentiation(-1, 4) + 1 + >>> binary_exponentiation(2, 2) + 4 >>> binary_exponentiation(3, 5) 243 >>> binary_exponentiation(10, 3) 1000 + >>> binary_exponentiation(5e3, 1) + 5000.0 + >>> binary_exponentiation(-5e3, 1) + -5000.0 """ if n == 0: return 1 @@ -28,7 +39,7 @@ def binary_exponentiation(a: int, n: int) -> int: doctest.testmod() try: - BASE = int(input("Enter Base : ").strip()) + BASE = int(float(input("Enter Base : ").strip())) POWER = int(input("Enter Power : ").strip()) except ValueError: print("Invalid literal for integer") From 33888646af9d74e46da0175df75b3e5892a72fc7 Mon Sep 17 00:00:00 2001 From: anshul-2010 <96651393+anshul-2010@users.noreply.github.com> Date: Thu, 19 Oct 2023 18:08:02 +0530 Subject: [PATCH 410/757] Edit Distance Algorithm for String Matching (#10571) * Edit Distance Algorithm for String Matching * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Apply suggestions from code review * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update edit_distance.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Tianyi Zheng --- strings/edit_distance.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 strings/edit_distance.py diff --git a/strings/edit_distance.py b/strings/edit_distance.py new file mode 100644 index 000000000000..e842c8555c8e --- /dev/null +++ b/strings/edit_distance.py @@ -0,0 +1,32 @@ +def edit_distance(source: str, target: str) -> int: + """ + Edit distance algorithm is a string metric, i.e., it is a way of quantifying how + dissimilar two strings are to one another. It is measured by counting the minimum + number of operations required to transform one string into another. + + This implementation assumes that the cost of operations (insertion, deletion and + substitution) is always 1 + + Args: + source: the initial string with respect to which we are calculating the edit + distance for the target + target: the target string, formed after performing n operations on the source string + + >>> edit_distance("GATTIC", "GALTIC") + 1 + """ + if len(source) == 0: + return len(target) + elif len(target) == 0: + return len(source) + + delta = int(source[-1] != target[-1]) # Substitution + return min( + edit_distance(source[:-1], target[:-1]) + delta, + edit_distance(source, target[:-1]) + 1, + edit_distance(source[:-1], target) + 1, + ) + + +if __name__ == "__main__": + print(edit_distance("ATCGCTG", "TAGCTAA")) # Answer is 4 From 289a4dd6d35a3dd402c98db04d2f39cfc08ea1be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ab=C3=ADlio=20Azevedo?= Date: Thu, 19 Oct 2023 11:35:41 -0300 Subject: [PATCH 411/757] docs: add test scenarios to pull request template (#10396) * docs: add test scenarios to pull request template * Update .github/pull_request_template.md --------- Co-authored-by: Christian Clauss --- .github/pull_request_template.md | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 1f9797fae038..e2ae0966cda5 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -4,6 +4,7 @@ * [ ] Add an algorithm? * [ ] Fix a bug or typo in an existing algorithm? +* [ ] Add or change doctests? -- Note: Please avoid changing both code and tests in a single pull request. * [ ] Documentation change? ### Checklist: From bd3072b84512b33a6fd7d788812340daa8ac3465 Mon Sep 17 00:00:00 2001 From: Ankit Avinash <128812932+Void426@users.noreply.github.com> Date: Thu, 19 Oct 2023 20:16:39 +0530 Subject: [PATCH 412/757] Added Mean Squared Logarithmic Error (MSLE) Loss Function (#10637) * Added Mean Squared Logarithmic Error (MSLE) * Added Mean Squared Logarithmic Error (MSLE) * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .../mean_squared_logarithmic_error.py | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 machine_learning/loss_functions/mean_squared_logarithmic_error.py diff --git a/machine_learning/loss_functions/mean_squared_logarithmic_error.py b/machine_learning/loss_functions/mean_squared_logarithmic_error.py new file mode 100644 index 000000000000..935ebff37a51 --- /dev/null +++ b/machine_learning/loss_functions/mean_squared_logarithmic_error.py @@ -0,0 +1,55 @@ +""" +Mean Squared Logarithmic Error (MSLE) Loss Function + +Description: +MSLE measures the mean squared logarithmic difference between +true values and predicted values, particularly useful when +dealing with regression problems involving skewed or large-value +targets. It is often used when the relative differences between +predicted and true values are more important than absolute +differences. + +Formula: +MSLE = (1/n) * Σ(log(1 + y_true) - log(1 + y_pred))^2 + +Source: +(https://insideaiml.com/blog/MeanSquared-Logarithmic-Error-Loss-1035) +""" + +import numpy as np + + +def mean_squared_logarithmic_error(y_true: np.ndarray, y_pred: np.ndarray) -> float: + """ + Calculate the Mean Squared Logarithmic Error (MSLE) between two arrays. + + Parameters: + - y_true: The true values (ground truth). + - y_pred: The predicted values. + + Returns: + - msle: The Mean Squared Logarithmic Error between y_true and y_pred. + + Example usage: + >>> true_values = np.array([1.0, 2.0, 3.0, 4.0, 5.0]) + >>> predicted_values = np.array([0.8, 2.1, 2.9, 4.2, 5.2]) + >>> mean_squared_logarithmic_error(true_values, predicted_values) + 0.0030860877925181344 + >>> true_labels = np.array([1.0, 2.0, 3.0, 4.0, 5.0]) + >>> predicted_probs = np.array([0.3, 0.8, 0.9, 0.2]) + >>> mean_squared_logarithmic_error(true_labels, predicted_probs) + Traceback (most recent call last): + ... + ValueError: Input arrays must have the same length. + """ + if len(y_true) != len(y_pred): + raise ValueError("Input arrays must have the same length.") + + squared_logarithmic_errors = (np.log1p(y_true) - np.log1p(y_pred)) ** 2 + return np.mean(squared_logarithmic_errors) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 69876140673881efefcb177e3ba2575b0c221438 Mon Sep 17 00:00:00 2001 From: ketan96-m <40893179+ketan96-m@users.noreply.github.com> Date: Thu, 19 Oct 2023 09:48:53 -0500 Subject: [PATCH 413/757] *added docstring and doctest for find_isolated_nodes (#10684) *added docstring and doctest for edglist *added docstring and doctest for adjm Co-authored-by: Ketan --- graphs/basic_graphs.py | 81 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 75 insertions(+), 6 deletions(-) diff --git a/graphs/basic_graphs.py b/graphs/basic_graphs.py index 065b6185c123..25c8045b3d2b 100644 --- a/graphs/basic_graphs.py +++ b/graphs/basic_graphs.py @@ -185,10 +185,29 @@ def topo(g, ind=None, q=None): def adjm(): - n = input().strip() + r""" + Reading an Adjacency matrix + + Parameters: + None + + Returns: + tuple: A tuple containing a list of edges and number of edges + + Example: + >>> # Simulate user input for 3 nodes + >>> input_data = "4\n0 1 0 1\n1 0 1 0\n0 1 0 1\n1 0 1 0\n" + >>> import sys,io + >>> original_input = sys.stdin + >>> sys.stdin = io.StringIO(input_data) # Redirect stdin for testing + >>> adjm() + ([(0, 1, 0, 1), (1, 0, 1, 0), (0, 1, 0, 1), (1, 0, 1, 0)], 4) + >>> sys.stdin = original_input # Restore original stdin + """ + n = int(input().strip()) a = [] for _ in range(n): - a.append(map(int, input().strip().split())) + a.append(tuple(map(int, input().strip().split()))) return a, n @@ -260,10 +279,29 @@ def prim(g, s): def edglist(): - n, m = map(int, input().split(" ")) + r""" + Get the edges and number of edges from the user + + Parameters: + None + + Returns: + tuple: A tuple containing a list of edges and number of edges + + Example: + >>> # Simulate user input for 3 edges and 4 vertices: (1, 2), (2, 3), (3, 4) + >>> input_data = "4 3\n1 2\n2 3\n3 4\n" + >>> import sys,io + >>> original_input = sys.stdin + >>> sys.stdin = io.StringIO(input_data) # Redirect stdin for testing + >>> edglist() + ([(1, 2), (2, 3), (3, 4)], 4) + >>> sys.stdin = original_input # Restore original stdin + """ + n, m = tuple(map(int, input().split(" "))) edges = [] for _ in range(m): - edges.append(map(int, input().split(" "))) + edges.append(tuple(map(int, input().split(" ")))) return edges, n @@ -278,7 +316,9 @@ def edglist(): def krusk(e_and_n): - # Sort edges on the basis of distance + """ + Sort edges on the basis of distance + """ (e, n) = e_and_n e.sort(reverse=True, key=lambda x: x[2]) s = [{i} for i in range(1, n + 1)] @@ -299,8 +339,37 @@ def krusk(e_and_n): break -# find the isolated node in the graph def find_isolated_nodes(graph): + """ + Find the isolated node in the graph + + Parameters: + graph (dict): A dictionary representing a graph. + + Returns: + list: A list of isolated nodes. + + Examples: + >>> graph1 = {1: [2, 3], 2: [1, 3], 3: [1, 2], 4: []} + >>> find_isolated_nodes(graph1) + [4] + + >>> graph2 = {'A': ['B', 'C'], 'B': ['A'], 'C': ['A'], 'D': []} + >>> find_isolated_nodes(graph2) + ['D'] + + >>> graph3 = {'X': [], 'Y': [], 'Z': []} + >>> find_isolated_nodes(graph3) + ['X', 'Y', 'Z'] + + >>> graph4 = {1: [2, 3], 2: [1, 3], 3: [1, 2]} + >>> find_isolated_nodes(graph4) + [] + + >>> graph5 = {} + >>> find_isolated_nodes(graph5) + [] + """ isolated = [] for node in graph: if not graph[node]: From 26ffad9d17232668d0630edb70167e5123a7f35c Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Thu, 19 Oct 2023 19:31:51 +0200 Subject: [PATCH 414/757] Simplify is_bst.py (#10627) * Simplify is_bst.py * updating DIRECTORY.md * Update is_bst.py * Rename is_bst.py to is_sorted.py * updating DIRECTORY.md * Update data_structures/binary_tree/is_sorted.py Co-authored-by: Tianyi Zheng --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: Tianyi Zheng --- DIRECTORY.md | 3 +- data_structures/binary_tree/is_bst.py | 131 ----------------------- data_structures/binary_tree/is_sorted.py | 97 +++++++++++++++++ 3 files changed, 99 insertions(+), 132 deletions(-) delete mode 100644 data_structures/binary_tree/is_bst.py create mode 100644 data_structures/binary_tree/is_sorted.py diff --git a/DIRECTORY.md b/DIRECTORY.md index d878f1c79a2d..0999d2e8687a 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -199,7 +199,8 @@ * [Flatten Binarytree To Linkedlist](data_structures/binary_tree/flatten_binarytree_to_linkedlist.py) * [Floor And Ceiling](data_structures/binary_tree/floor_and_ceiling.py) * [Inorder Tree Traversal 2022](data_structures/binary_tree/inorder_tree_traversal_2022.py) - * [Is Bst](data_structures/binary_tree/is_bst.py) + * [Is Sorted](data_structures/binary_tree/is_sorted.py) + * [Is Sum Tree](data_structures/binary_tree/is_sum_tree.py) * [Lazy Segment Tree](data_structures/binary_tree/lazy_segment_tree.py) * [Lowest Common Ancestor](data_structures/binary_tree/lowest_common_ancestor.py) * [Maximum Fenwick Tree](data_structures/binary_tree/maximum_fenwick_tree.py) diff --git a/data_structures/binary_tree/is_bst.py b/data_structures/binary_tree/is_bst.py deleted file mode 100644 index 0b2ef8c9ffde..000000000000 --- a/data_structures/binary_tree/is_bst.py +++ /dev/null @@ -1,131 +0,0 @@ -""" -Author : Alexander Pantyukhin -Date : November 2, 2022 - -Task: -Given the root of a binary tree, determine if it is a valid binary search -tree (BST). - -A valid binary search tree is defined as follows: - -- The left subtree of a node contains only nodes with keys less than the node's key. -- The right subtree of a node contains only nodes with keys greater than the node's key. -- Both the left and right subtrees must also be binary search trees. - -Implementation notes: -Depth-first search approach. - -leetcode: https://leetcode.com/problems/validate-binary-search-tree/ - -Let n is the number of nodes in tree -Runtime: O(n) -Space: O(1) -""" - -from __future__ import annotations - -from dataclasses import dataclass - - -@dataclass -class TreeNode: - data: float - left: TreeNode | None = None - right: TreeNode | None = None - - -def is_binary_search_tree(root: TreeNode | None) -> bool: - """ - >>> is_binary_search_tree(TreeNode(data=2, - ... left=TreeNode(data=1), - ... right=TreeNode(data=3)) - ... ) - True - - >>> is_binary_search_tree(TreeNode(data=0, - ... left=TreeNode(data=-11), - ... right=TreeNode(data=3)) - ... ) - True - - >>> is_binary_search_tree(TreeNode(data=5, - ... left=TreeNode(data=1), - ... right=TreeNode(data=4, left=TreeNode(data=3))) - ... ) - False - - >>> is_binary_search_tree(TreeNode(data='a', - ... left=TreeNode(data=1), - ... right=TreeNode(data=4, left=TreeNode(data=3))) - ... ) - Traceback (most recent call last): - ... - ValueError: Each node should be type of TreeNode and data should be float. - - >>> is_binary_search_tree(TreeNode(data=2, - ... left=TreeNode([]), - ... right=TreeNode(data=4, left=TreeNode(data=3))) - ... ) - Traceback (most recent call last): - ... - ValueError: Each node should be type of TreeNode and data should be float. - """ - - # Validation - def is_valid_tree(node: TreeNode | None) -> bool: - """ - >>> is_valid_tree(None) - True - >>> is_valid_tree('abc') - False - >>> is_valid_tree(TreeNode(data='not a float')) - False - >>> is_valid_tree(TreeNode(data=1, left=TreeNode('123'))) - False - """ - if node is None: - return True - - if not isinstance(node, TreeNode): - return False - - try: - float(node.data) - except (TypeError, ValueError): - return False - - return is_valid_tree(node.left) and is_valid_tree(node.right) - - if not is_valid_tree(root): - raise ValueError( - "Each node should be type of TreeNode and data should be float." - ) - - def is_binary_search_tree_recursive_check( - node: TreeNode | None, left_bound: float, right_bound: float - ) -> bool: - """ - >>> is_binary_search_tree_recursive_check(None) - True - >>> is_binary_search_tree_recursive_check(TreeNode(data=1), 10, 20) - False - """ - - if node is None: - return True - - return ( - left_bound < node.data < right_bound - and is_binary_search_tree_recursive_check(node.left, left_bound, node.data) - and is_binary_search_tree_recursive_check( - node.right, node.data, right_bound - ) - ) - - return is_binary_search_tree_recursive_check(root, -float("inf"), float("inf")) - - -if __name__ == "__main__": - import doctest - - doctest.testmod() diff --git a/data_structures/binary_tree/is_sorted.py b/data_structures/binary_tree/is_sorted.py new file mode 100644 index 000000000000..5876c5a9c96a --- /dev/null +++ b/data_structures/binary_tree/is_sorted.py @@ -0,0 +1,97 @@ +""" +Given the root of a binary tree, determine if it is a valid binary search tree (BST). + +A valid binary search tree is defined as follows: +- The left subtree of a node contains only nodes with keys less than the node's key. +- The right subtree of a node contains only nodes with keys greater than the node's key. +- Both the left and right subtrees must also be binary search trees. + +In effect, a binary tree is a valid BST if its nodes are sorted in ascending order. +leetcode: https://leetcode.com/problems/validate-binary-search-tree/ + +If n is the number of nodes in the tree then: +Runtime: O(n) +Space: O(1) +""" +from __future__ import annotations + +from collections.abc import Iterator +from dataclasses import dataclass + + +@dataclass +class Node: + data: float + left: Node | None = None + right: Node | None = None + + def __iter__(self) -> Iterator[float]: + """ + >>> root = Node(data=2.1) + >>> list(root) + [2.1] + >>> root.left=Node(data=2.0) + >>> list(root) + [2.0, 2.1] + >>> root.right=Node(data=2.2) + >>> list(root) + [2.0, 2.1, 2.2] + """ + if self.left: + yield from self.left + yield self.data + if self.right: + yield from self.right + + @property + def is_sorted(self) -> bool: + """ + >>> Node(data='abc').is_sorted + True + >>> Node(data=2, + ... left=Node(data=1.999), + ... right=Node(data=3)).is_sorted + True + >>> Node(data=0, + ... left=Node(data=0), + ... right=Node(data=0)).is_sorted + True + >>> Node(data=0, + ... left=Node(data=-11), + ... right=Node(data=3)).is_sorted + True + >>> Node(data=5, + ... left=Node(data=1), + ... right=Node(data=4, left=Node(data=3))).is_sorted + False + >>> Node(data='a', + ... left=Node(data=1), + ... right=Node(data=4, left=Node(data=3))).is_sorted + Traceback (most recent call last): + ... + TypeError: '<' not supported between instances of 'str' and 'int' + >>> Node(data=2, + ... left=Node([]), + ... right=Node(data=4, left=Node(data=3))).is_sorted + Traceback (most recent call last): + ... + TypeError: '<' not supported between instances of 'int' and 'list' + """ + if self.left and (self.data < self.left.data or not self.left.is_sorted): + return False + if self.right and (self.data > self.right.data or not self.right.is_sorted): + return False + return True + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + tree = Node(data=2.1, left=Node(data=2.0), right=Node(data=2.2)) + print(f"Tree {list(tree)} is sorted: {tree.is_sorted = }.") + assert tree.right + tree.right.data = 2.0 + print(f"Tree {list(tree)} is sorted: {tree.is_sorted = }.") + tree.right.data = 2.1 + print(f"Tree {list(tree)} is sorted: {tree.is_sorted = }.") From be94690decde9f0e1df78b41d2a22e7e69bc176d Mon Sep 17 00:00:00 2001 From: NikhithaBandari <91549688+NikhithaBandari@users.noreply.github.com> Date: Thu, 19 Oct 2023 23:19:47 +0530 Subject: [PATCH 415/757] Create swap_all_odd_and_even_bits.py (#10692) * Create swap_all_odd_and_even_bits.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update swap_all_odd_and_even_bits.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * 6: 00000110 --> 9: 00001001 * Update swap_all_odd_and_even_bits.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- .../swap_all_odd_and_even_bits.py | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 bit_manipulation/swap_all_odd_and_even_bits.py diff --git a/bit_manipulation/swap_all_odd_and_even_bits.py b/bit_manipulation/swap_all_odd_and_even_bits.py new file mode 100644 index 000000000000..5ec84417bea6 --- /dev/null +++ b/bit_manipulation/swap_all_odd_and_even_bits.py @@ -0,0 +1,58 @@ +def show_bits(before: int, after: int) -> str: + """ + >>> print(show_bits(0, 0xFFFF)) + 0: 00000000 + 65535: 1111111111111111 + """ + return f"{before:>5}: {before:08b}\n{after:>5}: {after:08b}" + + +def swap_odd_even_bits(num: int) -> int: + """ + 1. We use bitwise AND operations to separate the even bits (0, 2, 4, 6, etc.) and + odd bits (1, 3, 5, 7, etc.) in the input number. + 2. We then right-shift the even bits by 1 position and left-shift the odd bits by + 1 position to swap them. + 3. Finally, we combine the swapped even and odd bits using a bitwise OR operation + to obtain the final result. + >>> print(show_bits(0, swap_odd_even_bits(0))) + 0: 00000000 + 0: 00000000 + >>> print(show_bits(1, swap_odd_even_bits(1))) + 1: 00000001 + 2: 00000010 + >>> print(show_bits(2, swap_odd_even_bits(2))) + 2: 00000010 + 1: 00000001 + >>> print(show_bits(3, swap_odd_even_bits(3))) + 3: 00000011 + 3: 00000011 + >>> print(show_bits(4, swap_odd_even_bits(4))) + 4: 00000100 + 8: 00001000 + >>> print(show_bits(5, swap_odd_even_bits(5))) + 5: 00000101 + 10: 00001010 + >>> print(show_bits(6, swap_odd_even_bits(6))) + 6: 00000110 + 9: 00001001 + >>> print(show_bits(23, swap_odd_even_bits(23))) + 23: 00010111 + 43: 00101011 + """ + # Get all even bits - 0xAAAAAAAA is a 32-bit number with all even bits set to 1 + even_bits = num & 0xAAAAAAAA + + # Get all odd bits - 0x55555555 is a 32-bit number with all odd bits set to 1 + odd_bits = num & 0x55555555 + + # Right shift even bits and left shift odd bits and swap them + return even_bits >> 1 | odd_bits << 1 + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + for i in (-1, 0, 1, 2, 3, 4, 23, 24): + print(show_bits(i, swap_odd_even_bits(i)), "\n") From 34f48b684bce39cb24667e5181b268c9f3bf9980 Mon Sep 17 00:00:00 2001 From: Anupamaraie <91787285+Anupamaraie@users.noreply.github.com> Date: Fri, 20 Oct 2023 01:50:16 +0545 Subject: [PATCH 416/757] Create vernam_cipher.py (#10702) * Create vernam_cipher.py added vernam cipher * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update vernam_cipher.py added return type * Update vernam_cipher.py added type hint for plaintext and key * Update vernam_cipher.py added tests * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update vernam_cipher.py Added tests * Update vernam_cipher.py * Update vernam_cipher.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update vernam_cipher.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update vernam_cipher.py * Update vernam_cipher.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update vernam_cipher.py * Update vernam_cipher.py * Update vernam_cipher.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update vernam_cipher.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update vernam_cipher.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update vernam_cipher.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update vernam_cipher.py * Update vernam_cipher.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update vernam_cipher.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update vernam_cipher.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update vernam_cipher.py * Update vernam_cipher.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update vernam_cipher.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update vernam_cipher.py * Update vernam_cipher.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update vernam_cipher.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update vernam_cipher.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update vernam_cipher.py * Update ciphers/vernam_cipher.py * Update vernam_cipher.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- ciphers/vernam_cipher.py | 42 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 ciphers/vernam_cipher.py diff --git a/ciphers/vernam_cipher.py b/ciphers/vernam_cipher.py new file mode 100644 index 000000000000..197f28635a1c --- /dev/null +++ b/ciphers/vernam_cipher.py @@ -0,0 +1,42 @@ +def vernam_encrypt(plaintext: str, key: str) -> str: + """ + >>> vernam_encrypt("HELLO","KEY") + 'RIJVS' + """ + ciphertext = "" + for i in range(len(plaintext)): + ct = ord(key[i % len(key)]) - 65 + ord(plaintext[i]) - 65 + while ct > 25: + ct = ct - 26 + ciphertext += chr(65 + ct) + return ciphertext + + +def vernam_decrypt(ciphertext: str, key: str) -> str: + """ + >>> vernam_decrypt("RIJVS","KEY") + 'HELLO' + """ + decrypted_text = "" + for i in range(len(ciphertext)): + ct = ord(ciphertext[i]) - ord(key[i % len(key)]) + while ct < 0: + ct = 26 + ct + decrypted_text += chr(65 + ct) + return decrypted_text + + +if __name__ == "__main__": + from doctest import testmod + + testmod() + + # Example usage + plaintext = "HELLO" + key = "KEY" + encrypted_text = vernam_encrypt(plaintext, key) + decrypted_text = vernam_decrypt(encrypted_text, key) + print("\n\n") + print("Plaintext:", plaintext) + print("Encrypted:", encrypted_text) + print("Decrypted:", decrypted_text) From 9875f374f4762d6219067b2e7909a762f25b68ba Mon Sep 17 00:00:00 2001 From: Adam Ross <14985050+R055A@users.noreply.github.com> Date: Thu, 19 Oct 2023 22:45:51 +0200 Subject: [PATCH 417/757] Consolidate bubble sort iterative and recursive (#10651) * Consolidate bubble sort iterative and recursive * Update sorts/bubble_sort.py Co-authored-by: Christian Clauss * Refactor bubble sort func signature, doctest, timer * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update bubble_sort.py --------- Co-authored-by: Christian Clauss Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- sorts/bubble_sort.py | 109 ++++++++++++++++++++++++++++----- sorts/recursive_bubble_sort.py | 42 ------------- 2 files changed, 92 insertions(+), 59 deletions(-) delete mode 100644 sorts/recursive_bubble_sort.py diff --git a/sorts/bubble_sort.py b/sorts/bubble_sort.py index 7da4362a5b97..bdf85c70dd35 100644 --- a/sorts/bubble_sort.py +++ b/sorts/bubble_sort.py @@ -1,7 +1,7 @@ from typing import Any -def bubble_sort(collection: list[Any]) -> list[Any]: +def bubble_sort_iterative(collection: list[Any]) -> list[Any]: """Pure implementation of bubble sort algorithm in Python :param collection: some mutable ordered collection with heterogeneous @@ -9,25 +9,37 @@ def bubble_sort(collection: list[Any]) -> list[Any]: :return: the same collection ordered by ascending Examples: - >>> bubble_sort([0, 5, 2, 3, 2]) + >>> bubble_sort_iterative([0, 5, 2, 3, 2]) [0, 2, 2, 3, 5] - >>> bubble_sort([0, 5, 2, 3, 2]) == sorted([0, 5, 2, 3, 2]) + >>> bubble_sort_iterative([]) + [] + >>> bubble_sort_iterative([-2, -45, -5]) + [-45, -5, -2] + >>> bubble_sort_iterative([-23, 0, 6, -4, 34]) + [-23, -4, 0, 6, 34] + >>> bubble_sort_iterative([0, 5, 2, 3, 2]) == sorted([0, 5, 2, 3, 2]) True - >>> bubble_sort([]) == sorted([]) + >>> bubble_sort_iterative([]) == sorted([]) True - >>> bubble_sort([-2, -45, -5]) == sorted([-2, -45, -5]) + >>> bubble_sort_iterative([-2, -45, -5]) == sorted([-2, -45, -5]) True - >>> bubble_sort([-23, 0, 6, -4, 34]) == sorted([-23, 0, 6, -4, 34]) + >>> bubble_sort_iterative([-23, 0, 6, -4, 34]) == sorted([-23, 0, 6, -4, 34]) True - >>> bubble_sort(['d', 'a', 'b', 'e', 'c']) == sorted(['d', 'a', 'b', 'e', 'c']) + >>> bubble_sort_iterative(['d', 'a', 'b', 'e']) == sorted(['d', 'a', 'b', 'e']) True + >>> bubble_sort_iterative(['z', 'a', 'y', 'b', 'x', 'c']) + ['a', 'b', 'c', 'x', 'y', 'z'] + >>> bubble_sort_iterative([1.1, 3.3, 5.5, 7.7, 2.2, 4.4, 6.6]) + [1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7] + >>> bubble_sort_iterative([1, 3.3, 5, 7.7, 2, 4.4, 6]) + [1, 2, 3.3, 4.4, 5, 6, 7.7] >>> import random - >>> collection = random.sample(range(-50, 50), 100) - >>> bubble_sort(collection) == sorted(collection) + >>> collection_arg = random.sample(range(-50, 50), 100) + >>> bubble_sort_iterative(collection_arg) == sorted(collection_arg) True >>> import string - >>> collection = random.choices(string.ascii_letters + string.digits, k=100) - >>> bubble_sort(collection) == sorted(collection) + >>> collection_arg = random.choices(string.ascii_letters + string.digits, k=100) + >>> bubble_sort_iterative(collection_arg) == sorted(collection_arg) True """ length = len(collection) @@ -42,14 +54,77 @@ def bubble_sort(collection: list[Any]) -> list[Any]: return collection +def bubble_sort_recursive(collection: list[Any]) -> list[Any]: + """It is similar iterative bubble sort but recursive. + + :param collection: mutable ordered sequence of elements + :return: the same list in ascending order + + Examples: + >>> bubble_sort_recursive([0, 5, 2, 3, 2]) + [0, 2, 2, 3, 5] + >>> bubble_sort_iterative([]) + [] + >>> bubble_sort_recursive([-2, -45, -5]) + [-45, -5, -2] + >>> bubble_sort_recursive([-23, 0, 6, -4, 34]) + [-23, -4, 0, 6, 34] + >>> bubble_sort_recursive([0, 5, 2, 3, 2]) == sorted([0, 5, 2, 3, 2]) + True + >>> bubble_sort_recursive([]) == sorted([]) + True + >>> bubble_sort_recursive([-2, -45, -5]) == sorted([-2, -45, -5]) + True + >>> bubble_sort_recursive([-23, 0, 6, -4, 34]) == sorted([-23, 0, 6, -4, 34]) + True + >>> bubble_sort_recursive(['d', 'a', 'b', 'e']) == sorted(['d', 'a', 'b', 'e']) + True + >>> bubble_sort_recursive(['z', 'a', 'y', 'b', 'x', 'c']) + ['a', 'b', 'c', 'x', 'y', 'z'] + >>> bubble_sort_recursive([1.1, 3.3, 5.5, 7.7, 2.2, 4.4, 6.6]) + [1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7] + >>> bubble_sort_recursive([1, 3.3, 5, 7.7, 2, 4.4, 6]) + [1, 2, 3.3, 4.4, 5, 6, 7.7] + >>> import random + >>> collection_arg = random.sample(range(-50, 50), 100) + >>> bubble_sort_recursive(collection_arg) == sorted(collection_arg) + True + >>> import string + >>> collection_arg = random.choices(string.ascii_letters + string.digits, k=100) + >>> bubble_sort_recursive(collection_arg) == sorted(collection_arg) + True + """ + length = len(collection) + swapped = False + for i in range(length - 1): + if collection[i] > collection[i + 1]: + collection[i], collection[i + 1] = collection[i + 1], collection[i] + swapped = True + + return collection if not swapped else bubble_sort_recursive(collection) + + if __name__ == "__main__": import doctest - import time + from random import sample + from timeit import timeit doctest.testmod() - user_input = input("Enter numbers separated by a comma:").strip() - unsorted = [int(item) for item in user_input.split(",")] - start = time.process_time() - print(*bubble_sort(unsorted), sep=",") - print(f"Processing time: {(time.process_time() - start)%1e9 + 7}") + # Benchmark: Iterative seems slightly faster than recursive. + num_runs = 10_000 + unsorted = sample(range(-50, 50), 100) + timer_iterative = timeit( + "bubble_sort_iterative(unsorted[:])", globals=globals(), number=num_runs + ) + print("\nIterative bubble sort:") + print(*bubble_sort_iterative(unsorted), sep=",") + print(f"Processing time (iterative): {timer_iterative:.5f}s for {num_runs:,} runs") + + unsorted = sample(range(-50, 50), 100) + timer_recursive = timeit( + "bubble_sort_recursive(unsorted[:])", globals=globals(), number=num_runs + ) + print("\nRecursive bubble sort:") + print(*bubble_sort_recursive(unsorted), sep=",") + print(f"Processing time (recursive): {timer_recursive:.5f}s for {num_runs:,} runs") diff --git a/sorts/recursive_bubble_sort.py b/sorts/recursive_bubble_sort.py deleted file mode 100644 index 82af89593e5b..000000000000 --- a/sorts/recursive_bubble_sort.py +++ /dev/null @@ -1,42 +0,0 @@ -def bubble_sort(list_data: list, length: int = 0) -> list: - """ - It is similar is bubble sort but recursive. - :param list_data: mutable ordered sequence of elements - :param length: length of list data - :return: the same list in ascending order - - >>> bubble_sort([0, 5, 2, 3, 2], 5) - [0, 2, 2, 3, 5] - - >>> bubble_sort([], 0) - [] - - >>> bubble_sort([-2, -45, -5], 3) - [-45, -5, -2] - - >>> bubble_sort([-23, 0, 6, -4, 34], 5) - [-23, -4, 0, 6, 34] - - >>> bubble_sort([-23, 0, 6, -4, 34], 5) == sorted([-23, 0, 6, -4, 34]) - True - - >>> bubble_sort(['z','a','y','b','x','c'], 6) - ['a', 'b', 'c', 'x', 'y', 'z'] - - >>> bubble_sort([1.1, 3.3, 5.5, 7.7, 2.2, 4.4, 6.6]) - [1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7] - """ - length = length or len(list_data) - swapped = False - for i in range(length - 1): - if list_data[i] > list_data[i + 1]: - list_data[i], list_data[i + 1] = list_data[i + 1], list_data[i] - swapped = True - - return list_data if not swapped else bubble_sort(list_data, length - 1) - - -if __name__ == "__main__": - import doctest - - doctest.testmod() From 51805338afbbf76c3d1371b60ba301eaaf094359 Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Thu, 19 Oct 2023 23:35:38 -0400 Subject: [PATCH 418/757] Fix ruff error in `machine_learning/sequential_minimum_optimization.py` (#10717) * updating DIRECTORY.md * Try to fix ruff error in sequential_minimum_optimization.py * Update sequential_minimum_optimization.py * Update sequential_minimum_optimization.py * Update sequential_minimum_optimization.py --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 13 +++++++++++-- machine_learning/sequential_minimum_optimization.py | 2 +- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index 0999d2e8687a..1aaabf782fe3 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -37,6 +37,7 @@ ## Bit Manipulation * [Binary And Operator](bit_manipulation/binary_and_operator.py) + * [Binary Coded Decimal](bit_manipulation/binary_coded_decimal.py) * [Binary Count Setbits](bit_manipulation/binary_count_setbits.py) * [Binary Count Trailing Zeros](bit_manipulation/binary_count_trailing_zeros.py) * [Binary Or Operator](bit_manipulation/binary_or_operator.py) @@ -57,6 +58,7 @@ * [Power Of 4](bit_manipulation/power_of_4.py) * [Reverse Bits](bit_manipulation/reverse_bits.py) * [Single Bit Manipulation Operations](bit_manipulation/single_bit_manipulation_operations.py) + * [Swap All Odd And Even Bits](bit_manipulation/swap_all_odd_and_even_bits.py) ## Blockchain * [Diophantine Equation](blockchain/diophantine_equation.py) @@ -124,6 +126,7 @@ * [Transposition Cipher](ciphers/transposition_cipher.py) * [Transposition Cipher Encrypt Decrypt File](ciphers/transposition_cipher_encrypt_decrypt_file.py) * [Trifid Cipher](ciphers/trifid_cipher.py) + * [Vernam Cipher](ciphers/vernam_cipher.py) * [Vigenere Cipher](ciphers/vigenere_cipher.py) * [Xor Cipher](ciphers/xor_cipher.py) @@ -163,6 +166,7 @@ * [Molecular Chemistry](conversions/molecular_chemistry.py) * [Octal To Binary](conversions/octal_to_binary.py) * [Octal To Decimal](conversions/octal_to_decimal.py) + * [Octal To Hexadecimal](conversions/octal_to_hexadecimal.py) * [Prefix Conversions](conversions/prefix_conversions.py) * [Prefix Conversions String](conversions/prefix_conversions_string.py) * [Pressure Conversions](conversions/pressure_conversions.py) @@ -183,6 +187,7 @@ * [Prefix Sum](data_structures/arrays/prefix_sum.py) * [Product Sum](data_structures/arrays/product_sum.py) * [Sparse Table](data_structures/arrays/sparse_table.py) + * [Sudoku Solver](data_structures/arrays/sudoku_solver.py) * Binary Tree * [Avl Tree](data_structures/binary_tree/avl_tree.py) * [Basic Binary Tree](data_structures/binary_tree/basic_binary_tree.py) @@ -548,8 +553,10 @@ * Loss Functions * [Binary Cross Entropy](machine_learning/loss_functions/binary_cross_entropy.py) * [Categorical Cross Entropy](machine_learning/loss_functions/categorical_cross_entropy.py) + * [Hinge Loss](machine_learning/loss_functions/hinge_loss.py) * [Huber Loss](machine_learning/loss_functions/huber_loss.py) * [Mean Squared Error](machine_learning/loss_functions/mean_squared_error.py) + * [Mean Squared Logarithmic Error](machine_learning/loss_functions/mean_squared_logarithmic_error.py) * [Mfcc](machine_learning/mfcc.py) * [Multilayer Perceptron Classifier](machine_learning/multilayer_perceptron_classifier.py) * [Polynomial Regression](machine_learning/polynomial_regression.py) @@ -734,6 +741,7 @@ * [Inverse Of Matrix](matrix/inverse_of_matrix.py) * [Largest Square Area In Matrix](matrix/largest_square_area_in_matrix.py) * [Matrix Class](matrix/matrix_class.py) + * [Matrix Multiplication Recursion](matrix/matrix_multiplication_recursion.py) * [Matrix Operation](matrix/matrix_operation.py) * [Max Area Of Island](matrix/max_area_of_island.py) * [Median Matrix](matrix/median_matrix.py) @@ -760,10 +768,10 @@ * [Mish](neural_network/activation_functions/mish.py) * [Rectified Linear Unit](neural_network/activation_functions/rectified_linear_unit.py) * [Scaled Exponential Linear Unit](neural_network/activation_functions/scaled_exponential_linear_unit.py) - * [Sigmoid Linear Unit](neural_network/activation_functions/sigmoid_linear_unit.py) * [Soboleva Modified Hyperbolic Tangent](neural_network/activation_functions/soboleva_modified_hyperbolic_tangent.py) * [Softplus](neural_network/activation_functions/softplus.py) * [Squareplus](neural_network/activation_functions/squareplus.py) + * [Swish](neural_network/activation_functions/swish.py) * [Back Propagation Neural Network](neural_network/back_propagation_neural_network.py) * [Convolution Neural Network](neural_network/convolution_neural_network.py) * [Perceptron](neural_network/perceptron.py) @@ -1185,7 +1193,6 @@ * [Quick Sort](sorts/quick_sort.py) * [Quick Sort 3 Partition](sorts/quick_sort_3_partition.py) * [Radix Sort](sorts/radix_sort.py) - * [Recursive Bubble Sort](sorts/recursive_bubble_sort.py) * [Recursive Insertion Sort](sorts/recursive_insertion_sort.py) * [Recursive Mergesort Array](sorts/recursive_mergesort_array.py) * [Recursive Quick Sort](sorts/recursive_quick_sort.py) @@ -1216,12 +1223,14 @@ * [Damerau Levenshtein Distance](strings/damerau_levenshtein_distance.py) * [Detecting English Programmatically](strings/detecting_english_programmatically.py) * [Dna](strings/dna.py) + * [Edit Distance](strings/edit_distance.py) * [Frequency Finder](strings/frequency_finder.py) * [Hamming Distance](strings/hamming_distance.py) * [Indian Phone Validator](strings/indian_phone_validator.py) * [Is Contains Unique Chars](strings/is_contains_unique_chars.py) * [Is Isogram](strings/is_isogram.py) * [Is Pangram](strings/is_pangram.py) + * [Is Polish National Id](strings/is_polish_national_id.py) * [Is Spain National Id](strings/is_spain_national_id.py) * [Is Srilankan Phone Number](strings/is_srilankan_phone_number.py) * [Is Valid Email Address](strings/is_valid_email_address.py) diff --git a/machine_learning/sequential_minimum_optimization.py b/machine_learning/sequential_minimum_optimization.py index b24f5669e2e8..9e2304859f8d 100644 --- a/machine_learning/sequential_minimum_optimization.py +++ b/machine_learning/sequential_minimum_optimization.py @@ -454,7 +454,7 @@ def test_cancel_data(): print("Hello!\nStart test svm by smo algorithm!") # 0: download dataset and load into pandas' dataframe if not os.path.exists(r"cancel_data.csv"): - request = urllib.request.Request( + request = urllib.request.Request( # noqa: S310 CANCER_DATASET_URL, headers={"User-Agent": "Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)"}, ) From 4154428351cd60db504eb232e3b7900987a2fa19 Mon Sep 17 00:00:00 2001 From: Saptadeep Banerjee <69459134+imSanko@users.noreply.github.com> Date: Fri, 20 Oct 2023 09:59:24 +0530 Subject: [PATCH 419/757] [ADD]: Improved tests in power recursion! (#10664) * Added new tests! * [ADD]: Inproved Tests * fixed * Removed spaces * Changed the file name * Added Changes * changed the code and kept the test cases * changed the code and kept the test cases * missed the line * removed spaces * Update power_using_recursion.py --------- Co-authored-by: Christian Clauss --- maths/power_using_recursion.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/maths/power_using_recursion.py b/maths/power_using_recursion.py index e82635ba0005..462fc45bff64 100644 --- a/maths/power_using_recursion.py +++ b/maths/power_using_recursion.py @@ -15,6 +15,8 @@ def power(base: int, exponent: int) -> float: """ + Calculate the power of a base raised to an exponent. + >>> power(3, 4) 81 >>> power(2, 0) From 82fc24ce96036b6e1180de06c513bbaacda6a550 Mon Sep 17 00:00:00 2001 From: RaymondDashWu <33266041+RaymondDashWu@users.noreply.github.com> Date: Thu, 19 Oct 2023 21:42:20 -0700 Subject: [PATCH 420/757] Test cases for check_bipartite_graph_bfs (#10688) * [ADD] tests for check_bipartite_graph_bfs * linter fix? * linter fix * [ADD] more test cases check_bipartite_graph_bfs * doctest fixes. Forgot to add 'Traceback...' * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * missed a Traceback * Update check_bipartite_graph_bfs.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- graphs/check_bipartite_graph_bfs.py | 45 +++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/graphs/check_bipartite_graph_bfs.py b/graphs/check_bipartite_graph_bfs.py index 7fc57cbc78bd..6c385d54e0b6 100644 --- a/graphs/check_bipartite_graph_bfs.py +++ b/graphs/check_bipartite_graph_bfs.py @@ -10,6 +10,48 @@ def check_bipartite(graph): + """ + >>> check_bipartite({}) + True + >>> check_bipartite({0: [1, 3], 1: [0, 2], 2: [1, 3], 3: [0, 2]}) + True + >>> check_bipartite({0: [1, 2, 3], 1: [0, 2], 2: [0, 1, 3], 3: [0, 2]}) + False + >>> check_bipartite({0: [4], 1: [], 2: [4], 3: [4], 4: [0, 2, 3]}) + True + >>> check_bipartite({0: [1, 3], 1: [0, 2], 2: [1, 3], 3: [0, 2], 4: [0]}) + False + >>> check_bipartite({7: [1, 3], 1: [0, 2], 2: [1, 3], 3: [0, 2], 4: [0]}) + Traceback (most recent call last): + ... + KeyError: 0 + >>> check_bipartite({0: [1, 3], 1: [0, 2], 2: [1, 3], 3: [0, 2], 9: [0]}) + Traceback (most recent call last): + ... + KeyError: 4 + >>> check_bipartite({0: [-1, 3], 1: [0, -2]}) + Traceback (most recent call last): + ... + IndexError: list index out of range + >>> check_bipartite({-1: [0, 2], 0: [-1, 1], 1: [0, 2], 2: [-1, 1]}) + True + >>> check_bipartite({0.9: [1, 3], 1: [0, 2], 2: [1, 3], 3: [0, 2]}) + Traceback (most recent call last): + ... + KeyError: 0 + >>> check_bipartite({0: [1.0, 3.0], 1.0: [0, 2.0], 2.0: [1.0, 3.0], 3.0: [0, 2.0]}) + Traceback (most recent call last): + ... + TypeError: list indices must be integers or slices, not float + >>> check_bipartite({"a": [1, 3], "b": [0, 2], "c": [1, 3], "d": [0, 2]}) + Traceback (most recent call last): + ... + KeyError: 0 + >>> check_bipartite({0: ["b", "d"], 1: ["a", "c"], 2: ["b", "d"], 3: ["a", "c"]}) + Traceback (most recent call last): + ... + TypeError: list indices must be integers or slices, not str + """ queue = Queue() visited = [False] * len(graph) color = [-1] * len(graph) @@ -45,3 +87,6 @@ def bfs(): if __name__ == "__main__": # Adjacency List of graph print(check_bipartite({0: [1, 3], 1: [0, 2], 2: [1, 3], 3: [0, 2]})) + import doctest + + doctest.testmod() From 197604898b85e84cfbaee0a0dd06095db8d1c7b6 Mon Sep 17 00:00:00 2001 From: shivaparihar6119 <122152343+shivaparihar6119@users.noreply.github.com> Date: Fri, 20 Oct 2023 11:39:58 +0530 Subject: [PATCH 421/757] Concatenates both check bipatrite graphs(bfs&dfs) (#10708) * sync * fixes#8098 * deleted: graphs/check_bipartite_graph_all.py new file: graphs/check_bipatrite,py * renamed: graphs/check_bipatrite,py -> graphs/check_bipatrite.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Add the new tests --------- Co-authored-by: Christian Clauss Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- DIRECTORY.md | 10 -- graphs/check_bipartite_graph_bfs.py | 92 -------------- graphs/check_bipartite_graph_dfs.py | 55 --------- graphs/check_bipatrite.py | 179 ++++++++++++++++++++++++++++ 4 files changed, 179 insertions(+), 157 deletions(-) delete mode 100644 graphs/check_bipartite_graph_bfs.py delete mode 100644 graphs/check_bipartite_graph_dfs.py create mode 100644 graphs/check_bipatrite.py diff --git a/DIRECTORY.md b/DIRECTORY.md index 1aaabf782fe3..1320c70ef629 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -65,9 +65,7 @@ ## Boolean Algebra * [And Gate](boolean_algebra/and_gate.py) - * [Imply Gate](boolean_algebra/imply_gate.py) * [Nand Gate](boolean_algebra/nand_gate.py) - * [Nimply Gate](boolean_algebra/nimply_gate.py) * [Nor Gate](boolean_algebra/nor_gate.py) * [Not Gate](boolean_algebra/not_gate.py) * [Or Gate](boolean_algebra/or_gate.py) @@ -180,9 +178,7 @@ ## Data Structures * Arrays * [Equilibrium Index In Array](data_structures/arrays/equilibrium_index_in_array.py) - * [Find Triplets With 0 Sum](data_structures/arrays/find_triplets_with_0_sum.py) * [Median Two Array](data_structures/arrays/median_two_array.py) - * [Pairs With Given Sum](data_structures/arrays/pairs_with_given_sum.py) * [Permutations](data_structures/arrays/permutations.py) * [Prefix Sum](data_structures/arrays/prefix_sum.py) * [Product Sum](data_structures/arrays/product_sum.py) @@ -402,7 +398,6 @@ ## Financial * [Equated Monthly Installments](financial/equated_monthly_installments.py) - * [Exponential Moving Average](financial/exponential_moving_average.py) * [Interest](financial/interest.py) * [Present Value](financial/present_value.py) * [Price Plus Tax](financial/price_plus_tax.py) @@ -711,7 +706,6 @@ * [Sin](maths/sin.py) * [Sock Merchant](maths/sock_merchant.py) * [Softmax](maths/softmax.py) - * [Solovay Strassen Primality Test](maths/solovay_strassen_primality_test.py) * [Square Root](maths/square_root.py) * [Sum Of Arithmetic Series](maths/sum_of_arithmetic_series.py) * [Sum Of Digits](maths/sum_of_digits.py) @@ -753,7 +747,6 @@ * [Spiral Print](matrix/spiral_print.py) * Tests * [Test Matrix Operation](matrix/tests/test_matrix_operation.py) - * [Validate Sudoku Board](matrix/validate_sudoku_board.py) ## Networking Flow * [Ford Fulkerson](networking_flow/ford_fulkerson.py) @@ -829,7 +822,6 @@ * [Rms Speed Of Molecule](physics/rms_speed_of_molecule.py) * [Shear Stress](physics/shear_stress.py) * [Speed Of Sound](physics/speed_of_sound.py) - * [Speeds Of Gas Molecules](physics/speeds_of_gas_molecules.py) ## Project Euler * Problem 001 @@ -1220,7 +1212,6 @@ * [Capitalize](strings/capitalize.py) * [Check Anagrams](strings/check_anagrams.py) * [Credit Card Validator](strings/credit_card_validator.py) - * [Damerau Levenshtein Distance](strings/damerau_levenshtein_distance.py) * [Detecting English Programmatically](strings/detecting_english_programmatically.py) * [Dna](strings/dna.py) * [Edit Distance](strings/edit_distance.py) @@ -1255,7 +1246,6 @@ * [String Switch Case](strings/string_switch_case.py) * [Strip](strings/strip.py) * [Text Justification](strings/text_justification.py) - * [Title](strings/title.py) * [Top K Frequent Words](strings/top_k_frequent_words.py) * [Upper](strings/upper.py) * [Wave](strings/wave.py) diff --git a/graphs/check_bipartite_graph_bfs.py b/graphs/check_bipartite_graph_bfs.py deleted file mode 100644 index 6c385d54e0b6..000000000000 --- a/graphs/check_bipartite_graph_bfs.py +++ /dev/null @@ -1,92 +0,0 @@ -# Check whether Graph is Bipartite or Not using BFS - - -# A Bipartite Graph is a graph whose vertices can be divided into two independent sets, -# U and V such that every edge (u, v) either connects a vertex from U to V or a vertex -# from V to U. In other words, for every edge (u, v), either u belongs to U and v to V, -# or u belongs to V and v to U. We can also say that there is no edge that connects -# vertices of same set. -from queue import Queue - - -def check_bipartite(graph): - """ - >>> check_bipartite({}) - True - >>> check_bipartite({0: [1, 3], 1: [0, 2], 2: [1, 3], 3: [0, 2]}) - True - >>> check_bipartite({0: [1, 2, 3], 1: [0, 2], 2: [0, 1, 3], 3: [0, 2]}) - False - >>> check_bipartite({0: [4], 1: [], 2: [4], 3: [4], 4: [0, 2, 3]}) - True - >>> check_bipartite({0: [1, 3], 1: [0, 2], 2: [1, 3], 3: [0, 2], 4: [0]}) - False - >>> check_bipartite({7: [1, 3], 1: [0, 2], 2: [1, 3], 3: [0, 2], 4: [0]}) - Traceback (most recent call last): - ... - KeyError: 0 - >>> check_bipartite({0: [1, 3], 1: [0, 2], 2: [1, 3], 3: [0, 2], 9: [0]}) - Traceback (most recent call last): - ... - KeyError: 4 - >>> check_bipartite({0: [-1, 3], 1: [0, -2]}) - Traceback (most recent call last): - ... - IndexError: list index out of range - >>> check_bipartite({-1: [0, 2], 0: [-1, 1], 1: [0, 2], 2: [-1, 1]}) - True - >>> check_bipartite({0.9: [1, 3], 1: [0, 2], 2: [1, 3], 3: [0, 2]}) - Traceback (most recent call last): - ... - KeyError: 0 - >>> check_bipartite({0: [1.0, 3.0], 1.0: [0, 2.0], 2.0: [1.0, 3.0], 3.0: [0, 2.0]}) - Traceback (most recent call last): - ... - TypeError: list indices must be integers or slices, not float - >>> check_bipartite({"a": [1, 3], "b": [0, 2], "c": [1, 3], "d": [0, 2]}) - Traceback (most recent call last): - ... - KeyError: 0 - >>> check_bipartite({0: ["b", "d"], 1: ["a", "c"], 2: ["b", "d"], 3: ["a", "c"]}) - Traceback (most recent call last): - ... - TypeError: list indices must be integers or slices, not str - """ - queue = Queue() - visited = [False] * len(graph) - color = [-1] * len(graph) - - def bfs(): - while not queue.empty(): - u = queue.get() - visited[u] = True - - for neighbour in graph[u]: - if neighbour == u: - return False - - if color[neighbour] == -1: - color[neighbour] = 1 - color[u] - queue.put(neighbour) - - elif color[neighbour] == color[u]: - return False - - return True - - for i in range(len(graph)): - if not visited[i]: - queue.put(i) - color[i] = 0 - if bfs() is False: - return False - - return True - - -if __name__ == "__main__": - # Adjacency List of graph - print(check_bipartite({0: [1, 3], 1: [0, 2], 2: [1, 3], 3: [0, 2]})) - import doctest - - doctest.testmod() diff --git a/graphs/check_bipartite_graph_dfs.py b/graphs/check_bipartite_graph_dfs.py deleted file mode 100644 index b13a9eb95afb..000000000000 --- a/graphs/check_bipartite_graph_dfs.py +++ /dev/null @@ -1,55 +0,0 @@ -from collections import defaultdict - - -def is_bipartite(graph: defaultdict[int, list[int]]) -> bool: - """ - Check whether a graph is Bipartite or not using Depth-First Search (DFS). - - A Bipartite Graph is a graph whose vertices can be divided into two independent - sets, U and V such that every edge (u, v) either connects a vertex from - U to V or a vertex from V to U. In other words, for every edge (u, v), - either u belongs to U and v to V, or u belongs to V and v to U. There is - no edge that connects vertices of the same set. - - Args: - graph: An adjacency list representing the graph. - - Returns: - True if there's no edge that connects vertices of the same set, False otherwise. - - Examples: - >>> is_bipartite( - ... defaultdict(list, {0: [1, 2], 1: [0, 3], 2: [0, 4], 3: [1], 4: [2]}) - ... ) - False - >>> is_bipartite(defaultdict(list, {0: [1, 2], 1: [0, 2], 2: [0, 1]})) - True - """ - - def depth_first_search(node: int, color: int) -> bool: - visited[node] = color - return any( - visited[neighbour] == color - or ( - visited[neighbour] == -1 - and not depth_first_search(neighbour, 1 - color) - ) - for neighbour in graph[node] - ) - - visited: defaultdict[int, int] = defaultdict(lambda: -1) - - return all( - not (visited[node] == -1 and not depth_first_search(node, 0)) for node in graph - ) - - -if __name__ == "__main__": - import doctest - - result = doctest.testmod() - - if result.failed: - print(f"{result.failed} test(s) failed.") - else: - print("All tests passed!") diff --git a/graphs/check_bipatrite.py b/graphs/check_bipatrite.py new file mode 100644 index 000000000000..10b9cc965251 --- /dev/null +++ b/graphs/check_bipatrite.py @@ -0,0 +1,179 @@ +from collections import defaultdict, deque + + +def is_bipartite_dfs(graph: defaultdict[int, list[int]]) -> bool: + """ + Check if a graph is bipartite using depth-first search (DFS). + + Args: + graph: Adjacency list representing the graph. + + Returns: + True if bipartite, False otherwise. + + Checks if the graph can be divided into two sets of vertices, such that no two + vertices within the same set are connected by an edge. + + Examples: + # FIXME: This test should pass. + >>> is_bipartite_dfs(defaultdict(list, {0: [1, 2], 1: [0, 3], 2: [0, 4]})) + Traceback (most recent call last): + ... + RuntimeError: dictionary changed size during iteration + >>> is_bipartite_dfs(defaultdict(list, {0: [1, 2], 1: [0, 3], 2: [0, 1]})) + False + >>> is_bipartite_dfs({}) + True + >>> is_bipartite_dfs({0: [1, 3], 1: [0, 2], 2: [1, 3], 3: [0, 2]}) + True + >>> is_bipartite_dfs({0: [1, 2, 3], 1: [0, 2], 2: [0, 1, 3], 3: [0, 2]}) + False + >>> is_bipartite_dfs({0: [4], 1: [], 2: [4], 3: [4], 4: [0, 2, 3]}) + True + >>> is_bipartite_dfs({0: [1, 3], 1: [0, 2], 2: [1, 3], 3: [0, 2], 4: [0]}) + False + >>> is_bipartite_dfs({7: [1, 3], 1: [0, 2], 2: [1, 3], 3: [0, 2], 4: [0]}) + Traceback (most recent call last): + ... + KeyError: 0 + + # FIXME: This test should fails with KeyError: 4. + >>> is_bipartite_dfs({0: [1, 3], 1: [0, 2], 2: [1, 3], 3: [0, 2], 9: [0]}) + False + >>> is_bipartite_dfs({0: [-1, 3], 1: [0, -2]}) + Traceback (most recent call last): + ... + KeyError: -1 + >>> is_bipartite_dfs({-1: [0, 2], 0: [-1, 1], 1: [0, 2], 2: [-1, 1]}) + True + >>> is_bipartite_dfs({0.9: [1, 3], 1: [0, 2], 2: [1, 3], 3: [0, 2]}) + Traceback (most recent call last): + ... + KeyError: 0 + + # FIXME: This test should fails with TypeError: list indices must be integers or... + >>> is_bipartite_dfs({0: [1.0, 3.0], 1.0: [0, 2.0], 2.0: [1.0, 3.0], 3.0: [0, 2.0]}) + True + >>> is_bipartite_dfs({"a": [1, 3], "b": [0, 2], "c": [1, 3], "d": [0, 2]}) + Traceback (most recent call last): + ... + KeyError: 1 + >>> is_bipartite_dfs({0: ["b", "d"], 1: ["a", "c"], 2: ["b", "d"], 3: ["a", "c"]}) + Traceback (most recent call last): + ... + KeyError: 'b' + """ + + def depth_first_search(node: int, color: int) -> bool: + """ + Perform Depth-First Search (DFS) on the graph starting from a node. + + Args: + node: The current node being visited. + color: The color assigned to the current node. + + Returns: + True if the graph is bipartite starting from the current node, + False otherwise. + """ + if visited[node] == -1: + visited[node] = color + for neighbor in graph[node]: + if not depth_first_search(neighbor, 1 - color): + return False + return visited[node] == color + + visited: defaultdict[int, int] = defaultdict(lambda: -1) + for node in graph: + if visited[node] == -1 and not depth_first_search(node, 0): + return False + return True + + +def is_bipartite_bfs(graph: defaultdict[int, list[int]]) -> bool: + """ + Check if a graph is bipartite using a breadth-first search (BFS). + + Args: + graph: Adjacency list representing the graph. + + Returns: + True if bipartite, False otherwise. + + Check if the graph can be divided into two sets of vertices, such that no two + vertices within the same set are connected by an edge. + + Examples: + # FIXME: This test should pass. + >>> is_bipartite_bfs(defaultdict(list, {0: [1, 2], 1: [0, 3], 2: [0, 4]})) + Traceback (most recent call last): + ... + RuntimeError: dictionary changed size during iteration + >>> is_bipartite_bfs(defaultdict(list, {0: [1, 2], 1: [0, 2], 2: [0, 1]})) + False + >>> is_bipartite_bfs({}) + True + >>> is_bipartite_bfs({0: [1, 3], 1: [0, 2], 2: [1, 3], 3: [0, 2]}) + True + >>> is_bipartite_bfs({0: [1, 2, 3], 1: [0, 2], 2: [0, 1, 3], 3: [0, 2]}) + False + >>> is_bipartite_bfs({0: [4], 1: [], 2: [4], 3: [4], 4: [0, 2, 3]}) + True + >>> is_bipartite_bfs({0: [1, 3], 1: [0, 2], 2: [1, 3], 3: [0, 2], 4: [0]}) + False + >>> is_bipartite_bfs({7: [1, 3], 1: [0, 2], 2: [1, 3], 3: [0, 2], 4: [0]}) + Traceback (most recent call last): + ... + KeyError: 0 + + # FIXME: This test should fails with KeyError: 4. + >>> is_bipartite_bfs({0: [1, 3], 1: [0, 2], 2: [1, 3], 3: [0, 2], 9: [0]}) + False + >>> is_bipartite_bfs({0: [-1, 3], 1: [0, -2]}) + Traceback (most recent call last): + ... + KeyError: -1 + >>> is_bipartite_bfs({-1: [0, 2], 0: [-1, 1], 1: [0, 2], 2: [-1, 1]}) + True + >>> is_bipartite_bfs({0.9: [1, 3], 1: [0, 2], 2: [1, 3], 3: [0, 2]}) + Traceback (most recent call last): + ... + KeyError: 0 + + # FIXME: This test should fails with TypeError: list indices must be integers or... + >>> is_bipartite_bfs({0: [1.0, 3.0], 1.0: [0, 2.0], 2.0: [1.0, 3.0], 3.0: [0, 2.0]}) + True + >>> is_bipartite_bfs({"a": [1, 3], "b": [0, 2], "c": [1, 3], "d": [0, 2]}) + Traceback (most recent call last): + ... + KeyError: 1 + >>> is_bipartite_bfs({0: ["b", "d"], 1: ["a", "c"], 2: ["b", "d"], 3: ["a", "c"]}) + Traceback (most recent call last): + ... + KeyError: 'b' + """ + visited: defaultdict[int, int] = defaultdict(lambda: -1) + for node in graph: + if visited[node] == -1: + queue: deque[int] = deque() + queue.append(node) + visited[node] = 0 + while queue: + curr_node = queue.popleft() + for neighbor in graph[curr_node]: + if visited[neighbor] == -1: + visited[neighbor] = 1 - visited[curr_node] + queue.append(neighbor) + elif visited[neighbor] == visited[curr_node]: + return False + return True + + +if __name__ == "__main": + import doctest + + result = doctest.testmod() + if result.failed: + print(f"{result.failed} test(s) failed.") + else: + print("All tests passed!") From 6f2d6f72d56f832dcfaaf226688c1dab4cdb9d0e Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Fri, 20 Oct 2023 02:17:31 -0400 Subject: [PATCH 422/757] Move files for special numbers to own directory (#10714) --- .../armstrong_numbers.py | 196 +++++------ .../automorphic_number.py | 0 maths/{ => special_numbers}/bell_numbers.py | 0 .../carmichael_number.py | 0 maths/{ => special_numbers}/catalan_number.py | 0 .../{ => special_numbers}/hamming_numbers.py | 0 .../{ => special_numbers}/harshad_numbers.py | 316 +++++++++--------- .../{ => special_numbers}/hexagonal_number.py | 0 .../krishnamurthy_number.py | 0 maths/{ => special_numbers}/perfect_number.py | 0 .../polygonal_numbers.py | 0 maths/{ => special_numbers}/pronic_number.py | 0 maths/{ => special_numbers}/proth_number.py | 0 maths/{ => special_numbers}/ugly_numbers.py | 108 +++--- maths/{ => special_numbers}/weird_number.py | 0 15 files changed, 310 insertions(+), 310 deletions(-) rename maths/{ => special_numbers}/armstrong_numbers.py (96%) rename maths/{ => special_numbers}/automorphic_number.py (100%) rename maths/{ => special_numbers}/bell_numbers.py (100%) rename maths/{ => special_numbers}/carmichael_number.py (100%) rename maths/{ => special_numbers}/catalan_number.py (100%) rename maths/{ => special_numbers}/hamming_numbers.py (100%) rename maths/{ => special_numbers}/harshad_numbers.py (96%) rename maths/{ => special_numbers}/hexagonal_number.py (100%) rename maths/{ => special_numbers}/krishnamurthy_number.py (100%) rename maths/{ => special_numbers}/perfect_number.py (100%) rename maths/{ => special_numbers}/polygonal_numbers.py (100%) rename maths/{ => special_numbers}/pronic_number.py (100%) rename maths/{ => special_numbers}/proth_number.py (100%) rename maths/{ => special_numbers}/ugly_numbers.py (96%) rename maths/{ => special_numbers}/weird_number.py (100%) diff --git a/maths/armstrong_numbers.py b/maths/special_numbers/armstrong_numbers.py similarity index 96% rename from maths/armstrong_numbers.py rename to maths/special_numbers/armstrong_numbers.py index e1c25d4676c3..b037aacb16c3 100644 --- a/maths/armstrong_numbers.py +++ b/maths/special_numbers/armstrong_numbers.py @@ -1,98 +1,98 @@ -""" -An Armstrong number is equal to the sum of its own digits each raised to the -power of the number of digits. - -For example, 370 is an Armstrong number because 3*3*3 + 7*7*7 + 0*0*0 = 370. - -Armstrong numbers are also called Narcissistic numbers and Pluperfect numbers. - -On-Line Encyclopedia of Integer Sequences entry: https://oeis.org/A005188 -""" -PASSING = (1, 153, 370, 371, 1634, 24678051, 115132219018763992565095597973971522401) -FAILING: tuple = (-153, -1, 0, 1.2, 200, "A", [], {}, None) - - -def armstrong_number(n: int) -> bool: - """ - Return True if n is an Armstrong number or False if it is not. - - >>> all(armstrong_number(n) for n in PASSING) - True - >>> any(armstrong_number(n) for n in FAILING) - False - """ - if not isinstance(n, int) or n < 1: - return False - - # Initialization of sum and number of digits. - total = 0 - number_of_digits = 0 - temp = n - # Calculation of digits of the number - number_of_digits = len(str(n)) - # Dividing number into separate digits and find Armstrong number - temp = n - while temp > 0: - rem = temp % 10 - total += rem**number_of_digits - temp //= 10 - return n == total - - -def pluperfect_number(n: int) -> bool: - """Return True if n is a pluperfect number or False if it is not - - >>> all(armstrong_number(n) for n in PASSING) - True - >>> any(armstrong_number(n) for n in FAILING) - False - """ - if not isinstance(n, int) or n < 1: - return False - - # Init a "histogram" of the digits - digit_histogram = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] - digit_total = 0 - total = 0 - temp = n - while temp > 0: - temp, rem = divmod(temp, 10) - digit_histogram[rem] += 1 - digit_total += 1 - - for cnt, i in zip(digit_histogram, range(len(digit_histogram))): - total += cnt * i**digit_total - - return n == total - - -def narcissistic_number(n: int) -> bool: - """Return True if n is a narcissistic number or False if it is not. - - >>> all(armstrong_number(n) for n in PASSING) - True - >>> any(armstrong_number(n) for n in FAILING) - False - """ - if not isinstance(n, int) or n < 1: - return False - expo = len(str(n)) # the power that all digits will be raised to - # check if sum of each digit multiplied expo times is equal to number - return n == sum(int(i) ** expo for i in str(n)) - - -def main(): - """ - Request that user input an integer and tell them if it is Armstrong number. - """ - num = int(input("Enter an integer to see if it is an Armstrong number: ").strip()) - print(f"{num} is {'' if armstrong_number(num) else 'not '}an Armstrong number.") - print(f"{num} is {'' if narcissistic_number(num) else 'not '}an Armstrong number.") - print(f"{num} is {'' if pluperfect_number(num) else 'not '}an Armstrong number.") - - -if __name__ == "__main__": - import doctest - - doctest.testmod() - main() +""" +An Armstrong number is equal to the sum of its own digits each raised to the +power of the number of digits. + +For example, 370 is an Armstrong number because 3*3*3 + 7*7*7 + 0*0*0 = 370. + +Armstrong numbers are also called Narcissistic numbers and Pluperfect numbers. + +On-Line Encyclopedia of Integer Sequences entry: https://oeis.org/A005188 +""" +PASSING = (1, 153, 370, 371, 1634, 24678051, 115132219018763992565095597973971522401) +FAILING: tuple = (-153, -1, 0, 1.2, 200, "A", [], {}, None) + + +def armstrong_number(n: int) -> bool: + """ + Return True if n is an Armstrong number or False if it is not. + + >>> all(armstrong_number(n) for n in PASSING) + True + >>> any(armstrong_number(n) for n in FAILING) + False + """ + if not isinstance(n, int) or n < 1: + return False + + # Initialization of sum and number of digits. + total = 0 + number_of_digits = 0 + temp = n + # Calculation of digits of the number + number_of_digits = len(str(n)) + # Dividing number into separate digits and find Armstrong number + temp = n + while temp > 0: + rem = temp % 10 + total += rem**number_of_digits + temp //= 10 + return n == total + + +def pluperfect_number(n: int) -> bool: + """Return True if n is a pluperfect number or False if it is not + + >>> all(armstrong_number(n) for n in PASSING) + True + >>> any(armstrong_number(n) for n in FAILING) + False + """ + if not isinstance(n, int) or n < 1: + return False + + # Init a "histogram" of the digits + digit_histogram = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + digit_total = 0 + total = 0 + temp = n + while temp > 0: + temp, rem = divmod(temp, 10) + digit_histogram[rem] += 1 + digit_total += 1 + + for cnt, i in zip(digit_histogram, range(len(digit_histogram))): + total += cnt * i**digit_total + + return n == total + + +def narcissistic_number(n: int) -> bool: + """Return True if n is a narcissistic number or False if it is not. + + >>> all(armstrong_number(n) for n in PASSING) + True + >>> any(armstrong_number(n) for n in FAILING) + False + """ + if not isinstance(n, int) or n < 1: + return False + expo = len(str(n)) # the power that all digits will be raised to + # check if sum of each digit multiplied expo times is equal to number + return n == sum(int(i) ** expo for i in str(n)) + + +def main(): + """ + Request that user input an integer and tell them if it is Armstrong number. + """ + num = int(input("Enter an integer to see if it is an Armstrong number: ").strip()) + print(f"{num} is {'' if armstrong_number(num) else 'not '}an Armstrong number.") + print(f"{num} is {'' if narcissistic_number(num) else 'not '}an Armstrong number.") + print(f"{num} is {'' if pluperfect_number(num) else 'not '}an Armstrong number.") + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + main() diff --git a/maths/automorphic_number.py b/maths/special_numbers/automorphic_number.py similarity index 100% rename from maths/automorphic_number.py rename to maths/special_numbers/automorphic_number.py diff --git a/maths/bell_numbers.py b/maths/special_numbers/bell_numbers.py similarity index 100% rename from maths/bell_numbers.py rename to maths/special_numbers/bell_numbers.py diff --git a/maths/carmichael_number.py b/maths/special_numbers/carmichael_number.py similarity index 100% rename from maths/carmichael_number.py rename to maths/special_numbers/carmichael_number.py diff --git a/maths/catalan_number.py b/maths/special_numbers/catalan_number.py similarity index 100% rename from maths/catalan_number.py rename to maths/special_numbers/catalan_number.py diff --git a/maths/hamming_numbers.py b/maths/special_numbers/hamming_numbers.py similarity index 100% rename from maths/hamming_numbers.py rename to maths/special_numbers/hamming_numbers.py diff --git a/maths/harshad_numbers.py b/maths/special_numbers/harshad_numbers.py similarity index 96% rename from maths/harshad_numbers.py rename to maths/special_numbers/harshad_numbers.py index 050c69e0bd15..61667adfa127 100644 --- a/maths/harshad_numbers.py +++ b/maths/special_numbers/harshad_numbers.py @@ -1,158 +1,158 @@ -""" -A harshad number (or more specifically an n-harshad number) is a number that's -divisible by the sum of its digits in some given base n. -Reference: https://en.wikipedia.org/wiki/Harshad_number -""" - - -def int_to_base(number: int, base: int) -> str: - """ - Convert a given positive decimal integer to base 'base'. - Where 'base' ranges from 2 to 36. - - Examples: - >>> int_to_base(23, 2) - '10111' - >>> int_to_base(58, 5) - '213' - >>> int_to_base(167, 16) - 'A7' - >>> # bases below 2 and beyond 36 will error - >>> int_to_base(98, 1) - Traceback (most recent call last): - ... - ValueError: 'base' must be between 2 and 36 inclusive - >>> int_to_base(98, 37) - Traceback (most recent call last): - ... - ValueError: 'base' must be between 2 and 36 inclusive - """ - - if base < 2 or base > 36: - raise ValueError("'base' must be between 2 and 36 inclusive") - - digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" - result = "" - - if number < 0: - raise ValueError("number must be a positive integer") - - while number > 0: - number, remainder = divmod(number, base) - result = digits[remainder] + result - - if result == "": - result = "0" - - return result - - -def sum_of_digits(num: int, base: int) -> str: - """ - Calculate the sum of digit values in a positive integer - converted to the given 'base'. - Where 'base' ranges from 2 to 36. - - Examples: - >>> sum_of_digits(103, 12) - '13' - >>> sum_of_digits(1275, 4) - '30' - >>> sum_of_digits(6645, 2) - '1001' - >>> # bases below 2 and beyond 36 will error - >>> sum_of_digits(543, 1) - Traceback (most recent call last): - ... - ValueError: 'base' must be between 2 and 36 inclusive - >>> sum_of_digits(543, 37) - Traceback (most recent call last): - ... - ValueError: 'base' must be between 2 and 36 inclusive - """ - - if base < 2 or base > 36: - raise ValueError("'base' must be between 2 and 36 inclusive") - - num_str = int_to_base(num, base) - res = sum(int(char, base) for char in num_str) - res_str = int_to_base(res, base) - return res_str - - -def harshad_numbers_in_base(limit: int, base: int) -> list[str]: - """ - Finds all Harshad numbers smaller than num in base 'base'. - Where 'base' ranges from 2 to 36. - - Examples: - >>> harshad_numbers_in_base(15, 2) - ['1', '10', '100', '110', '1000', '1010', '1100'] - >>> harshad_numbers_in_base(12, 34) - ['1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B'] - >>> harshad_numbers_in_base(12, 4) - ['1', '2', '3', '10', '12', '20', '21'] - >>> # bases below 2 and beyond 36 will error - >>> harshad_numbers_in_base(234, 37) - Traceback (most recent call last): - ... - ValueError: 'base' must be between 2 and 36 inclusive - >>> harshad_numbers_in_base(234, 1) - Traceback (most recent call last): - ... - ValueError: 'base' must be between 2 and 36 inclusive - """ - - if base < 2 or base > 36: - raise ValueError("'base' must be between 2 and 36 inclusive") - - if limit < 0: - return [] - - numbers = [ - int_to_base(i, base) - for i in range(1, limit) - if i % int(sum_of_digits(i, base), base) == 0 - ] - - return numbers - - -def is_harshad_number_in_base(num: int, base: int) -> bool: - """ - Determines whether n in base 'base' is a harshad number. - Where 'base' ranges from 2 to 36. - - Examples: - >>> is_harshad_number_in_base(18, 10) - True - >>> is_harshad_number_in_base(21, 10) - True - >>> is_harshad_number_in_base(-21, 5) - False - >>> # bases below 2 and beyond 36 will error - >>> is_harshad_number_in_base(45, 37) - Traceback (most recent call last): - ... - ValueError: 'base' must be between 2 and 36 inclusive - >>> is_harshad_number_in_base(45, 1) - Traceback (most recent call last): - ... - ValueError: 'base' must be between 2 and 36 inclusive - """ - - if base < 2 or base > 36: - raise ValueError("'base' must be between 2 and 36 inclusive") - - if num < 0: - return False - - n = int_to_base(num, base) - d = sum_of_digits(num, base) - return int(n, base) % int(d, base) == 0 - - -if __name__ == "__main__": - import doctest - - doctest.testmod() +""" +A harshad number (or more specifically an n-harshad number) is a number that's +divisible by the sum of its digits in some given base n. +Reference: https://en.wikipedia.org/wiki/Harshad_number +""" + + +def int_to_base(number: int, base: int) -> str: + """ + Convert a given positive decimal integer to base 'base'. + Where 'base' ranges from 2 to 36. + + Examples: + >>> int_to_base(23, 2) + '10111' + >>> int_to_base(58, 5) + '213' + >>> int_to_base(167, 16) + 'A7' + >>> # bases below 2 and beyond 36 will error + >>> int_to_base(98, 1) + Traceback (most recent call last): + ... + ValueError: 'base' must be between 2 and 36 inclusive + >>> int_to_base(98, 37) + Traceback (most recent call last): + ... + ValueError: 'base' must be between 2 and 36 inclusive + """ + + if base < 2 or base > 36: + raise ValueError("'base' must be between 2 and 36 inclusive") + + digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" + result = "" + + if number < 0: + raise ValueError("number must be a positive integer") + + while number > 0: + number, remainder = divmod(number, base) + result = digits[remainder] + result + + if result == "": + result = "0" + + return result + + +def sum_of_digits(num: int, base: int) -> str: + """ + Calculate the sum of digit values in a positive integer + converted to the given 'base'. + Where 'base' ranges from 2 to 36. + + Examples: + >>> sum_of_digits(103, 12) + '13' + >>> sum_of_digits(1275, 4) + '30' + >>> sum_of_digits(6645, 2) + '1001' + >>> # bases below 2 and beyond 36 will error + >>> sum_of_digits(543, 1) + Traceback (most recent call last): + ... + ValueError: 'base' must be between 2 and 36 inclusive + >>> sum_of_digits(543, 37) + Traceback (most recent call last): + ... + ValueError: 'base' must be between 2 and 36 inclusive + """ + + if base < 2 or base > 36: + raise ValueError("'base' must be between 2 and 36 inclusive") + + num_str = int_to_base(num, base) + res = sum(int(char, base) for char in num_str) + res_str = int_to_base(res, base) + return res_str + + +def harshad_numbers_in_base(limit: int, base: int) -> list[str]: + """ + Finds all Harshad numbers smaller than num in base 'base'. + Where 'base' ranges from 2 to 36. + + Examples: + >>> harshad_numbers_in_base(15, 2) + ['1', '10', '100', '110', '1000', '1010', '1100'] + >>> harshad_numbers_in_base(12, 34) + ['1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B'] + >>> harshad_numbers_in_base(12, 4) + ['1', '2', '3', '10', '12', '20', '21'] + >>> # bases below 2 and beyond 36 will error + >>> harshad_numbers_in_base(234, 37) + Traceback (most recent call last): + ... + ValueError: 'base' must be between 2 and 36 inclusive + >>> harshad_numbers_in_base(234, 1) + Traceback (most recent call last): + ... + ValueError: 'base' must be between 2 and 36 inclusive + """ + + if base < 2 or base > 36: + raise ValueError("'base' must be between 2 and 36 inclusive") + + if limit < 0: + return [] + + numbers = [ + int_to_base(i, base) + for i in range(1, limit) + if i % int(sum_of_digits(i, base), base) == 0 + ] + + return numbers + + +def is_harshad_number_in_base(num: int, base: int) -> bool: + """ + Determines whether n in base 'base' is a harshad number. + Where 'base' ranges from 2 to 36. + + Examples: + >>> is_harshad_number_in_base(18, 10) + True + >>> is_harshad_number_in_base(21, 10) + True + >>> is_harshad_number_in_base(-21, 5) + False + >>> # bases below 2 and beyond 36 will error + >>> is_harshad_number_in_base(45, 37) + Traceback (most recent call last): + ... + ValueError: 'base' must be between 2 and 36 inclusive + >>> is_harshad_number_in_base(45, 1) + Traceback (most recent call last): + ... + ValueError: 'base' must be between 2 and 36 inclusive + """ + + if base < 2 or base > 36: + raise ValueError("'base' must be between 2 and 36 inclusive") + + if num < 0: + return False + + n = int_to_base(num, base) + d = sum_of_digits(num, base) + return int(n, base) % int(d, base) == 0 + + +if __name__ == "__main__": + import doctest + + doctest.testmod() diff --git a/maths/hexagonal_number.py b/maths/special_numbers/hexagonal_number.py similarity index 100% rename from maths/hexagonal_number.py rename to maths/special_numbers/hexagonal_number.py diff --git a/maths/krishnamurthy_number.py b/maths/special_numbers/krishnamurthy_number.py similarity index 100% rename from maths/krishnamurthy_number.py rename to maths/special_numbers/krishnamurthy_number.py diff --git a/maths/perfect_number.py b/maths/special_numbers/perfect_number.py similarity index 100% rename from maths/perfect_number.py rename to maths/special_numbers/perfect_number.py diff --git a/maths/polygonal_numbers.py b/maths/special_numbers/polygonal_numbers.py similarity index 100% rename from maths/polygonal_numbers.py rename to maths/special_numbers/polygonal_numbers.py diff --git a/maths/pronic_number.py b/maths/special_numbers/pronic_number.py similarity index 100% rename from maths/pronic_number.py rename to maths/special_numbers/pronic_number.py diff --git a/maths/proth_number.py b/maths/special_numbers/proth_number.py similarity index 100% rename from maths/proth_number.py rename to maths/special_numbers/proth_number.py diff --git a/maths/ugly_numbers.py b/maths/special_numbers/ugly_numbers.py similarity index 96% rename from maths/ugly_numbers.py rename to maths/special_numbers/ugly_numbers.py index 81bd928c6b3d..c6ceb784622a 100644 --- a/maths/ugly_numbers.py +++ b/maths/special_numbers/ugly_numbers.py @@ -1,54 +1,54 @@ -""" -Ugly numbers are numbers whose only prime factors are 2, 3 or 5. The sequence -1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, … shows the first 11 ugly numbers. By convention, -1 is included. -Given an integer n, we have to find the nth ugly number. - -For more details, refer this article -https://www.geeksforgeeks.org/ugly-numbers/ -""" - - -def ugly_numbers(n: int) -> int: - """ - Returns the nth ugly number. - >>> ugly_numbers(100) - 1536 - >>> ugly_numbers(0) - 1 - >>> ugly_numbers(20) - 36 - >>> ugly_numbers(-5) - 1 - >>> ugly_numbers(-5.5) - Traceback (most recent call last): - ... - TypeError: 'float' object cannot be interpreted as an integer - """ - ugly_nums = [1] - - i2, i3, i5 = 0, 0, 0 - next_2 = ugly_nums[i2] * 2 - next_3 = ugly_nums[i3] * 3 - next_5 = ugly_nums[i5] * 5 - - for _ in range(1, n): - next_num = min(next_2, next_3, next_5) - ugly_nums.append(next_num) - if next_num == next_2: - i2 += 1 - next_2 = ugly_nums[i2] * 2 - if next_num == next_3: - i3 += 1 - next_3 = ugly_nums[i3] * 3 - if next_num == next_5: - i5 += 1 - next_5 = ugly_nums[i5] * 5 - return ugly_nums[-1] - - -if __name__ == "__main__": - from doctest import testmod - - testmod(verbose=True) - print(f"{ugly_numbers(200) = }") +""" +Ugly numbers are numbers whose only prime factors are 2, 3 or 5. The sequence +1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, … shows the first 11 ugly numbers. By convention, +1 is included. +Given an integer n, we have to find the nth ugly number. + +For more details, refer this article +https://www.geeksforgeeks.org/ugly-numbers/ +""" + + +def ugly_numbers(n: int) -> int: + """ + Returns the nth ugly number. + >>> ugly_numbers(100) + 1536 + >>> ugly_numbers(0) + 1 + >>> ugly_numbers(20) + 36 + >>> ugly_numbers(-5) + 1 + >>> ugly_numbers(-5.5) + Traceback (most recent call last): + ... + TypeError: 'float' object cannot be interpreted as an integer + """ + ugly_nums = [1] + + i2, i3, i5 = 0, 0, 0 + next_2 = ugly_nums[i2] * 2 + next_3 = ugly_nums[i3] * 3 + next_5 = ugly_nums[i5] * 5 + + for _ in range(1, n): + next_num = min(next_2, next_3, next_5) + ugly_nums.append(next_num) + if next_num == next_2: + i2 += 1 + next_2 = ugly_nums[i2] * 2 + if next_num == next_3: + i3 += 1 + next_3 = ugly_nums[i3] * 3 + if next_num == next_5: + i5 += 1 + next_5 = ugly_nums[i5] * 5 + return ugly_nums[-1] + + +if __name__ == "__main__": + from doctest import testmod + + testmod(verbose=True) + print(f"{ugly_numbers(200) = }") diff --git a/maths/weird_number.py b/maths/special_numbers/weird_number.py similarity index 100% rename from maths/weird_number.py rename to maths/special_numbers/weird_number.py From ce0ede6476fb14ba18ef03246b169a7e5615bdec Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Fri, 20 Oct 2023 03:08:23 -0400 Subject: [PATCH 423/757] Fix typo in DPLL file name (#10723) * Fix DPLL file name * updating DIRECTORY.md --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 46 +++++++++++-------- ...d.py => davis_putnam_logemann_loveland.py} | 0 2 files changed, 28 insertions(+), 18 deletions(-) rename other/{davisb_putnamb_logemannb_loveland.py => davis_putnam_logemann_loveland.py} (100%) diff --git a/DIRECTORY.md b/DIRECTORY.md index 1320c70ef629..5b7ca856ea15 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -65,7 +65,9 @@ ## Boolean Algebra * [And Gate](boolean_algebra/and_gate.py) + * [Imply Gate](boolean_algebra/imply_gate.py) * [Nand Gate](boolean_algebra/nand_gate.py) + * [Nimply Gate](boolean_algebra/nimply_gate.py) * [Nor Gate](boolean_algebra/nor_gate.py) * [Not Gate](boolean_algebra/not_gate.py) * [Or Gate](boolean_algebra/or_gate.py) @@ -178,7 +180,9 @@ ## Data Structures * Arrays * [Equilibrium Index In Array](data_structures/arrays/equilibrium_index_in_array.py) + * [Find Triplets With 0 Sum](data_structures/arrays/find_triplets_with_0_sum.py) * [Median Two Array](data_structures/arrays/median_two_array.py) + * [Pairs With Given Sum](data_structures/arrays/pairs_with_given_sum.py) * [Permutations](data_structures/arrays/permutations.py) * [Prefix Sum](data_structures/arrays/prefix_sum.py) * [Product Sum](data_structures/arrays/product_sum.py) @@ -398,6 +402,7 @@ ## Financial * [Equated Monthly Installments](financial/equated_monthly_installments.py) + * [Exponential Moving Average](financial/exponential_moving_average.py) * [Interest](financial/interest.py) * [Present Value](financial/present_value.py) * [Price Plus Tax](financial/price_plus_tax.py) @@ -433,8 +438,7 @@ * [Breadth First Search Shortest Path](graphs/breadth_first_search_shortest_path.py) * [Breadth First Search Shortest Path 2](graphs/breadth_first_search_shortest_path_2.py) * [Breadth First Search Zero One Shortest Path](graphs/breadth_first_search_zero_one_shortest_path.py) - * [Check Bipartite Graph Bfs](graphs/check_bipartite_graph_bfs.py) - * [Check Bipartite Graph Dfs](graphs/check_bipartite_graph_dfs.py) + * [Check Bipatrite](graphs/check_bipatrite.py) * [Check Cycle](graphs/check_cycle.py) * [Connected Components](graphs/connected_components.py) * [Deep Clone Graph](graphs/deep_clone_graph.py) @@ -572,8 +576,6 @@ * [Arc Length](maths/arc_length.py) * [Area](maths/area.py) * [Area Under Curve](maths/area_under_curve.py) - * [Armstrong Numbers](maths/armstrong_numbers.py) - * [Automorphic Number](maths/automorphic_number.py) * [Average Absolute Deviation](maths/average_absolute_deviation.py) * [Average Mean](maths/average_mean.py) * [Average Median](maths/average_median.py) @@ -581,7 +583,6 @@ * [Bailey Borwein Plouffe](maths/bailey_borwein_plouffe.py) * [Base Neg2 Conversion](maths/base_neg2_conversion.py) * [Basic Maths](maths/basic_maths.py) - * [Bell Numbers](maths/bell_numbers.py) * [Binary Exp Mod](maths/binary_exp_mod.py) * [Binary Exponentiation](maths/binary_exponentiation.py) * [Binary Exponentiation 2](maths/binary_exponentiation_2.py) @@ -589,8 +590,6 @@ * [Binomial Coefficient](maths/binomial_coefficient.py) * [Binomial Distribution](maths/binomial_distribution.py) * [Bisection](maths/bisection.py) - * [Carmichael Number](maths/carmichael_number.py) - * [Catalan Number](maths/catalan_number.py) * [Ceil](maths/ceil.py) * [Chebyshev Distance](maths/chebyshev_distance.py) * [Check Polygon](maths/check_polygon.py) @@ -623,10 +622,7 @@ * [Gcd Of N Numbers](maths/gcd_of_n_numbers.py) * [Germain Primes](maths/germain_primes.py) * [Greatest Common Divisor](maths/greatest_common_divisor.py) - * [Hamming Numbers](maths/hamming_numbers.py) * [Hardy Ramanujanalgo](maths/hardy_ramanujanalgo.py) - * [Harshad Numbers](maths/harshad_numbers.py) - * [Hexagonal Number](maths/hexagonal_number.py) * [Integration By Simpson Approx](maths/integration_by_simpson_approx.py) * [Interquartile Range](maths/interquartile_range.py) * [Is Int Palindrome](maths/is_int_palindrome.py) @@ -636,7 +632,6 @@ * [Joint Probability Distribution](maths/joint_probability_distribution.py) * [Juggler Sequence](maths/juggler_sequence.py) * [Karatsuba](maths/karatsuba.py) - * [Krishnamurthy Number](maths/krishnamurthy_number.py) * [Kth Lexicographic Permutation](maths/kth_lexicographic_permutation.py) * [Largest Of Very Large Numbers](maths/largest_of_very_large_numbers.py) * [Least Common Multiple](maths/least_common_multiple.py) @@ -661,14 +656,12 @@ * [Numerical Integration](maths/numerical_integration.py) * [Odd Sieve](maths/odd_sieve.py) * [Perfect Cube](maths/perfect_cube.py) - * [Perfect Number](maths/perfect_number.py) * [Perfect Square](maths/perfect_square.py) * [Persistence](maths/persistence.py) * [Pi Generator](maths/pi_generator.py) * [Pi Monte Carlo Estimation](maths/pi_monte_carlo_estimation.py) * [Points Are Collinear 3D](maths/points_are_collinear_3d.py) * [Pollard Rho](maths/pollard_rho.py) - * [Polygonal Numbers](maths/polygonal_numbers.py) * [Polynomial Evaluation](maths/polynomial_evaluation.py) * Polynomials * [Single Indeterminate Operations](maths/polynomials/single_indeterminate_operations.py) @@ -679,8 +672,6 @@ * [Prime Sieve Eratosthenes](maths/prime_sieve_eratosthenes.py) * [Primelib](maths/primelib.py) * [Print Multiplication Table](maths/print_multiplication_table.py) - * [Pronic Number](maths/pronic_number.py) - * [Proth Number](maths/proth_number.py) * [Pythagoras](maths/pythagoras.py) * [Qr Decomposition](maths/qr_decomposition.py) * [Quadratic Equations Complex Numbers](maths/quadratic_equations_complex_numbers.py) @@ -706,6 +697,23 @@ * [Sin](maths/sin.py) * [Sock Merchant](maths/sock_merchant.py) * [Softmax](maths/softmax.py) + * [Solovay Strassen Primality Test](maths/solovay_strassen_primality_test.py) + * Special Numbers + * [Armstrong Numbers](maths/special_numbers/armstrong_numbers.py) + * [Automorphic Number](maths/special_numbers/automorphic_number.py) + * [Bell Numbers](maths/special_numbers/bell_numbers.py) + * [Carmichael Number](maths/special_numbers/carmichael_number.py) + * [Catalan Number](maths/special_numbers/catalan_number.py) + * [Hamming Numbers](maths/special_numbers/hamming_numbers.py) + * [Harshad Numbers](maths/special_numbers/harshad_numbers.py) + * [Hexagonal Number](maths/special_numbers/hexagonal_number.py) + * [Krishnamurthy Number](maths/special_numbers/krishnamurthy_number.py) + * [Perfect Number](maths/special_numbers/perfect_number.py) + * [Polygonal Numbers](maths/special_numbers/polygonal_numbers.py) + * [Pronic Number](maths/special_numbers/pronic_number.py) + * [Proth Number](maths/special_numbers/proth_number.py) + * [Ugly Numbers](maths/special_numbers/ugly_numbers.py) + * [Weird Number](maths/special_numbers/weird_number.py) * [Square Root](maths/square_root.py) * [Sum Of Arithmetic Series](maths/sum_of_arithmetic_series.py) * [Sum Of Digits](maths/sum_of_digits.py) @@ -721,9 +729,7 @@ * [Twin Prime](maths/twin_prime.py) * [Two Pointer](maths/two_pointer.py) * [Two Sum](maths/two_sum.py) - * [Ugly Numbers](maths/ugly_numbers.py) * [Volume](maths/volume.py) - * [Weird Number](maths/weird_number.py) * [Zellers Congruence](maths/zellers_congruence.py) ## Matrix @@ -747,6 +753,7 @@ * [Spiral Print](matrix/spiral_print.py) * Tests * [Test Matrix Operation](matrix/tests/test_matrix_operation.py) + * [Validate Sudoku Board](matrix/validate_sudoku_board.py) ## Networking Flow * [Ford Fulkerson](networking_flow/ford_fulkerson.py) @@ -773,7 +780,7 @@ ## Other * [Activity Selection](other/activity_selection.py) * [Alternative List Arrange](other/alternative_list_arrange.py) - * [Davisb Putnamb Logemannb Loveland](other/davisb_putnamb_logemannb_loveland.py) + * [Davis Putnam Logemann Loveland](other/davis_putnam_logemann_loveland.py) * [Dijkstra Bankers Algorithm](other/dijkstra_bankers_algorithm.py) * [Doomsday](other/doomsday.py) * [Fischer Yates Shuffle](other/fischer_yates_shuffle.py) @@ -822,6 +829,7 @@ * [Rms Speed Of Molecule](physics/rms_speed_of_molecule.py) * [Shear Stress](physics/shear_stress.py) * [Speed Of Sound](physics/speed_of_sound.py) + * [Speeds Of Gas Molecules](physics/speeds_of_gas_molecules.py) ## Project Euler * Problem 001 @@ -1212,6 +1220,7 @@ * [Capitalize](strings/capitalize.py) * [Check Anagrams](strings/check_anagrams.py) * [Credit Card Validator](strings/credit_card_validator.py) + * [Damerau Levenshtein Distance](strings/damerau_levenshtein_distance.py) * [Detecting English Programmatically](strings/detecting_english_programmatically.py) * [Dna](strings/dna.py) * [Edit Distance](strings/edit_distance.py) @@ -1246,6 +1255,7 @@ * [String Switch Case](strings/string_switch_case.py) * [Strip](strings/strip.py) * [Text Justification](strings/text_justification.py) + * [Title](strings/title.py) * [Top K Frequent Words](strings/top_k_frequent_words.py) * [Upper](strings/upper.py) * [Wave](strings/wave.py) diff --git a/other/davisb_putnamb_logemannb_loveland.py b/other/davis_putnam_logemann_loveland.py similarity index 100% rename from other/davisb_putnamb_logemannb_loveland.py rename to other/davis_putnam_logemann_loveland.py From 579937613a6dc7e099b710e3d57767a2fab115ad Mon Sep 17 00:00:00 2001 From: Saptadeep Banerjee <69459134+imSanko@users.noreply.github.com> Date: Fri, 20 Oct 2023 16:32:30 +0530 Subject: [PATCH 424/757] Added New Tests in Signum (#10724) * Added new tests! * [ADD]: Inproved Tests * fixed * Removed spaces * Changed the file name * Added Changes * changed the code and kept the test cases * changed the code and kept the test cases * missed the line * removed spaces * Update power_using_recursion.py * Added new tests in Signum * Few things added * Removed few stuff and added few changes * Fixed few things * Reverted the function * Update maths/signum.py Co-authored-by: Christian Clauss * Added few things * Update maths/signum.py Co-authored-by: Christian Clauss * Added the type hint back * Update signum.py --------- Co-authored-by: Christian Clauss --- maths/signum.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/maths/signum.py b/maths/signum.py index 148f931767c1..c89753e76637 100644 --- a/maths/signum.py +++ b/maths/signum.py @@ -7,12 +7,29 @@ def signum(num: float) -> int: """ Applies signum function on the number + Custom test cases: >>> signum(-10) -1 >>> signum(10) 1 >>> signum(0) 0 + >>> signum(-20.5) + -1 + >>> signum(20.5) + 1 + >>> signum(-1e-6) + -1 + >>> signum(1e-6) + 1 + >>> signum("Hello") + Traceback (most recent call last): + ... + TypeError: '<' not supported between instances of 'str' and 'int' + >>> signum([]) + Traceback (most recent call last): + ... + TypeError: '<' not supported between instances of 'list' and 'int' """ if num < 0: return -1 @@ -22,10 +39,17 @@ def signum(num: float) -> int: def test_signum() -> None: """ Tests the signum function + >>> test_signum() """ assert signum(5) == 1 assert signum(-5) == -1 assert signum(0) == 0 + assert signum(10.5) == 1 + assert signum(-10.5) == -1 + assert signum(1e-6) == 1 + assert signum(-1e-6) == -1 + assert signum(123456789) == 1 + assert signum(-123456789) == -1 if __name__ == "__main__": From 52a987ea2f299c8215c1107b8dd793919c962f10 Mon Sep 17 00:00:00 2001 From: Ope Oluwaferanmi <111365699+FEROS01@users.noreply.github.com> Date: Fri, 20 Oct 2023 22:28:21 +0100 Subject: [PATCH 425/757] Add docstrings and doctests and fix a bug ciphers/trifid_cipher.py (#10716) * Added docstrings,doctests and fixed a bug * Added docstrings,doctests and fixed a bug * Added docstrings,doctests and fixed a bug * Added docstrings and doctests with a bug fix * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Added docstrings and doctests with a bug fix * Update ciphers/trifid_cipher.py Co-authored-by: Christian Clauss * Update ciphers/trifid_cipher.py Co-authored-by: Christian Clauss * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Docstrings edit * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update trifid_cipher.py * Update pyproject.toml --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- ciphers/trifid_cipher.py | 191 +++++++++++++++++++++++++++------------ pyproject.toml | 2 +- 2 files changed, 134 insertions(+), 59 deletions(-) diff --git a/ciphers/trifid_cipher.py b/ciphers/trifid_cipher.py index 8aa2263ca5ac..16b9faf67688 100644 --- a/ciphers/trifid_cipher.py +++ b/ciphers/trifid_cipher.py @@ -1,15 +1,35 @@ -# https://en.wikipedia.org/wiki/Trifid_cipher +""" +The trifid cipher uses a table to fractionate each plaintext letter into a trigram, +mixes the constituents of the trigrams, and then applies the table in reverse to turn +these mixed trigrams into ciphertext letters. + +https://en.wikipedia.org/wiki/Trifid_cipher +""" + from __future__ import annotations +# fmt: off +TEST_CHARACTER_TO_NUMBER = { + "A": "111", "B": "112", "C": "113", "D": "121", "E": "122", "F": "123", "G": "131", + "H": "132", "I": "133", "J": "211", "K": "212", "L": "213", "M": "221", "N": "222", + "O": "223", "P": "231", "Q": "232", "R": "233", "S": "311", "T": "312", "U": "313", + "V": "321", "W": "322", "X": "323", "Y": "331", "Z": "332", "+": "333", +} +# fmt: off -def __encrypt_part(message_part: str, character_to_number: dict[str, str]) -> str: - one, two, three = "", "", "" - tmp = [] +TEST_NUMBER_TO_CHARACTER = {val: key for key, val in TEST_CHARACTER_TO_NUMBER.items()} - for character in message_part: - tmp.append(character_to_number[character]) - for each in tmp: +def __encrypt_part(message_part: str, character_to_number: dict[str, str]) -> str: + """ + Arrange the triagram value of each letter of 'message_part' vertically and join + them horizontally. + + >>> __encrypt_part('ASK', TEST_CHARACTER_TO_NUMBER) + '132111112' + """ + one, two, three = "", "", "" + for each in (character_to_number[character] for character in message_part): one += each[0] two += each[1] three += each[2] @@ -20,12 +40,16 @@ def __encrypt_part(message_part: str, character_to_number: dict[str, str]) -> st def __decrypt_part( message_part: str, character_to_number: dict[str, str] ) -> tuple[str, str, str]: - tmp, this_part = "", "" + """ + Convert each letter of the input string into their respective trigram values, join + them and split them into three equal groups of strings which are returned. + + >>> __decrypt_part('ABCDE', TEST_CHARACTER_TO_NUMBER) + ('11111', '21131', '21122') + """ + this_part = "".join(character_to_number[character] for character in message_part) result = [] - - for character in message_part: - this_part += character_to_number[character] - + tmp = "" for digit in this_part: tmp += digit if len(tmp) == len(message_part): @@ -38,6 +62,42 @@ def __decrypt_part( def __prepare( message: str, alphabet: str ) -> tuple[str, str, dict[str, str], dict[str, str]]: + """ + A helper function that generates the triagrams and assigns each letter of the + alphabet to its corresponding triagram and stores this in a dictionary + ("character_to_number" and "number_to_character") after confirming if the + alphabet's length is 27. + + >>> test = __prepare('I aM a BOy','abCdeFghijkLmnopqrStuVwxYZ+') + >>> expected = ('IAMABOY','ABCDEFGHIJKLMNOPQRSTUVWXYZ+', + ... TEST_CHARACTER_TO_NUMBER, TEST_NUMBER_TO_CHARACTER) + >>> test == expected + True + + Testing with incomplete alphabet + >>> __prepare('I aM a BOy','abCdeFghijkLmnopqrStuVw') + Traceback (most recent call last): + ... + KeyError: 'Length of alphabet has to be 27.' + + Testing with extra long alphabets + >>> __prepare('I aM a BOy','abCdeFghijkLmnopqrStuVwxyzzwwtyyujjgfd') + Traceback (most recent call last): + ... + KeyError: 'Length of alphabet has to be 27.' + + Testing with punctuations that are not in the given alphabet + >>> __prepare('am i a boy?','abCdeFghijkLmnopqrStuVwxYZ+') + Traceback (most recent call last): + ... + ValueError: Each message character has to be included in alphabet! + + Testing with numbers + >>> __prepare(500,'abCdeFghijkLmnopqrStuVwxYZ+') + Traceback (most recent call last): + ... + AttributeError: 'int' object has no attribute 'replace' + """ # Validate message and alphabet, set to upper and remove spaces alphabet = alphabet.replace(" ", "").upper() message = message.replace(" ", "").upper() @@ -45,45 +105,14 @@ def __prepare( # Check length and characters if len(alphabet) != 27: raise KeyError("Length of alphabet has to be 27.") - for each in message: - if each not in alphabet: - raise ValueError("Each message character has to be included in alphabet!") + if any(char not in alphabet for char in message): + raise ValueError("Each message character has to be included in alphabet!") # Generate dictionares - numbers = ( - "111", - "112", - "113", - "121", - "122", - "123", - "131", - "132", - "133", - "211", - "212", - "213", - "221", - "222", - "223", - "231", - "232", - "233", - "311", - "312", - "313", - "321", - "322", - "323", - "331", - "332", - "333", - ) - character_to_number = {} - number_to_character = {} - for letter, number in zip(alphabet, numbers): - character_to_number[letter] = number - number_to_character[number] = letter + character_to_number = dict(zip(alphabet, TEST_CHARACTER_TO_NUMBER.values())) + number_to_character = { + number: letter for letter, number in character_to_number.items() + } return message, alphabet, character_to_number, number_to_character @@ -91,44 +120,90 @@ def __prepare( def encrypt_message( message: str, alphabet: str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ.", period: int = 5 ) -> str: + """ + encrypt_message + =============== + + Encrypts a message using the trifid_cipher. Any punctuatuions that + would be used should be added to the alphabet. + + PARAMETERS + ---------- + + * message: The message you want to encrypt. + * alphabet (optional): The characters to be used for the cipher . + * period (optional): The number of characters you want in a group whilst + encrypting. + + >>> encrypt_message('I am a boy') + 'BCDGBQY' + + >>> encrypt_message(' ') + '' + + >>> encrypt_message(' aide toi le c iel ta id era ', + ... 'FELIXMARDSTBCGHJKNOPQUVWYZ+',5) + 'FMJFVOISSUFTFPUFEQQC' + + """ message, alphabet, character_to_number, number_to_character = __prepare( message, alphabet ) - encrypted, encrypted_numeric = "", "" + encrypted_numeric = "" for i in range(0, len(message) + 1, period): encrypted_numeric += __encrypt_part( message[i : i + period], character_to_number ) + encrypted = "" for i in range(0, len(encrypted_numeric), 3): encrypted += number_to_character[encrypted_numeric[i : i + 3]] - return encrypted def decrypt_message( message: str, alphabet: str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ.", period: int = 5 ) -> str: + """ + decrypt_message + =============== + + Decrypts a trifid_cipher encrypted message . + + PARAMETERS + ---------- + + * message: The message you want to decrypt . + * alphabet (optional): The characters used for the cipher. + * period (optional): The number of characters used in grouping when it + was encrypted. + + >>> decrypt_message('BCDGBQY') + 'IAMABOY' + + Decrypting with your own alphabet and period + >>> decrypt_message('FMJFVOISSUFTFPUFEQQC','FELIXMARDSTBCGHJKNOPQUVWYZ+',5) + 'AIDETOILECIELTAIDERA' + """ message, alphabet, character_to_number, number_to_character = __prepare( message, alphabet ) - decrypted_numeric = [] - decrypted = "" - for i in range(0, len(message) + 1, period): + decrypted_numeric = [] + for i in range(0, len(message), period): a, b, c = __decrypt_part(message[i : i + period], character_to_number) for j in range(len(a)): decrypted_numeric.append(a[j] + b[j] + c[j]) - for each in decrypted_numeric: - decrypted += number_to_character[each] - - return decrypted + return "".join(number_to_character[each] for each in decrypted_numeric) if __name__ == "__main__": + import doctest + + doctest.testmod() msg = "DEFEND THE EAST WALL OF THE CASTLE." encrypted = encrypt_message(msg, "EPSDUCVWYM.ZLKXNBTFGORIJHAQ") decrypted = decrypt_message(encrypted, "EPSDUCVWYM.ZLKXNBTFGORIJHAQ") diff --git a/pyproject.toml b/pyproject.toml index 9c9262d77748..790a328b3564 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -135,5 +135,5 @@ omit = [ sort = "Cover" [tool.codespell] -ignore-words-list = "3rt,ans,crate,damon,fo,followings,hist,iff,kwanza,manuel,mater,secant,som,sur,tim,zar" +ignore-words-list = "3rt,ans,crate,damon,fo,followings,hist,iff,kwanza,manuel,mater,secant,som,sur,tim,toi,zar" skip = "./.*,*.json,ciphers/prehistoric_men.txt,project_euler/problem_022/p022_names.txt,pyproject.toml,strings/dictionary.txt,strings/words.txt" From 5645084dcd5cf398caefa40641ac99144a40e572 Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Fri, 20 Oct 2023 17:29:42 -0400 Subject: [PATCH 426/757] Consolidate loss functions into a single file (#10737) * Consolidate loss functions into single file * updating DIRECTORY.md * Fix typo --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 8 +- machine_learning/loss_functions.py | 252 ++++++++++++++++++ .../loss_functions/binary_cross_entropy.py | 59 ---- .../categorical_cross_entropy.py | 85 ------ machine_learning/loss_functions/hinge_loss.py | 64 ----- machine_learning/loss_functions/huber_loss.py | 52 ---- .../loss_functions/mean_squared_error.py | 51 ---- .../mean_squared_logarithmic_error.py | 55 ---- 8 files changed, 253 insertions(+), 373 deletions(-) create mode 100644 machine_learning/loss_functions.py delete mode 100644 machine_learning/loss_functions/binary_cross_entropy.py delete mode 100644 machine_learning/loss_functions/categorical_cross_entropy.py delete mode 100644 machine_learning/loss_functions/hinge_loss.py delete mode 100644 machine_learning/loss_functions/huber_loss.py delete mode 100644 machine_learning/loss_functions/mean_squared_error.py delete mode 100644 machine_learning/loss_functions/mean_squared_logarithmic_error.py diff --git a/DIRECTORY.md b/DIRECTORY.md index 5b7ca856ea15..b92f8f877e97 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -549,13 +549,7 @@ * Local Weighted Learning * [Local Weighted Learning](machine_learning/local_weighted_learning/local_weighted_learning.py) * [Logistic Regression](machine_learning/logistic_regression.py) - * Loss Functions - * [Binary Cross Entropy](machine_learning/loss_functions/binary_cross_entropy.py) - * [Categorical Cross Entropy](machine_learning/loss_functions/categorical_cross_entropy.py) - * [Hinge Loss](machine_learning/loss_functions/hinge_loss.py) - * [Huber Loss](machine_learning/loss_functions/huber_loss.py) - * [Mean Squared Error](machine_learning/loss_functions/mean_squared_error.py) - * [Mean Squared Logarithmic Error](machine_learning/loss_functions/mean_squared_logarithmic_error.py) + * [Loss Functions](machine_learning/loss_functions.py) * [Mfcc](machine_learning/mfcc.py) * [Multilayer Perceptron Classifier](machine_learning/multilayer_perceptron_classifier.py) * [Polynomial Regression](machine_learning/polynomial_regression.py) diff --git a/machine_learning/loss_functions.py b/machine_learning/loss_functions.py new file mode 100644 index 000000000000..0fa0956ed572 --- /dev/null +++ b/machine_learning/loss_functions.py @@ -0,0 +1,252 @@ +import numpy as np + + +def binary_cross_entropy( + y_true: np.ndarray, y_pred: np.ndarray, epsilon: float = 1e-15 +) -> float: + """ + Calculate the mean binary cross-entropy (BCE) loss between true labels and predicted + probabilities. + + BCE loss quantifies dissimilarity between true labels (0 or 1) and predicted + probabilities. It's widely used in binary classification tasks. + + BCE = -Σ(y_true * ln(y_pred) + (1 - y_true) * ln(1 - y_pred)) + + Reference: https://en.wikipedia.org/wiki/Cross_entropy + + Parameters: + - y_true: True binary labels (0 or 1) + - y_pred: Predicted probabilities for class 1 + - epsilon: Small constant to avoid numerical instability + + >>> true_labels = np.array([0, 1, 1, 0, 1]) + >>> predicted_probs = np.array([0.2, 0.7, 0.9, 0.3, 0.8]) + >>> binary_cross_entropy(true_labels, predicted_probs) + 0.2529995012327421 + >>> true_labels = np.array([0, 1, 1, 0, 1]) + >>> predicted_probs = np.array([0.3, 0.8, 0.9, 0.2]) + >>> binary_cross_entropy(true_labels, predicted_probs) + Traceback (most recent call last): + ... + ValueError: Input arrays must have the same length. + """ + if len(y_true) != len(y_pred): + raise ValueError("Input arrays must have the same length.") + + y_pred = np.clip(y_pred, epsilon, 1 - epsilon) # Clip predictions to avoid log(0) + bce_loss = -(y_true * np.log(y_pred) + (1 - y_true) * np.log(1 - y_pred)) + return np.mean(bce_loss) + + +def categorical_cross_entropy( + y_true: np.ndarray, y_pred: np.ndarray, epsilon: float = 1e-15 +) -> float: + """ + Calculate categorical cross-entropy (CCE) loss between true class labels and + predicted class probabilities. + + CCE = -Σ(y_true * ln(y_pred)) + + Reference: https://en.wikipedia.org/wiki/Cross_entropy + + Parameters: + - y_true: True class labels (one-hot encoded) + - y_pred: Predicted class probabilities + - epsilon: Small constant to avoid numerical instability + + >>> true_labels = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]]) + >>> pred_probs = np.array([[0.9, 0.1, 0.0], [0.2, 0.7, 0.1], [0.0, 0.1, 0.9]]) + >>> categorical_cross_entropy(true_labels, pred_probs) + 0.567395975254385 + >>> true_labels = np.array([[1, 0], [0, 1]]) + >>> pred_probs = np.array([[0.9, 0.1, 0.0], [0.2, 0.7, 0.1]]) + >>> categorical_cross_entropy(true_labels, pred_probs) + Traceback (most recent call last): + ... + ValueError: Input arrays must have the same shape. + >>> true_labels = np.array([[2, 0, 1], [1, 0, 0]]) + >>> pred_probs = np.array([[0.9, 0.1, 0.0], [0.2, 0.7, 0.1]]) + >>> categorical_cross_entropy(true_labels, pred_probs) + Traceback (most recent call last): + ... + ValueError: y_true must be one-hot encoded. + >>> true_labels = np.array([[1, 0, 1], [1, 0, 0]]) + >>> pred_probs = np.array([[0.9, 0.1, 0.0], [0.2, 0.7, 0.1]]) + >>> categorical_cross_entropy(true_labels, pred_probs) + Traceback (most recent call last): + ... + ValueError: y_true must be one-hot encoded. + >>> true_labels = np.array([[1, 0, 0], [0, 1, 0]]) + >>> pred_probs = np.array([[0.9, 0.1, 0.1], [0.2, 0.7, 0.1]]) + >>> categorical_cross_entropy(true_labels, pred_probs) + Traceback (most recent call last): + ... + ValueError: Predicted probabilities must sum to approximately 1. + """ + if y_true.shape != y_pred.shape: + raise ValueError("Input arrays must have the same shape.") + + if np.any((y_true != 0) & (y_true != 1)) or np.any(y_true.sum(axis=1) != 1): + raise ValueError("y_true must be one-hot encoded.") + + if not np.all(np.isclose(np.sum(y_pred, axis=1), 1, rtol=epsilon, atol=epsilon)): + raise ValueError("Predicted probabilities must sum to approximately 1.") + + y_pred = np.clip(y_pred, epsilon, 1) # Clip predictions to avoid log(0) + return -np.sum(y_true * np.log(y_pred)) + + +def hinge_loss(y_true: np.ndarray, y_pred: np.ndarray) -> float: + """ + Calculate the mean hinge loss for between true labels and predicted probabilities + for training support vector machines (SVMs). + + Hinge loss = max(0, 1 - true * pred) + + Reference: https://en.wikipedia.org/wiki/Hinge_loss + + Args: + - y_true: actual values (ground truth) encoded as -1 or 1 + - y_pred: predicted values + + >>> true_labels = np.array([-1, 1, 1, -1, 1]) + >>> pred = np.array([-4, -0.3, 0.7, 5, 10]) + >>> hinge_loss(true_labels, pred) + 1.52 + >>> true_labels = np.array([-1, 1, 1, -1, 1, 1]) + >>> pred = np.array([-4, -0.3, 0.7, 5, 10]) + >>> hinge_loss(true_labels, pred) + Traceback (most recent call last): + ... + ValueError: Length of predicted and actual array must be same. + >>> true_labels = np.array([-1, 1, 10, -1, 1]) + >>> pred = np.array([-4, -0.3, 0.7, 5, 10]) + >>> hinge_loss(true_labels, pred) + Traceback (most recent call last): + ... + ValueError: y_true can have values -1 or 1 only. + """ + if len(y_true) != len(y_pred): + raise ValueError("Length of predicted and actual array must be same.") + + if np.any((y_true != -1) & (y_true != 1)): + raise ValueError("y_true can have values -1 or 1 only.") + + hinge_losses = np.maximum(0, 1.0 - (y_true * y_pred)) + return np.mean(hinge_losses) + + +def huber_loss(y_true: np.ndarray, y_pred: np.ndarray, delta: float) -> float: + """ + Calculate the mean Huber loss between the given ground truth and predicted values. + + The Huber loss describes the penalty incurred by an estimation procedure, and it + serves as a measure of accuracy for regression models. + + Huber loss = + 0.5 * (y_true - y_pred)^2 if |y_true - y_pred| <= delta + delta * |y_true - y_pred| - 0.5 * delta^2 otherwise + + Reference: https://en.wikipedia.org/wiki/Huber_loss + + Parameters: + - y_true: The true values (ground truth) + - y_pred: The predicted values + + >>> true_values = np.array([0.9, 10.0, 2.0, 1.0, 5.2]) + >>> predicted_values = np.array([0.8, 2.1, 2.9, 4.2, 5.2]) + >>> np.isclose(huber_loss(true_values, predicted_values, 1.0), 2.102) + True + >>> true_labels = np.array([11.0, 21.0, 3.32, 4.0, 5.0]) + >>> predicted_probs = np.array([8.3, 20.8, 2.9, 11.2, 5.0]) + >>> np.isclose(huber_loss(true_labels, predicted_probs, 1.0), 1.80164) + True + >>> true_labels = np.array([11.0, 21.0, 3.32, 4.0]) + >>> predicted_probs = np.array([8.3, 20.8, 2.9, 11.2, 5.0]) + >>> huber_loss(true_labels, predicted_probs, 1.0) + Traceback (most recent call last): + ... + ValueError: Input arrays must have the same length. + """ + if len(y_true) != len(y_pred): + raise ValueError("Input arrays must have the same length.") + + huber_mse = 0.5 * (y_true - y_pred) ** 2 + huber_mae = delta * (np.abs(y_true - y_pred) - 0.5 * delta) + return np.where(np.abs(y_true - y_pred) <= delta, huber_mse, huber_mae).mean() + + +def mean_squared_error(y_true: np.ndarray, y_pred: np.ndarray) -> float: + """ + Calculate the mean squared error (MSE) between ground truth and predicted values. + + MSE measures the squared difference between true values and predicted values, and it + serves as a measure of accuracy for regression models. + + MSE = (1/n) * Σ(y_true - y_pred)^2 + + Reference: https://en.wikipedia.org/wiki/Mean_squared_error + + Parameters: + - y_true: The true values (ground truth) + - y_pred: The predicted values + + >>> true_values = np.array([1.0, 2.0, 3.0, 4.0, 5.0]) + >>> predicted_values = np.array([0.8, 2.1, 2.9, 4.2, 5.2]) + >>> np.isclose(mean_squared_error(true_values, predicted_values), 0.028) + True + >>> true_labels = np.array([1.0, 2.0, 3.0, 4.0, 5.0]) + >>> predicted_probs = np.array([0.3, 0.8, 0.9, 0.2]) + >>> mean_squared_error(true_labels, predicted_probs) + Traceback (most recent call last): + ... + ValueError: Input arrays must have the same length. + """ + if len(y_true) != len(y_pred): + raise ValueError("Input arrays must have the same length.") + + squared_errors = (y_true - y_pred) ** 2 + return np.mean(squared_errors) + + +def mean_squared_logarithmic_error(y_true: np.ndarray, y_pred: np.ndarray) -> float: + """ + Calculate the mean squared logarithmic error (MSLE) between ground truth and + predicted values. + + MSLE measures the squared logarithmic difference between true values and predicted + values for regression models. It's particularly useful for dealing with skewed or + large-value data, and it's often used when the relative differences between + predicted and true values are more important than absolute differences. + + MSLE = (1/n) * Σ(log(1 + y_true) - log(1 + y_pred))^2 + + Reference: https://insideaiml.com/blog/MeanSquared-Logarithmic-Error-Loss-1035 + + Parameters: + - y_true: The true values (ground truth) + - y_pred: The predicted values + + >>> true_values = np.array([1.0, 2.0, 3.0, 4.0, 5.0]) + >>> predicted_values = np.array([0.8, 2.1, 2.9, 4.2, 5.2]) + >>> mean_squared_logarithmic_error(true_values, predicted_values) + 0.0030860877925181344 + >>> true_labels = np.array([1.0, 2.0, 3.0, 4.0, 5.0]) + >>> predicted_probs = np.array([0.3, 0.8, 0.9, 0.2]) + >>> mean_squared_logarithmic_error(true_labels, predicted_probs) + Traceback (most recent call last): + ... + ValueError: Input arrays must have the same length. + """ + if len(y_true) != len(y_pred): + raise ValueError("Input arrays must have the same length.") + + squared_logarithmic_errors = (np.log1p(y_true) - np.log1p(y_pred)) ** 2 + return np.mean(squared_logarithmic_errors) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() diff --git a/machine_learning/loss_functions/binary_cross_entropy.py b/machine_learning/loss_functions/binary_cross_entropy.py deleted file mode 100644 index 4ebca7f21757..000000000000 --- a/machine_learning/loss_functions/binary_cross_entropy.py +++ /dev/null @@ -1,59 +0,0 @@ -""" -Binary Cross-Entropy (BCE) Loss Function - -Description: -Quantifies dissimilarity between true labels (0 or 1) and predicted probabilities. -It's widely used in binary classification tasks. - -Formula: -BCE = -Σ(y_true * log(y_pred) + (1 - y_true) * log(1 - y_pred)) - -Source: -[Wikipedia - Cross entropy](https://en.wikipedia.org/wiki/Cross_entropy) -""" - -import numpy as np - - -def binary_cross_entropy( - y_true: np.ndarray, y_pred: np.ndarray, epsilon: float = 1e-15 -) -> float: - """ - Calculate the BCE Loss between true labels and predicted probabilities. - - Parameters: - - y_true: True binary labels (0 or 1). - - y_pred: Predicted probabilities for class 1. - - epsilon: Small constant to avoid numerical instability. - - Returns: - - bce_loss: Binary Cross-Entropy Loss. - - Example Usage: - >>> true_labels = np.array([0, 1, 1, 0, 1]) - >>> predicted_probs = np.array([0.2, 0.7, 0.9, 0.3, 0.8]) - >>> binary_cross_entropy(true_labels, predicted_probs) - 0.2529995012327421 - >>> true_labels = np.array([0, 1, 1, 0, 1]) - >>> predicted_probs = np.array([0.3, 0.8, 0.9, 0.2]) - >>> binary_cross_entropy(true_labels, predicted_probs) - Traceback (most recent call last): - ... - ValueError: Input arrays must have the same length. - """ - if len(y_true) != len(y_pred): - raise ValueError("Input arrays must have the same length.") - # Clip predicted probabilities to avoid log(0) and log(1) - y_pred = np.clip(y_pred, epsilon, 1 - epsilon) - - # Calculate binary cross-entropy loss - bce_loss = -(y_true * np.log(y_pred) + (1 - y_true) * np.log(1 - y_pred)) - - # Take the mean over all samples - return np.mean(bce_loss) - - -if __name__ == "__main__": - import doctest - - doctest.testmod() diff --git a/machine_learning/loss_functions/categorical_cross_entropy.py b/machine_learning/loss_functions/categorical_cross_entropy.py deleted file mode 100644 index 68f98902b473..000000000000 --- a/machine_learning/loss_functions/categorical_cross_entropy.py +++ /dev/null @@ -1,85 +0,0 @@ -""" -Categorical Cross-Entropy Loss - -This function calculates the Categorical Cross-Entropy Loss between true class -labels and predicted class probabilities. - -Formula: -Categorical Cross-Entropy Loss = -Σ(y_true * ln(y_pred)) - -Resources: -- [Wikipedia - Cross entropy](https://en.wikipedia.org/wiki/Cross_entropy) -""" - -import numpy as np - - -def categorical_cross_entropy( - y_true: np.ndarray, y_pred: np.ndarray, epsilon: float = 1e-15 -) -> float: - """ - Calculate Categorical Cross-Entropy Loss between true class labels and - predicted class probabilities. - - Parameters: - - y_true: True class labels (one-hot encoded) as a NumPy array. - - y_pred: Predicted class probabilities as a NumPy array. - - epsilon: Small constant to avoid numerical instability. - - Returns: - - ce_loss: Categorical Cross-Entropy Loss as a floating-point number. - - Example: - >>> true_labels = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]]) - >>> pred_probs = np.array([[0.9, 0.1, 0.0], [0.2, 0.7, 0.1], [0.0, 0.1, 0.9]]) - >>> categorical_cross_entropy(true_labels, pred_probs) - 0.567395975254385 - - >>> y_true = np.array([[1, 0], [0, 1]]) - >>> y_pred = np.array([[0.9, 0.1, 0.0], [0.2, 0.7, 0.1]]) - >>> categorical_cross_entropy(y_true, y_pred) - Traceback (most recent call last): - ... - ValueError: Input arrays must have the same shape. - - >>> y_true = np.array([[2, 0, 1], [1, 0, 0]]) - >>> y_pred = np.array([[0.9, 0.1, 0.0], [0.2, 0.7, 0.1]]) - >>> categorical_cross_entropy(y_true, y_pred) - Traceback (most recent call last): - ... - ValueError: y_true must be one-hot encoded. - - >>> y_true = np.array([[1, 0, 1], [1, 0, 0]]) - >>> y_pred = np.array([[0.9, 0.1, 0.0], [0.2, 0.7, 0.1]]) - >>> categorical_cross_entropy(y_true, y_pred) - Traceback (most recent call last): - ... - ValueError: y_true must be one-hot encoded. - - >>> y_true = np.array([[1, 0, 0], [0, 1, 0]]) - >>> y_pred = np.array([[0.9, 0.1, 0.1], [0.2, 0.7, 0.1]]) - >>> categorical_cross_entropy(y_true, y_pred) - Traceback (most recent call last): - ... - ValueError: Predicted probabilities must sum to approximately 1. - """ - if y_true.shape != y_pred.shape: - raise ValueError("Input arrays must have the same shape.") - - if np.any((y_true != 0) & (y_true != 1)) or np.any(y_true.sum(axis=1) != 1): - raise ValueError("y_true must be one-hot encoded.") - - if not np.all(np.isclose(np.sum(y_pred, axis=1), 1, rtol=epsilon, atol=epsilon)): - raise ValueError("Predicted probabilities must sum to approximately 1.") - - # Clip predicted probabilities to avoid log(0) - y_pred = np.clip(y_pred, epsilon, 1) - - # Calculate categorical cross-entropy loss - return -np.sum(y_true * np.log(y_pred)) - - -if __name__ == "__main__": - import doctest - - doctest.testmod() diff --git a/machine_learning/loss_functions/hinge_loss.py b/machine_learning/loss_functions/hinge_loss.py deleted file mode 100644 index 5480a8cd62ee..000000000000 --- a/machine_learning/loss_functions/hinge_loss.py +++ /dev/null @@ -1,64 +0,0 @@ -""" -Hinge Loss - -Description: -Compute the Hinge loss used for training SVM (Support Vector Machine). - -Formula: -loss = max(0, 1 - true * pred) - -Reference: https://en.wikipedia.org/wiki/Hinge_loss - -Author: Poojan Smart -Email: smrtpoojan@gmail.com -""" - -import numpy as np - - -def hinge_loss(y_true: np.ndarray, y_pred: np.ndarray) -> float: - """ - Calculate the mean hinge loss for y_true and y_pred for binary classification. - - Args: - y_true: Array of actual values (ground truth) encoded as -1 and 1. - y_pred: Array of predicted values. - - Returns: - The hinge loss between y_true and y_pred. - - Examples: - >>> y_true = np.array([-1, 1, 1, -1, 1]) - >>> pred = np.array([-4, -0.3, 0.7, 5, 10]) - >>> hinge_loss(y_true, pred) - 1.52 - >>> y_true = np.array([-1, 1, 1, -1, 1, 1]) - >>> pred = np.array([-4, -0.3, 0.7, 5, 10]) - >>> hinge_loss(y_true, pred) - Traceback (most recent call last): - ... - ValueError: Length of predicted and actual array must be same. - >>> y_true = np.array([-1, 1, 10, -1, 1]) - >>> pred = np.array([-4, -0.3, 0.7, 5, 10]) - >>> hinge_loss(y_true, pred) - Traceback (most recent call last): - ... - ValueError: y_true can have values -1 or 1 only. - """ - - if len(y_true) != len(y_pred): - raise ValueError("Length of predicted and actual array must be same.") - - # Raise value error when y_true (encoded labels) have any other values - # than -1 and 1 - if np.any((y_true != -1) & (y_true != 1)): - raise ValueError("y_true can have values -1 or 1 only.") - - hinge_losses = np.maximum(0, 1.0 - (y_true * y_pred)) - return np.mean(hinge_losses) - - -if __name__ == "__main__": - import doctest - - doctest.testmod() diff --git a/machine_learning/loss_functions/huber_loss.py b/machine_learning/loss_functions/huber_loss.py deleted file mode 100644 index 202e013f2928..000000000000 --- a/machine_learning/loss_functions/huber_loss.py +++ /dev/null @@ -1,52 +0,0 @@ -""" -Huber Loss Function - -Description: -Huber loss function describes the penalty incurred by an estimation procedure. -It serves as a measure of the model's accuracy in regression tasks. - -Formula: -Huber Loss = if |y_true - y_pred| <= delta then 0.5 * (y_true - y_pred)^2 - else delta * |y_true - y_pred| - 0.5 * delta^2 - -Source: -[Wikipedia - Huber Loss](https://en.wikipedia.org/wiki/Huber_loss) -""" - -import numpy as np - - -def huber_loss(y_true: np.ndarray, y_pred: np.ndarray, delta: float) -> float: - """ - Calculate the mean of Huber Loss. - - Parameters: - - y_true: The true values (ground truth). - - y_pred: The predicted values. - - Returns: - - huber_loss: The mean of Huber Loss between y_true and y_pred. - - Example usage: - >>> true_values = np.array([0.9, 10.0, 2.0, 1.0, 5.2]) - >>> predicted_values = np.array([0.8, 2.1, 2.9, 4.2, 5.2]) - >>> np.isclose(huber_loss(true_values, predicted_values, 1.0), 2.102) - True - >>> true_labels = np.array([11.0, 21.0, 3.32, 4.0, 5.0]) - >>> predicted_probs = np.array([8.3, 20.8, 2.9, 11.2, 5.0]) - >>> np.isclose(huber_loss(true_labels, predicted_probs, 1.0), 1.80164) - True - """ - - if len(y_true) != len(y_pred): - raise ValueError("Input arrays must have the same length.") - - huber_mse = 0.5 * (y_true - y_pred) ** 2 - huber_mae = delta * (np.abs(y_true - y_pred) - 0.5 * delta) - return np.where(np.abs(y_true - y_pred) <= delta, huber_mse, huber_mae).mean() - - -if __name__ == "__main__": - import doctest - - doctest.testmod() diff --git a/machine_learning/loss_functions/mean_squared_error.py b/machine_learning/loss_functions/mean_squared_error.py deleted file mode 100644 index d2b0e1e158ba..000000000000 --- a/machine_learning/loss_functions/mean_squared_error.py +++ /dev/null @@ -1,51 +0,0 @@ -""" -Mean Squared Error (MSE) Loss Function - -Description: -MSE measures the mean squared difference between true values and predicted values. -It serves as a measure of the model's accuracy in regression tasks. - -Formula: -MSE = (1/n) * Σ(y_true - y_pred)^2 - -Source: -[Wikipedia - Mean squared error](https://en.wikipedia.org/wiki/Mean_squared_error) -""" - -import numpy as np - - -def mean_squared_error(y_true: np.ndarray, y_pred: np.ndarray) -> float: - """ - Calculate the Mean Squared Error (MSE) between two arrays. - - Parameters: - - y_true: The true values (ground truth). - - y_pred: The predicted values. - - Returns: - - mse: The Mean Squared Error between y_true and y_pred. - - Example usage: - >>> true_values = np.array([1.0, 2.0, 3.0, 4.0, 5.0]) - >>> predicted_values = np.array([0.8, 2.1, 2.9, 4.2, 5.2]) - >>> mean_squared_error(true_values, predicted_values) - 0.028000000000000032 - >>> true_labels = np.array([1.0, 2.0, 3.0, 4.0, 5.0]) - >>> predicted_probs = np.array([0.3, 0.8, 0.9, 0.2]) - >>> mean_squared_error(true_labels, predicted_probs) - Traceback (most recent call last): - ... - ValueError: Input arrays must have the same length. - """ - if len(y_true) != len(y_pred): - raise ValueError("Input arrays must have the same length.") - - squared_errors = (y_true - y_pred) ** 2 - return np.mean(squared_errors) - - -if __name__ == "__main__": - import doctest - - doctest.testmod() diff --git a/machine_learning/loss_functions/mean_squared_logarithmic_error.py b/machine_learning/loss_functions/mean_squared_logarithmic_error.py deleted file mode 100644 index 935ebff37a51..000000000000 --- a/machine_learning/loss_functions/mean_squared_logarithmic_error.py +++ /dev/null @@ -1,55 +0,0 @@ -""" -Mean Squared Logarithmic Error (MSLE) Loss Function - -Description: -MSLE measures the mean squared logarithmic difference between -true values and predicted values, particularly useful when -dealing with regression problems involving skewed or large-value -targets. It is often used when the relative differences between -predicted and true values are more important than absolute -differences. - -Formula: -MSLE = (1/n) * Σ(log(1 + y_true) - log(1 + y_pred))^2 - -Source: -(https://insideaiml.com/blog/MeanSquared-Logarithmic-Error-Loss-1035) -""" - -import numpy as np - - -def mean_squared_logarithmic_error(y_true: np.ndarray, y_pred: np.ndarray) -> float: - """ - Calculate the Mean Squared Logarithmic Error (MSLE) between two arrays. - - Parameters: - - y_true: The true values (ground truth). - - y_pred: The predicted values. - - Returns: - - msle: The Mean Squared Logarithmic Error between y_true and y_pred. - - Example usage: - >>> true_values = np.array([1.0, 2.0, 3.0, 4.0, 5.0]) - >>> predicted_values = np.array([0.8, 2.1, 2.9, 4.2, 5.2]) - >>> mean_squared_logarithmic_error(true_values, predicted_values) - 0.0030860877925181344 - >>> true_labels = np.array([1.0, 2.0, 3.0, 4.0, 5.0]) - >>> predicted_probs = np.array([0.3, 0.8, 0.9, 0.2]) - >>> mean_squared_logarithmic_error(true_labels, predicted_probs) - Traceback (most recent call last): - ... - ValueError: Input arrays must have the same length. - """ - if len(y_true) != len(y_pred): - raise ValueError("Input arrays must have the same length.") - - squared_logarithmic_errors = (np.log1p(y_true) - np.log1p(y_pred)) ** 2 - return np.mean(squared_logarithmic_errors) - - -if __name__ == "__main__": - import doctest - - doctest.testmod() From 47c19d9b2da6a56f47b520e6c5ca6b654a5eff47 Mon Sep 17 00:00:00 2001 From: Jeel Gajera <83470656+JeelGajera@users.noreply.github.com> Date: Sat, 21 Oct 2023 20:21:29 +0530 Subject: [PATCH 427/757] Add: FP Growth Algorithm (#10746) * Add: FP Growth Algorithm * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * changes names * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Revert "changes names" This reverts commit c0470094d01391294617df6a92734b78b470b127. * refactore code * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update frequent_pattern_growth.py --------- Co-authored-by: Jeel Gajera Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- DIRECTORY.md | 1 + machine_learning/frequent_pattern_growth.py | 349 ++++++++++++++++++++ 2 files changed, 350 insertions(+) create mode 100644 machine_learning/frequent_pattern_growth.py diff --git a/DIRECTORY.md b/DIRECTORY.md index b92f8f877e97..916d993c563a 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -541,6 +541,7 @@ * [Dimensionality Reduction](machine_learning/dimensionality_reduction.py) * Forecasting * [Run](machine_learning/forecasting/run.py) + * [Frequent Pattern Growth Algorithm](machine_learning/frequent_pattern_growth.py) * [Gradient Descent](machine_learning/gradient_descent.py) * [K Means Clust](machine_learning/k_means_clust.py) * [K Nearest Neighbours](machine_learning/k_nearest_neighbours.py) diff --git a/machine_learning/frequent_pattern_growth.py b/machine_learning/frequent_pattern_growth.py new file mode 100644 index 000000000000..205d598464a1 --- /dev/null +++ b/machine_learning/frequent_pattern_growth.py @@ -0,0 +1,349 @@ +""" +The Frequent Pattern Growth algorithm (FP-Growth) is a widely used data mining +technique for discovering frequent itemsets in large transaction databases. + +It overcomes some of the limitations of traditional methods such as Apriori by +efficiently constructing the FP-Tree + +WIKI: https://athena.ecs.csus.edu/~mei/associationcw/FpGrowth.html + +Examples: https://www.javatpoint.com/fp-growth-algorithm-in-data-mining +""" +from __future__ import annotations + +from dataclasses import dataclass, field + + +@dataclass +class TreeNode: + """ + A node in a Frequent Pattern tree. + + Args: + name: The name of this node. + num_occur: The number of occurrences of the node. + parent_node: The parent node. + + Example: + >>> parent = TreeNode("Parent", 1, None) + >>> child = TreeNode("Child", 2, parent) + >>> child.name + 'Child' + >>> child.count + 2 + """ + + name: str + count: int + parent: TreeNode | None = None + children: dict[str, TreeNode] = field(default_factory=dict) + node_link: TreeNode | None = None + + def __repr__(self) -> str: + return f"TreeNode({self.name!r}, {self.count!r}, {self.parent!r})" + + def inc(self, num_occur: int) -> None: + self.count += num_occur + + def disp(self, ind: int = 1) -> None: + print(f"{' ' * ind} {self.name} {self.count}") + for child in self.children.values(): + child.disp(ind + 1) + + +def create_tree(data_set: list, min_sup: int = 1) -> tuple[TreeNode, dict]: + """ + Create Frequent Pattern tree + + Args: + data_set: A list of transactions, where each transaction is a list of items. + min_sup: The minimum support threshold. + Items with support less than this will be pruned. Default is 1. + + Returns: + The root of the FP-Tree. + header_table: The header table dictionary with item information. + + Example: + >>> data_set = [ + ... ['A', 'B', 'C'], + ... ['A', 'C'], + ... ['A', 'B', 'E'], + ... ['A', 'B', 'C', 'E'], + ... ['B', 'E'] + ... ] + >>> min_sup = 2 + >>> fp_tree, header_table = create_tree(data_set, min_sup) + >>> fp_tree + TreeNode('Null Set', 1, None) + >>> len(header_table) + 4 + >>> header_table["A"] + [[4, None], TreeNode('A', 4, TreeNode('Null Set', 1, None))] + >>> header_table["E"][1] # doctest: +NORMALIZE_WHITESPACE + TreeNode('E', 1, TreeNode('B', 3, TreeNode('A', 4, TreeNode('Null Set', 1, None)))) + >>> sorted(header_table) + ['A', 'B', 'C', 'E'] + >>> fp_tree.name + 'Null Set' + >>> sorted(fp_tree.children) + ['A', 'B'] + >>> fp_tree.children['A'].name + 'A' + >>> sorted(fp_tree.children['A'].children) + ['B', 'C'] + """ + header_table: dict = {} + for trans in data_set: + for item in trans: + header_table[item] = header_table.get(item, [0, None]) + header_table[item][0] += 1 + + for k in list(header_table): + if header_table[k][0] < min_sup: + del header_table[k] + + if not (freq_item_set := set(header_table)): + return TreeNode("Null Set", 1, None), {} + + for k in header_table: + header_table[k] = [header_table[k], None] + + fp_tree = TreeNode("Null Set", 1, None) # Parent is None for the root node + for tran_set in data_set: + local_d = { + item: header_table[item][0] for item in tran_set if item in freq_item_set + } + if local_d: + sorted_items = sorted( + local_d.items(), key=lambda item_info: item_info[1], reverse=True + ) + ordered_items = [item[0] for item in sorted_items] + update_tree(ordered_items, fp_tree, header_table, 1) + + return fp_tree, header_table + + +def update_tree(items: list, in_tree: TreeNode, header_table: dict, count: int) -> None: + """ + Update the FP-Tree with a transaction. + + Args: + items: List of items in the transaction. + in_tree: The current node in the FP-Tree. + header_table: The header table dictionary with item information. + count: The count of the transaction. + + Example: + >>> data_set = [ + ... ['A', 'B', 'C'], + ... ['A', 'C'], + ... ['A', 'B', 'E'], + ... ['A', 'B', 'C', 'E'], + ... ['B', 'E'] + ... ] + >>> min_sup = 2 + >>> fp_tree, header_table = create_tree(data_set, min_sup) + >>> fp_tree + TreeNode('Null Set', 1, None) + >>> transaction = ['A', 'B', 'E'] + >>> update_tree(transaction, fp_tree, header_table, 1) + >>> fp_tree + TreeNode('Null Set', 1, None) + >>> fp_tree.children['A'].children['B'].children['E'].children + {} + >>> fp_tree.children['A'].children['B'].children['E'].count + 2 + >>> header_table['E'][1].name + 'E' + """ + if items[0] in in_tree.children: + in_tree.children[items[0]].inc(count) + else: + in_tree.children[items[0]] = TreeNode(items[0], count, in_tree) + if header_table[items[0]][1] is None: + header_table[items[0]][1] = in_tree.children[items[0]] + else: + update_header(header_table[items[0]][1], in_tree.children[items[0]]) + if len(items) > 1: + update_tree(items[1:], in_tree.children[items[0]], header_table, count) + + +def update_header(node_to_test: TreeNode, target_node: TreeNode) -> TreeNode: + """ + Update the header table with a node link. + + Args: + node_to_test: The node to be updated in the header table. + target_node: The node to link to. + + Example: + >>> data_set = [ + ... ['A', 'B', 'C'], + ... ['A', 'C'], + ... ['A', 'B', 'E'], + ... ['A', 'B', 'C', 'E'], + ... ['B', 'E'] + ... ] + >>> min_sup = 2 + >>> fp_tree, header_table = create_tree(data_set, min_sup) + >>> fp_tree + TreeNode('Null Set', 1, None) + >>> node1 = TreeNode("A", 3, None) + >>> node2 = TreeNode("B", 4, None) + >>> node1 + TreeNode('A', 3, None) + >>> node1 = update_header(node1, node2) + >>> node1 + TreeNode('A', 3, None) + >>> node1.node_link + TreeNode('B', 4, None) + >>> node2.node_link is None + True + """ + while node_to_test.node_link is not None: + node_to_test = node_to_test.node_link + if node_to_test.node_link is None: + node_to_test.node_link = target_node + # Return the updated node + return node_to_test + + +def ascend_tree(leaf_node: TreeNode, prefix_path: list[str]) -> None: + """ + Ascend the FP-Tree from a leaf node to its root, adding item names to the prefix + path. + + Args: + leaf_node: The leaf node to start ascending from. + prefix_path: A list to store the item as they are ascended. + + Example: + >>> data_set = [ + ... ['A', 'B', 'C'], + ... ['A', 'C'], + ... ['A', 'B', 'E'], + ... ['A', 'B', 'C', 'E'], + ... ['B', 'E'] + ... ] + >>> min_sup = 2 + >>> fp_tree, header_table = create_tree(data_set, min_sup) + + >>> path = [] + >>> ascend_tree(fp_tree.children['A'], path) + >>> path # ascending from a leaf node 'A' + ['A'] + """ + if leaf_node.parent is not None: + prefix_path.append(leaf_node.name) + ascend_tree(leaf_node.parent, prefix_path) + + +def find_prefix_path(base_pat: frozenset, tree_node: TreeNode | None) -> dict: + """ + Find the conditional pattern base for a given base pattern. + + Args: + base_pat: The base pattern for which to find the conditional pattern base. + tree_node: The node in the FP-Tree. + + Example: + >>> data_set = [ + ... ['A', 'B', 'C'], + ... ['A', 'C'], + ... ['A', 'B', 'E'], + ... ['A', 'B', 'C', 'E'], + ... ['B', 'E'] + ... ] + >>> min_sup = 2 + >>> fp_tree, header_table = create_tree(data_set, min_sup) + >>> fp_tree + TreeNode('Null Set', 1, None) + >>> len(header_table) + 4 + >>> base_pattern = frozenset(['A']) + >>> sorted(find_prefix_path(base_pattern, fp_tree.children['A'])) + [] + """ + cond_pats: dict = {} + while tree_node is not None: + prefix_path: list = [] + ascend_tree(tree_node, prefix_path) + if len(prefix_path) > 1: + cond_pats[frozenset(prefix_path[1:])] = tree_node.count + tree_node = tree_node.node_link + return cond_pats + + +def mine_tree( + in_tree: TreeNode, + header_table: dict, + min_sup: int, + pre_fix: set, + freq_item_list: list, +) -> None: + """ + Mine the FP-Tree recursively to discover frequent itemsets. + + Args: + in_tree: The FP-Tree to mine. + header_table: The header table dictionary with item information. + min_sup: The minimum support threshold. + pre_fix: A set of items as a prefix for the itemsets being mined. + freq_item_list: A list to store the frequent itemsets. + + Example: + >>> data_set = [ + ... ['A', 'B', 'C'], + ... ['A', 'C'], + ... ['A', 'B', 'E'], + ... ['A', 'B', 'C', 'E'], + ... ['B', 'E'] + ... ] + >>> min_sup = 2 + >>> fp_tree, header_table = create_tree(data_set, min_sup) + >>> fp_tree + TreeNode('Null Set', 1, None) + >>> frequent_itemsets = [] + >>> mine_tree(fp_tree, header_table, min_sup, set([]), frequent_itemsets) + >>> expe_itm = [{'C'}, {'C', 'A'}, {'E'}, {'A', 'E'}, {'E', 'B'}, {'A'}, {'B'}] + >>> all(expected in frequent_itemsets for expected in expe_itm) + True + """ + sorted_items = sorted(header_table.items(), key=lambda item_info: item_info[1][0]) + big_l = [item[0] for item in sorted_items] + for base_pat in big_l: + new_freq_set = pre_fix.copy() + new_freq_set.add(base_pat) + freq_item_list.append(new_freq_set) + cond_patt_bases = find_prefix_path(base_pat, header_table[base_pat][1]) + my_cond_tree, my_head = create_tree(list(cond_patt_bases), min_sup) + if my_head is not None: + # Pass header_table[base_pat][1] as node_to_test to update_header + header_table[base_pat][1] = update_header( + header_table[base_pat][1], my_cond_tree + ) + mine_tree(my_cond_tree, my_head, min_sup, new_freq_set, freq_item_list) + + +if __name__ == "__main__": + from doctest import testmod + + testmod() + data_set: list[frozenset] = [ + frozenset(["bread", "milk", "cheese"]), + frozenset(["bread", "milk"]), + frozenset(["bread", "diapers"]), + frozenset(["bread", "milk", "diapers"]), + frozenset(["milk", "diapers"]), + frozenset(["milk", "cheese"]), + frozenset(["diapers", "cheese"]), + frozenset(["bread", "milk", "cheese", "diapers"]), + ] + print(f"{len(data_set) = }") + fp_tree, header_table = create_tree(data_set, min_sup=3) + print(f"{fp_tree = }") + print(f"{len(header_table) = }") + freq_items: list = [] + mine_tree(fp_tree, header_table, 3, set(), freq_items) + print(f"{freq_items = }") From 06edc0eea0220f29491f75351cde1af9716aca8d Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Sat, 21 Oct 2023 13:27:36 -0400 Subject: [PATCH 428/757] Consolidate binary exponentiation files (#10742) * Consolidate binary exponentiation files * updating DIRECTORY.md * Fix typos in doctests * Add suggestions from code review * Fix timeit benchmarks --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 2 - maths/binary_exp_mod.py | 28 ---- maths/binary_exponentiation.py | 214 ++++++++++++++++++++++++++----- maths/binary_exponentiation_2.py | 61 --------- 4 files changed, 181 insertions(+), 124 deletions(-) delete mode 100644 maths/binary_exp_mod.py delete mode 100644 maths/binary_exponentiation_2.py diff --git a/DIRECTORY.md b/DIRECTORY.md index 916d993c563a..9e0166ad80c5 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -578,9 +578,7 @@ * [Bailey Borwein Plouffe](maths/bailey_borwein_plouffe.py) * [Base Neg2 Conversion](maths/base_neg2_conversion.py) * [Basic Maths](maths/basic_maths.py) - * [Binary Exp Mod](maths/binary_exp_mod.py) * [Binary Exponentiation](maths/binary_exponentiation.py) - * [Binary Exponentiation 2](maths/binary_exponentiation_2.py) * [Binary Multiplication](maths/binary_multiplication.py) * [Binomial Coefficient](maths/binomial_coefficient.py) * [Binomial Distribution](maths/binomial_distribution.py) diff --git a/maths/binary_exp_mod.py b/maths/binary_exp_mod.py deleted file mode 100644 index 8893182a3496..000000000000 --- a/maths/binary_exp_mod.py +++ /dev/null @@ -1,28 +0,0 @@ -def bin_exp_mod(a: int, n: int, b: int) -> int: - """ - >>> bin_exp_mod(3, 4, 5) - 1 - >>> bin_exp_mod(7, 13, 10) - 7 - """ - # mod b - assert b != 0, "This cannot accept modulo that is == 0" - if n == 0: - return 1 - - if n % 2 == 1: - return (bin_exp_mod(a, n - 1, b) * a) % b - - r = bin_exp_mod(a, n // 2, b) - return (r * r) % b - - -if __name__ == "__main__": - try: - BASE = int(input("Enter Base : ").strip()) - POWER = int(input("Enter Power : ").strip()) - MODULO = int(input("Enter Modulo : ").strip()) - except ValueError: - print("Invalid literal for integer") - - print(bin_exp_mod(BASE, POWER, MODULO)) diff --git a/maths/binary_exponentiation.py b/maths/binary_exponentiation.py index f613767f547e..51ce86d26c41 100644 --- a/maths/binary_exponentiation.py +++ b/maths/binary_exponentiation.py @@ -1,48 +1,196 @@ -"""Binary Exponentiation.""" +""" +Binary Exponentiation -# Author : Junth Basnet -# Time Complexity : O(logn) +This is a method to find a^b in O(log b) time complexity and is one of the most commonly +used methods of exponentiation. The method is also useful for modular exponentiation, +when the solution to (a^b) % c is required. +To calculate a^b: +- If b is even, then a^b = (a * a)^(b / 2) +- If b is odd, then a^b = a * a^(b - 1) +Repeat until b = 1 or b = 0 -def binary_exponentiation(a: int, n: int) -> int: +For modular exponentiation, we use the fact that (a * b) % c = ((a % c) * (b % c)) % c +""" + + +def binary_exp_recursive(base: float, exponent: int) -> float: """ - Compute a number raised by some quantity - >>> binary_exponentiation(-1, 3) + Computes a^b recursively, where a is the base and b is the exponent + + >>> binary_exp_recursive(3, 5) + 243 + >>> binary_exp_recursive(11, 13) + 34522712143931 + >>> binary_exp_recursive(-1, 3) -1 - >>> binary_exponentiation(-1, 4) + >>> binary_exp_recursive(0, 5) + 0 + >>> binary_exp_recursive(3, 1) + 3 + >>> binary_exp_recursive(3, 0) 1 - >>> binary_exponentiation(2, 2) - 4 - >>> binary_exponentiation(3, 5) + >>> binary_exp_recursive(1.5, 4) + 5.0625 + >>> binary_exp_recursive(3, -1) + Traceback (most recent call last): + ... + ValueError: Exponent must be a non-negative integer + """ + if exponent < 0: + raise ValueError("Exponent must be a non-negative integer") + + if exponent == 0: + return 1 + + if exponent % 2 == 1: + return binary_exp_recursive(base, exponent - 1) * base + + b = binary_exp_recursive(base, exponent // 2) + return b * b + + +def binary_exp_iterative(base: float, exponent: int) -> float: + """ + Computes a^b iteratively, where a is the base and b is the exponent + + >>> binary_exp_iterative(3, 5) 243 - >>> binary_exponentiation(10, 3) - 1000 - >>> binary_exponentiation(5e3, 1) - 5000.0 - >>> binary_exponentiation(-5e3, 1) - -5000.0 - """ - if n == 0: + >>> binary_exp_iterative(11, 13) + 34522712143931 + >>> binary_exp_iterative(-1, 3) + -1 + >>> binary_exp_iterative(0, 5) + 0 + >>> binary_exp_iterative(3, 1) + 3 + >>> binary_exp_iterative(3, 0) + 1 + >>> binary_exp_iterative(1.5, 4) + 5.0625 + >>> binary_exp_iterative(3, -1) + Traceback (most recent call last): + ... + ValueError: Exponent must be a non-negative integer + """ + if exponent < 0: + raise ValueError("Exponent must be a non-negative integer") + + res: int | float = 1 + while exponent > 0: + if exponent & 1: + res *= base + + base *= base + exponent >>= 1 + + return res + + +def binary_exp_mod_recursive(base: float, exponent: int, modulus: int) -> float: + """ + Computes a^b % c recursively, where a is the base, b is the exponent, and c is the + modulus + + >>> binary_exp_mod_recursive(3, 4, 5) + 1 + >>> binary_exp_mod_recursive(11, 13, 7) + 4 + >>> binary_exp_mod_recursive(1.5, 4, 3) + 2.0625 + >>> binary_exp_mod_recursive(7, -1, 10) + Traceback (most recent call last): + ... + ValueError: Exponent must be a non-negative integer + >>> binary_exp_mod_recursive(7, 13, 0) + Traceback (most recent call last): + ... + ValueError: Modulus must be a positive integer + """ + if exponent < 0: + raise ValueError("Exponent must be a non-negative integer") + if modulus <= 0: + raise ValueError("Modulus must be a positive integer") + + if exponent == 0: return 1 - elif n % 2 == 1: - return binary_exponentiation(a, n - 1) * a + if exponent % 2 == 1: + return (binary_exp_mod_recursive(base, exponent - 1, modulus) * base) % modulus - else: - b = binary_exponentiation(a, n // 2) - return b * b + r = binary_exp_mod_recursive(base, exponent // 2, modulus) + return (r * r) % modulus -if __name__ == "__main__": - import doctest +def binary_exp_mod_iterative(base: float, exponent: int, modulus: int) -> float: + """ + Computes a^b % c iteratively, where a is the base, b is the exponent, and c is the + modulus - doctest.testmod() + >>> binary_exp_mod_iterative(3, 4, 5) + 1 + >>> binary_exp_mod_iterative(11, 13, 7) + 4 + >>> binary_exp_mod_iterative(1.5, 4, 3) + 2.0625 + >>> binary_exp_mod_iterative(7, -1, 10) + Traceback (most recent call last): + ... + ValueError: Exponent must be a non-negative integer + >>> binary_exp_mod_iterative(7, 13, 0) + Traceback (most recent call last): + ... + ValueError: Modulus must be a positive integer + """ + if exponent < 0: + raise ValueError("Exponent must be a non-negative integer") + if modulus <= 0: + raise ValueError("Modulus must be a positive integer") + + res: int | float = 1 + while exponent > 0: + if exponent & 1: + res = ((res % modulus) * (base % modulus)) % modulus + + base *= base + exponent >>= 1 + + return res + + +if __name__ == "__main__": + from timeit import timeit - try: - BASE = int(float(input("Enter Base : ").strip())) - POWER = int(input("Enter Power : ").strip()) - except ValueError: - print("Invalid literal for integer") + a = 1269380576 + b = 374 + c = 34 - RESULT = binary_exponentiation(BASE, POWER) - print(f"{BASE}^({POWER}) : {RESULT}") + runs = 100_000 + print( + timeit( + f"binary_exp_recursive({a}, {b})", + setup="from __main__ import binary_exp_recursive", + number=runs, + ) + ) + print( + timeit( + f"binary_exp_iterative({a}, {b})", + setup="from __main__ import binary_exp_iterative", + number=runs, + ) + ) + print( + timeit( + f"binary_exp_mod_recursive({a}, {b}, {c})", + setup="from __main__ import binary_exp_mod_recursive", + number=runs, + ) + ) + print( + timeit( + f"binary_exp_mod_iterative({a}, {b}, {c})", + setup="from __main__ import binary_exp_mod_iterative", + number=runs, + ) + ) diff --git a/maths/binary_exponentiation_2.py b/maths/binary_exponentiation_2.py deleted file mode 100644 index edb6b66b2594..000000000000 --- a/maths/binary_exponentiation_2.py +++ /dev/null @@ -1,61 +0,0 @@ -""" -Binary Exponentiation -This is a method to find a^b in O(log b) time complexity -This is one of the most commonly used methods of exponentiation -It's also useful when the solution to (a^b) % c is required because a, b, c may be -over the computer's calculation limits - -Let's say you need to calculate a ^ b -- RULE 1 : a ^ b = (a*a) ^ (b/2) ---- example : 4 ^ 4 = (4*4) ^ (4/2) = 16 ^ 2 -- RULE 2 : IF b is odd, then a ^ b = a * (a ^ (b - 1)), where b - 1 is even -Once b is even, repeat the process until b = 1 or b = 0, because a^1 = a and a^0 = 1 - -For modular exponentiation, we use the fact that (a*b) % c = ((a%c) * (b%c)) % c -Now apply RULE 1 or 2 as required - -@author chinmoy159 -""" - - -def b_expo(a: int, b: int) -> int: - """ - >>> b_expo(2, 10) - 1024 - >>> b_expo(9, 0) - 1 - >>> b_expo(0, 12) - 0 - >>> b_expo(4, 12) - 16777216 - """ - res = 1 - while b > 0: - if b & 1: - res *= a - - a *= a - b >>= 1 - - return res - - -def b_expo_mod(a: int, b: int, c: int) -> int: - """ - >>> b_expo_mod(2, 10, 1000000007) - 1024 - >>> b_expo_mod(11, 13, 19) - 11 - >>> b_expo_mod(0, 19, 20) - 0 - >>> b_expo_mod(15, 5, 4) - 3 - """ - res = 1 - while b > 0: - if b & 1: - res = ((res % c) * (a % c)) % c - - a *= a - b >>= 1 - - return res From b814cf3781a97c273a779823b8b8ab388417b7b4 Mon Sep 17 00:00:00 2001 From: Kiarash Hajian <133909368+kiarash8112@users.noreply.github.com> Date: Sat, 21 Oct 2023 14:53:34 -0400 Subject: [PATCH 429/757] add exponential search algorithm (#10732) * add exponential_search algorithm * replace binary_search with binary_search_recursion * convert left type to int to be useable in binary_search_recursion * add docs and tests for exponential_search algorithm * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * move exponential_search to binary_search.py to pass github auto build tests delete exponential_search.py file * Update searches/binary_search.py Co-authored-by: Christian Clauss * remove additional space searches/binary_search.py Co-authored-by: Christian Clauss * return single data type in exponential_search searches/binary_search.py Co-authored-by: Christian Clauss * add doctest mod searches/binary_search.py Co-authored-by: Christian Clauss * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * use // instread of int() convert searches/binary_search.py Co-authored-by: Christian Clauss * change test according to new code searches/binary_search.py Co-authored-by: Christian Clauss * fix binary_search_recursion multiple type return error * add a timeit benchmark for exponential_search * sort input of binary search to be equal in performance test with exponential_search * raise value error instead of sorting input in binary and exonential search to fix bugs * Update binary_search.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss Co-authored-by: user --- searches/binary_search.py | 149 +++++++++++++++++++++++++------------- 1 file changed, 100 insertions(+), 49 deletions(-) diff --git a/searches/binary_search.py b/searches/binary_search.py index 05dadd4fe965..586be39c9a0d 100644 --- a/searches/binary_search.py +++ b/searches/binary_search.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 """ -This is pure Python implementation of binary search algorithms +Pure Python implementations of binary search algorithms -For doctests run following command: +For doctests run the following command: python3 -m doctest -v binary_search.py For manual testing run: @@ -34,16 +34,12 @@ def bisect_left( Examples: >>> bisect_left([0, 5, 7, 10, 15], 0) 0 - >>> bisect_left([0, 5, 7, 10, 15], 6) 2 - >>> bisect_left([0, 5, 7, 10, 15], 20) 5 - >>> bisect_left([0, 5, 7, 10, 15], 15, 1, 3) 3 - >>> bisect_left([0, 5, 7, 10, 15], 6, 2) 2 """ @@ -79,16 +75,12 @@ def bisect_right( Examples: >>> bisect_right([0, 5, 7, 10, 15], 0) 1 - >>> bisect_right([0, 5, 7, 10, 15], 15) 5 - >>> bisect_right([0, 5, 7, 10, 15], 6) 2 - >>> bisect_right([0, 5, 7, 10, 15], 15, 1, 3) 3 - >>> bisect_right([0, 5, 7, 10, 15], 6, 2) 2 """ @@ -124,7 +116,6 @@ def insort_left( >>> insort_left(sorted_collection, 6) >>> sorted_collection [0, 5, 6, 7, 10, 15] - >>> sorted_collection = [(0, 0), (5, 5), (7, 7), (10, 10), (15, 15)] >>> item = (5, 5) >>> insort_left(sorted_collection, item) @@ -134,12 +125,10 @@ def insort_left( True >>> item is sorted_collection[2] False - >>> sorted_collection = [0, 5, 7, 10, 15] >>> insort_left(sorted_collection, 20) >>> sorted_collection [0, 5, 7, 10, 15, 20] - >>> sorted_collection = [0, 5, 7, 10, 15] >>> insort_left(sorted_collection, 15, 1, 3) >>> sorted_collection @@ -167,7 +156,6 @@ def insort_right( >>> insort_right(sorted_collection, 6) >>> sorted_collection [0, 5, 6, 7, 10, 15] - >>> sorted_collection = [(0, 0), (5, 5), (7, 7), (10, 10), (15, 15)] >>> item = (5, 5) >>> insort_right(sorted_collection, item) @@ -177,12 +165,10 @@ def insort_right( False >>> item is sorted_collection[2] True - >>> sorted_collection = [0, 5, 7, 10, 15] >>> insort_right(sorted_collection, 20) >>> sorted_collection [0, 5, 7, 10, 15, 20] - >>> sorted_collection = [0, 5, 7, 10, 15] >>> insort_right(sorted_collection, 15, 1, 3) >>> sorted_collection @@ -191,29 +177,28 @@ def insort_right( sorted_collection.insert(bisect_right(sorted_collection, item, lo, hi), item) -def binary_search(sorted_collection: list[int], item: int) -> int | None: - """Pure implementation of binary search algorithm in Python +def binary_search(sorted_collection: list[int], item: int) -> int: + """Pure implementation of a binary search algorithm in Python - Be careful collection must be ascending sorted, otherwise result will be + Be careful collection must be ascending sorted otherwise, the result will be unpredictable :param sorted_collection: some ascending sorted collection with comparable items :param item: item value to search - :return: index of found item or None if item is not found + :return: index of the found item or -1 if the item is not found Examples: >>> binary_search([0, 5, 7, 10, 15], 0) 0 - >>> binary_search([0, 5, 7, 10, 15], 15) 4 - >>> binary_search([0, 5, 7, 10, 15], 5) 1 - >>> binary_search([0, 5, 7, 10, 15], 6) - + -1 """ + if list(sorted_collection) != sorted(sorted_collection): + raise ValueError("sorted_collection must be sorted in ascending order") left = 0 right = len(sorted_collection) - 1 @@ -226,66 +211,66 @@ def binary_search(sorted_collection: list[int], item: int) -> int | None: right = midpoint - 1 else: left = midpoint + 1 - return None + return -1 -def binary_search_std_lib(sorted_collection: list[int], item: int) -> int | None: - """Pure implementation of binary search algorithm in Python using stdlib +def binary_search_std_lib(sorted_collection: list[int], item: int) -> int: + """Pure implementation of a binary search algorithm in Python using stdlib - Be careful collection must be ascending sorted, otherwise result will be + Be careful collection must be ascending sorted otherwise, the result will be unpredictable :param sorted_collection: some ascending sorted collection with comparable items :param item: item value to search - :return: index of found item or None if item is not found + :return: index of the found item or -1 if the item is not found Examples: >>> binary_search_std_lib([0, 5, 7, 10, 15], 0) 0 - >>> binary_search_std_lib([0, 5, 7, 10, 15], 15) 4 - >>> binary_search_std_lib([0, 5, 7, 10, 15], 5) 1 - >>> binary_search_std_lib([0, 5, 7, 10, 15], 6) - + -1 """ + if list(sorted_collection) != sorted(sorted_collection): + raise ValueError("sorted_collection must be sorted in ascending order") index = bisect.bisect_left(sorted_collection, item) if index != len(sorted_collection) and sorted_collection[index] == item: return index - return None + return -1 def binary_search_by_recursion( - sorted_collection: list[int], item: int, left: int, right: int -) -> int | None: - """Pure implementation of binary search algorithm in Python by recursion + sorted_collection: list[int], item: int, left: int = 0, right: int = -1 +) -> int: + """Pure implementation of a binary search algorithm in Python by recursion - Be careful collection must be ascending sorted, otherwise result will be + Be careful collection must be ascending sorted otherwise, the result will be unpredictable First recursion should be started with left=0 and right=(len(sorted_collection)-1) :param sorted_collection: some ascending sorted collection with comparable items :param item: item value to search - :return: index of found item or None if item is not found + :return: index of the found item or -1 if the item is not found Examples: >>> binary_search_by_recursion([0, 5, 7, 10, 15], 0, 0, 4) 0 - >>> binary_search_by_recursion([0, 5, 7, 10, 15], 15, 0, 4) 4 - >>> binary_search_by_recursion([0, 5, 7, 10, 15], 5, 0, 4) 1 - >>> binary_search_by_recursion([0, 5, 7, 10, 15], 6, 0, 4) - + -1 """ + if right < 0: + right = len(sorted_collection) - 1 + if list(sorted_collection) != sorted(sorted_collection): + raise ValueError("sorted_collection must be sorted in ascending order") if right < left: - return None + return -1 midpoint = left + (right - left) // 2 @@ -297,12 +282,78 @@ def binary_search_by_recursion( return binary_search_by_recursion(sorted_collection, item, midpoint + 1, right) +def exponential_search(sorted_collection: list[int], item: int) -> int: + """Pure implementation of an exponential search algorithm in Python + Resources used: + https://en.wikipedia.org/wiki/Exponential_search + + Be careful collection must be ascending sorted otherwise, result will be + unpredictable + + :param sorted_collection: some ascending sorted collection with comparable items + :param item: item value to search + :return: index of the found item or -1 if the item is not found + + the order of this algorithm is O(lg I) where I is index position of item if exist + + Examples: + >>> exponential_search([0, 5, 7, 10, 15], 0) + 0 + >>> exponential_search([0, 5, 7, 10, 15], 15) + 4 + >>> exponential_search([0, 5, 7, 10, 15], 5) + 1 + >>> exponential_search([0, 5, 7, 10, 15], 6) + -1 + """ + if list(sorted_collection) != sorted(sorted_collection): + raise ValueError("sorted_collection must be sorted in ascending order") + bound = 1 + while bound < len(sorted_collection) and sorted_collection[bound] < item: + bound *= 2 + left = bound // 2 + right = min(bound, len(sorted_collection) - 1) + last_result = binary_search_by_recursion( + sorted_collection=sorted_collection, item=item, left=left, right=right + ) + if last_result is None: + return -1 + return last_result + + +searches = ( # Fastest to slowest... + binary_search_std_lib, + binary_search, + exponential_search, + binary_search_by_recursion, +) + + if __name__ == "__main__": - user_input = input("Enter numbers separated by comma:\n").strip() + import doctest + import timeit + + doctest.testmod() + for search in searches: + name = f"{search.__name__:>26}" + print(f"{name}: {search([0, 5, 7, 10, 15], 10) = }") # type: ignore[operator] + + print("\nBenchmarks...") + setup = "collection = range(1000)" + for search in searches: + name = search.__name__ + print( + f"{name:>26}:", + timeit.timeit( + f"{name}(collection, 500)", setup=setup, number=5_000, globals=globals() + ), + ) + + user_input = input("\nEnter numbers separated by comma: ").strip() collection = sorted(int(item) for item in user_input.split(",")) - target = int(input("Enter a single number to be found in the list:\n")) - result = binary_search(collection, target) - if result is None: + target = int(input("Enter a single number to be found in the list: ")) + result = binary_search(sorted_collection=collection, item=target) + if result == -1: print(f"{target} was not found in {collection}.") else: - print(f"{target} was found at position {result} in {collection}.") + print(f"{target} was found at position {result} of {collection}.") From 4707fdb0f27bdc1e7442ce5940da335d58885104 Mon Sep 17 00:00:00 2001 From: Saptadeep Banerjee <69459134+imSanko@users.noreply.github.com> Date: Sun, 22 Oct 2023 03:35:37 +0530 Subject: [PATCH 430/757] Add tests for Perfect_Number (#10745) * Added new tests! * [ADD]: Inproved Tests * fixed * Removed spaces * Changed the file name * Added Changes * changed the code and kept the test cases * changed the code and kept the test cases * missed the line * removed spaces * Update power_using_recursion.py * Added new tests in Signum * Few things added * Removed few stuff and added few changes * Fixed few things * Reverted the function * Update maths/signum.py Co-authored-by: Christian Clauss * Added few things * Update maths/signum.py Co-authored-by: Christian Clauss * Added the type hint back * Update signum.py * Added NEW tests for Perfect_Number * Update maths/special_numbers/perfect_number.py Co-authored-by: Christian Clauss * Added the line back * Update maths/special_numbers/perfect_number.py Co-authored-by: Christian Clauss * Fixed a space * Updated * Reverted changes * Added the old code and FIXED few LINES * Fixed few things * Changed Test CASES * Update perfect_number.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: Christian Clauss Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- maths/perfect_number.py | 77 +++++++++++++++++++++++++ maths/special_numbers/perfect_number.py | 29 ++++++++-- 2 files changed, 102 insertions(+), 4 deletions(-) create mode 100644 maths/perfect_number.py diff --git a/maths/perfect_number.py b/maths/perfect_number.py new file mode 100644 index 000000000000..df6b6e3d91d8 --- /dev/null +++ b/maths/perfect_number.py @@ -0,0 +1,77 @@ +""" +== Perfect Number == +In number theory, a perfect number is a positive integer that is equal to the sum of +its positive divisors, excluding the number itself. +For example: 6 ==> divisors[1, 2, 3, 6] + Excluding 6, the sum(divisors) is 1 + 2 + 3 = 6 + So, 6 is a Perfect Number + +Other examples of Perfect Numbers: 28, 486, ... + +https://en.wikipedia.org/wiki/Perfect_number +""" + + +def perfect(number: int) -> bool: + """ + Check if a number is a perfect number. + + A perfect number is a positive integer that is equal to the sum of its proper + divisors (excluding itself). + + Args: + number: The number to be checked. + + Returns: + True if the number is a perfect number otherwise, False. + Start from 1 because dividing by 0 will raise ZeroDivisionError. + A number at most can be divisible by the half of the number except the number + itself. For example, 6 is at most can be divisible by 3 except by 6 itself. + Examples: + >>> perfect(27) + False + >>> perfect(28) + True + >>> perfect(29) + False + >>> perfect(6) + True + >>> perfect(12) + False + >>> perfect(496) + True + >>> perfect(8128) + True + >>> perfect(0) + False + >>> perfect(-1) + False + >>> perfect(12.34) + Traceback (most recent call last): + ... + ValueError: number must an integer + >>> perfect("Hello") + Traceback (most recent call last): + ... + ValueError: number must an integer + """ + if not isinstance(number, int): + raise ValueError("number must an integer") + if number <= 0: + return False + return sum(i for i in range(1, number // 2 + 1) if number % i == 0) == number + + +if __name__ == "__main__": + from doctest import testmod + + testmod() + print("Program to check whether a number is a Perfect number or not...") + try: + number = int(input("Enter a positive integer: ").strip()) + except ValueError: + msg = "number must an integer" + print(msg) + raise ValueError(msg) + + print(f"{number} is {'' if perfect(number) else 'not '}a Perfect Number.") diff --git a/maths/special_numbers/perfect_number.py b/maths/special_numbers/perfect_number.py index 148e988fb4c5..160ab2d967ad 100644 --- a/maths/special_numbers/perfect_number.py +++ b/maths/special_numbers/perfect_number.py @@ -14,16 +14,37 @@ def perfect(number: int) -> bool: """ + Check if a number is a perfect number. + + A perfect number is a positive integer that is equal to the sum of its proper + divisors (excluding itself). + + Args: + number: The number to be checked. + + Returns: + True if the number is a perfect number, False otherwise. + + Examples: >>> perfect(27) False >>> perfect(28) True >>> perfect(29) False - - Start from 1 because dividing by 0 will raise ZeroDivisionError. - A number at most can be divisible by the half of the number except the number - itself. For example, 6 is at most can be divisible by 3 except by 6 itself. + >>> perfect(6) + True + >>> perfect(12) + False + >>> perfect(496) + True + >>> perfect(8128) + True + >>> perfect(0) + >>> perfect(-3) + >>> perfect(12.34) + >>> perfect("day") + >>> perfect(["call"]) """ return sum(i for i in range(1, number // 2 + 1) if number % i == 0) == number From d9562033f6b15c17e0b48181c087731751abd7a6 Mon Sep 17 00:00:00 2001 From: Barun Parua <76466796+Baron105@users.noreply.github.com> Date: Sun, 22 Oct 2023 04:03:50 +0530 Subject: [PATCH 431/757] added a function to calculate perceived frequency by observer using Doppler Effect (#10776) * avg and mps speed formulae added * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * avg and mps speed formulae added * fixed_spacing * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * spacing * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * ws * added amicable numbers * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * added amicable numbers * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * spacing * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * removed * changed name of file and added code improvements * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * issues fixed due to pi * requested changes added * Created doppler_effect_of_sound.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Updated doppler_effect_of_sound.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * added desc names * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fixed spacing * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * renamed doppler_effect_of_sound.py to doppler_frequency.py * used expection handling rather than print statements * fixed spacing for ruff * Update doppler_frequency.py This is super slick! Well done. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- maths/special_numbers/perfect_number.py | 32 +++++- .../{perceptron.py => perceptron.py.DISABLED} | 0 physics/doppler_frequency.py | 104 ++++++++++++++++++ 3 files changed, 132 insertions(+), 4 deletions(-) rename neural_network/{perceptron.py => perceptron.py.DISABLED} (100%) create mode 100644 physics/doppler_frequency.py diff --git a/maths/special_numbers/perfect_number.py b/maths/special_numbers/perfect_number.py index 160ab2d967ad..a022dc677638 100644 --- a/maths/special_numbers/perfect_number.py +++ b/maths/special_numbers/perfect_number.py @@ -25,6 +25,10 @@ def perfect(number: int) -> bool: Returns: True if the number is a perfect number, False otherwise. + Start from 1 because dividing by 0 will raise ZeroDivisionError. + A number at most can be divisible by the half of the number except the number + itself. For example, 6 is at most can be divisible by 3 except by 6 itself. + Examples: >>> perfect(27) False @@ -41,15 +45,35 @@ def perfect(number: int) -> bool: >>> perfect(8128) True >>> perfect(0) - >>> perfect(-3) + False + >>> perfect(-1) + False >>> perfect(12.34) - >>> perfect("day") - >>> perfect(["call"]) + Traceback (most recent call last): + ... + ValueError: number must be an integer + >>> perfect("Hello") + Traceback (most recent call last): + ... + ValueError: number must be an integer """ + if not isinstance(number, int): + raise ValueError("number must be an integer") + if number <= 0: + return False return sum(i for i in range(1, number // 2 + 1) if number % i == 0) == number if __name__ == "__main__": + from doctest import testmod + + testmod() print("Program to check whether a number is a Perfect number or not...") - number = int(input("Enter number: ").strip()) + try: + number = int(input("Enter a positive integer: ").strip()) + except ValueError: + msg = "number must be an integer" + print(msg) + raise ValueError(msg) + print(f"{number} is {'' if perfect(number) else 'not '}a Perfect Number.") diff --git a/neural_network/perceptron.py b/neural_network/perceptron.py.DISABLED similarity index 100% rename from neural_network/perceptron.py rename to neural_network/perceptron.py.DISABLED diff --git a/physics/doppler_frequency.py b/physics/doppler_frequency.py new file mode 100644 index 000000000000..2a761c72d9b8 --- /dev/null +++ b/physics/doppler_frequency.py @@ -0,0 +1,104 @@ +""" +Doppler's effect + +The Doppler effect (also Doppler shift) is the change in the frequency of a wave in +relation to an observer who is moving relative to the source of the wave. The Doppler +effect is named after the physicist Christian Doppler. A common example of Doppler +shift is the change of pitch heard when a vehicle sounding a horn approaches and +recedes from an observer. + +The reason for the Doppler effect is that when the source of the waves is moving +towards the observer, each successive wave crest is emitted from a position closer to +the observer than the crest of the previous wave. Therefore, each wave takes slightly +less time to reach the observer than the previous wave. Hence, the time between the +arrivals of successive wave crests at the observer is reduced, causing an increase in +the frequency. Similarly, if the source of waves is moving away from the observer, +each wave is emitted from a position farther from the observer than the previous wave, +so the arrival time between successive waves is increased, reducing the frequency. + +If the source of waves is stationary but the observer is moving with respect to the +source, the transmission velocity of the waves changes (ie the rate at which the +observer receives waves) even if the wavelength and frequency emitted from the source +remain constant. + +These results are all summarized by the Doppler formula: + + f = (f0 * (v + v0)) / (v - vs) + +where: + f: frequency of the wave + f0: frequency of the wave when the source is stationary + v: velocity of the wave in the medium + v0: velocity of the observer, positive if the observer is moving towards the source + vs: velocity of the source, positive if the source is moving towards the observer + +Doppler's effect has many applications in physics and engineering, such as radar, +astronomy, medical imaging, and seismology. + +References: +https://en.wikipedia.org/wiki/Doppler_effect + +Now, we will implement a function that calculates the frequency of a wave as a function +of the frequency of the wave when the source is stationary, the velocity of the wave +in the medium, the velocity of the observer and the velocity of the source. +""" + + +def doppler_effect( + org_freq: float, wave_vel: float, obs_vel: float, src_vel: float +) -> float: + """ + Input Parameters: + ----------------- + org_freq: frequency of the wave when the source is stationary + wave_vel: velocity of the wave in the medium + obs_vel: velocity of the observer, +ve if the observer is moving towards the source + src_vel: velocity of the source, +ve if the source is moving towards the observer + + Returns: + -------- + f: frequency of the wave as perceived by the observer + + Docstring Tests: + >>> doppler_effect(100, 330, 10, 0) # observer moving towards the source + 103.03030303030303 + >>> doppler_effect(100, 330, -10, 0) # observer moving away from the source + 96.96969696969697 + >>> doppler_effect(100, 330, 0, 10) # source moving towards the observer + 103.125 + >>> doppler_effect(100, 330, 0, -10) # source moving away from the observer + 97.05882352941177 + >>> doppler_effect(100, 330, 10, 10) # source & observer moving towards each other + 106.25 + >>> doppler_effect(100, 330, -10, -10) # source and observer moving away + 94.11764705882354 + >>> doppler_effect(100, 330, 10, 330) # source moving at same speed as the wave + Traceback (most recent call last): + ... + ZeroDivisionError: Division by zero implies vs=v and observer in front of the source + >>> doppler_effect(100, 330, 10, 340) # source moving faster than the wave + Traceback (most recent call last): + ... + ValueError: Non-positive frequency implies vs>v or v0>v (in the opposite direction) + >>> doppler_effect(100, 330, -340, 10) # observer moving faster than the wave + Traceback (most recent call last): + ... + ValueError: Non-positive frequency implies vs>v or v0>v (in the opposite direction) + """ + + if wave_vel == src_vel: + raise ZeroDivisionError( + "Division by zero implies vs=v and observer in front of the source" + ) + doppler_freq = (org_freq * (wave_vel + obs_vel)) / (wave_vel - src_vel) + if doppler_freq <= 0: + raise ValueError( + "Non-positive frequency implies vs>v or v0>v (in the opposite direction)" + ) + return doppler_freq + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From c92e86bd7950b443fe39ccb19b587df44feaa068 Mon Sep 17 00:00:00 2001 From: "Precious C. Jacob" <72174492+PreciousJac0b@users.noreply.github.com> Date: Sun, 22 Oct 2023 00:33:49 +0100 Subject: [PATCH 432/757] Add tests to data_structures/linked_list/swap_nodes.py (#10751) * Added doctests to the swap_nodes file under linkedlist data structure * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Added doctests to the swap_nodes file under linkedlist data structure * Added doctests to the swap_nodes file under linkedlist data structure * Added doctests to the swap_nodes file under linkedlist data structure * Update swap_nodes.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- data_structures/linked_list/swap_nodes.py | 152 +++++++++++++++------- 1 file changed, 108 insertions(+), 44 deletions(-) diff --git a/data_structures/linked_list/swap_nodes.py b/data_structures/linked_list/swap_nodes.py index 31dcb02bfa9a..d66512087d2d 100644 --- a/data_structures/linked_list/swap_nodes.py +++ b/data_structures/linked_list/swap_nodes.py @@ -1,49 +1,73 @@ +from __future__ import annotations + +from collections.abc import Iterator +from dataclasses import dataclass from typing import Any +@dataclass class Node: - def __init__(self, data: Any) -> None: - """ - Initialize a new Node with the given data. - - Args: - data: The data to be stored in the node. - - """ - self.data = data - self.next: Node | None = None # Reference to the next node + data: Any + next_node: Node | None = None +@dataclass class LinkedList: - def __init__(self) -> None: + head: Node | None = None + + def __iter__(self) -> Iterator: """ - Initialize an empty Linked List. + >>> linked_list = LinkedList() + >>> list(linked_list) + [] + >>> linked_list.push(0) + >>> tuple(linked_list) + (0,) """ - self.head: Node | None = None # Reference to the head (first node) + node = self.head + while node: + yield node.data + node = node.next_node - def print_list(self): + def __len__(self) -> int: """ - Print the elements of the Linked List in order. + >>> linked_list = LinkedList() + >>> len(linked_list) + 0 + >>> linked_list.push(0) + >>> len(linked_list) + 1 """ - temp = self.head - while temp is not None: - print(temp.data, end=" ") - temp = temp.next - print() + return sum(1 for _ in self) def push(self, new_data: Any) -> None: """ Add a new node with the given data to the beginning of the Linked List. + Args: new_data (Any): The data to be added to the new node. + + Returns: + None + + Examples: + >>> linked_list = LinkedList() + >>> linked_list.push(5) + >>> linked_list.push(4) + >>> linked_list.push(3) + >>> linked_list.push(2) + >>> linked_list.push(1) + >>> list(linked_list) + [1, 2, 3, 4, 5] """ new_node = Node(new_data) - new_node.next = self.head + new_node.next_node = self.head self.head = new_node - def swap_nodes(self, node_data_1, node_data_2) -> None: + def swap_nodes(self, node_data_1: Any, node_data_2: Any) -> None: """ Swap the positions of two nodes in the Linked List based on their data values. + Args: node_data_1: Data value of the first node to be swapped. node_data_2: Data value of the second node to be swapped. @@ -51,34 +75,74 @@ def swap_nodes(self, node_data_1, node_data_2) -> None: Note: If either of the specified data values isn't found then, no swapping occurs. + + Examples: + When both values are present in a linked list. + >>> linked_list = LinkedList() + >>> linked_list.push(5) + >>> linked_list.push(4) + >>> linked_list.push(3) + >>> linked_list.push(2) + >>> linked_list.push(1) + >>> list(linked_list) + [1, 2, 3, 4, 5] + >>> linked_list.swap_nodes(1, 5) + >>> tuple(linked_list) + (5, 2, 3, 4, 1) + + When one value is present and the other isn't in the linked list. + >>> second_list = LinkedList() + >>> second_list.push(6) + >>> second_list.push(7) + >>> second_list.push(8) + >>> second_list.push(9) + >>> second_list.swap_nodes(1, 6) is None + True + + When both values are absent in the linked list. + >>> second_list = LinkedList() + >>> second_list.push(10) + >>> second_list.push(9) + >>> second_list.push(8) + >>> second_list.push(7) + >>> second_list.swap_nodes(1, 3) is None + True + + When linkedlist is empty. + >>> second_list = LinkedList() + >>> second_list.swap_nodes(1, 3) is None + True + + Returns: + None """ if node_data_1 == node_data_2: return - else: - node_1 = self.head - while node_1 is not None and node_1.data != node_data_1: - node_1 = node_1.next - - node_2 = self.head - while node_2 is not None and node_2.data != node_data_2: - node_2 = node_2.next - - if node_1 is None or node_2 is None: - return - # Swap the data values of the two nodes - node_1.data, node_2.data = node_2.data, node_1.data + node_1 = self.head + while node_1 and node_1.data != node_data_1: + node_1 = node_1.next_node + node_2 = self.head + while node_2 and node_2.data != node_data_2: + node_2 = node_2.next_node + if node_1 is None or node_2 is None: + return + # Swap the data values of the two nodes + node_1.data, node_2.data = node_2.data, node_1.data if __name__ == "__main__": - ll = LinkedList() - for i in range(5, 0, -1): - ll.push(i) + """ + Python script that outputs the swap of nodes in a linked list. + """ + from doctest import testmod - print("Original Linked List:") - ll.print_list() - - ll.swap_nodes(1, 4) - print("After swapping the nodes whose data is 1 and 4:") + testmod() + linked_list = LinkedList() + for i in range(5, 0, -1): + linked_list.push(i) - ll.print_list() + print(f"Original Linked List: {list(linked_list)}") + linked_list.swap_nodes(1, 4) + print(f"Modified Linked List: {list(linked_list)}") + print("After swapping the nodes whose data is 1 and 4.") From d73a4c2ee035698de437086230985574766f195b Mon Sep 17 00:00:00 2001 From: santiditomas <72716997+santiditomas@users.noreply.github.com> Date: Sat, 21 Oct 2023 20:59:41 -0300 Subject: [PATCH 433/757] adding new physics algorithm: center of mass (#10743) * adding new physics algorithm: center of mass * Add changes requested by the reviewer * Add changes requested by the reviewer * Update center_of_mass.py * Update center_of_mass.py --------- Co-authored-by: Christian Clauss --- physics/center_of_mass.py | 109 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 physics/center_of_mass.py diff --git a/physics/center_of_mass.py b/physics/center_of_mass.py new file mode 100644 index 000000000000..bd9ba2480584 --- /dev/null +++ b/physics/center_of_mass.py @@ -0,0 +1,109 @@ +""" +Calculating the center of mass for a discrete system of particles, given their +positions and masses. + +Description: + +In physics, the center of mass of a distribution of mass in space (sometimes referred +to as the barycenter or balance point) is the unique point at any given time where the +weighted relative position of the distributed mass sums to zero. This is the point to +which a force may be applied to cause a linear acceleration without an angular +acceleration. + +Calculations in mechanics are often simplified when formulated with respect to the +center of mass. It is a hypothetical point where the entire mass of an object may be +assumed to be concentrated to visualize its motion. In other words, the center of mass +is the particle equivalent of a given object for the application of Newton's laws of +motion. + +In the case of a system of particles P_i, i = 1, ..., n , each with mass m_i that are +located in space with coordinates r_i, i = 1, ..., n , the coordinates R of the center +of mass corresponds to: + +R = (Σ(mi * ri) / Σ(mi)) + +Reference: https://en.wikipedia.org/wiki/Center_of_mass +""" +from collections import namedtuple + +Particle = namedtuple("Particle", "x y z mass") # noqa: PYI024 +Coord3D = namedtuple("Coord3D", "x y z") # noqa: PYI024 + + +def center_of_mass(particles: list[Particle]) -> Coord3D: + """ + Input Parameters + ---------------- + particles: list(Particle): + A list of particles where each particle is a tuple with it´s (x, y, z) position and + it´s mass. + + Returns + ------- + Coord3D: + A tuple with the coordinates of the center of mass (Xcm, Ycm, Zcm) rounded to two + decimal places. + + Examples + -------- + >>> center_of_mass([ + ... Particle(1.5, 4, 3.4, 4), + ... Particle(5, 6.8, 7, 8.1), + ... Particle(9.4, 10.1, 11.6, 12) + ... ]) + Coord3D(x=6.61, y=7.98, z=8.69) + + >>> center_of_mass([ + ... Particle(1, 2, 3, 4), + ... Particle(5, 6, 7, 8), + ... Particle(9, 10, 11, 12) + ... ]) + Coord3D(x=6.33, y=7.33, z=8.33) + + >>> center_of_mass([ + ... Particle(1, 2, 3, -4), + ... Particle(5, 6, 7, 8), + ... Particle(9, 10, 11, 12) + ... ]) + Traceback (most recent call last): + ... + ValueError: Mass of all particles must be greater than 0 + + >>> center_of_mass([ + ... Particle(1, 2, 3, 0), + ... Particle(5, 6, 7, 8), + ... Particle(9, 10, 11, 12) + ... ]) + Traceback (most recent call last): + ... + ValueError: Mass of all particles must be greater than 0 + + >>> center_of_mass([]) + Traceback (most recent call last): + ... + ValueError: No particles provided + """ + if not particles: + raise ValueError("No particles provided") + + if any(particle.mass <= 0 for particle in particles): + raise ValueError("Mass of all particles must be greater than 0") + + total_mass = sum(particle.mass for particle in particles) + + center_of_mass_x = round( + sum(particle.x * particle.mass for particle in particles) / total_mass, 2 + ) + center_of_mass_y = round( + sum(particle.y * particle.mass for particle in particles) / total_mass, 2 + ) + center_of_mass_z = round( + sum(particle.z * particle.mass for particle in particles) / total_mass, 2 + ) + return Coord3D(center_of_mass_x, center_of_mass_y, center_of_mass_z) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 0601b56173021fe96fb070d20085962b036e85c3 Mon Sep 17 00:00:00 2001 From: gio-puter <103840942+gio-puter@users.noreply.github.com> Date: Sat, 21 Oct 2023 22:42:26 -0700 Subject: [PATCH 434/757] Add tests without modifying code (#10740) * Contributes to #9943 Added doctest to largest_of_very_large_numbers.py Added doctest to word_patterns.py Added doctest to onepad_cipher.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Contributes to #9943 Added doctest to maths/largest_of_very_large_numbers.py Added doctest to strings/word_patterns.py Added doctest to ciphers/onepad_cipher.py * Add tests without modifying code #10740 Added test to maths/largest_of_very_large_numbers Added test to strings/word_patterns.py Added test to ciphers/onepad_cipher.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- ciphers/onepad_cipher.py | 37 ++++++++++++++++++++++++-- maths/largest_of_very_large_numbers.py | 13 +++++++++ strings/word_patterns.py | 21 +++++++++++++++ 3 files changed, 69 insertions(+), 2 deletions(-) diff --git a/ciphers/onepad_cipher.py b/ciphers/onepad_cipher.py index 4bfe35b7180a..c4fb22e14a06 100644 --- a/ciphers/onepad_cipher.py +++ b/ciphers/onepad_cipher.py @@ -4,7 +4,27 @@ class Onepad: @staticmethod def encrypt(text: str) -> tuple[list[int], list[int]]: - """Function to encrypt text using pseudo-random numbers""" + """ + Function to encrypt text using pseudo-random numbers + >>> Onepad().encrypt("") + ([], []) + >>> Onepad().encrypt([]) + ([], []) + >>> random.seed(1) + >>> Onepad().encrypt(" ") + ([6969], [69]) + >>> random.seed(1) + >>> Onepad().encrypt("Hello") + ([9729, 114756, 4653, 31309, 10492], [69, 292, 33, 131, 61]) + >>> Onepad().encrypt(1) + Traceback (most recent call last): + ... + TypeError: 'int' object is not iterable + >>> Onepad().encrypt(1.1) + Traceback (most recent call last): + ... + TypeError: 'float' object is not iterable + """ plain = [ord(i) for i in text] key = [] cipher = [] @@ -17,7 +37,20 @@ def encrypt(text: str) -> tuple[list[int], list[int]]: @staticmethod def decrypt(cipher: list[int], key: list[int]) -> str: - """Function to decrypt text using pseudo-random numbers.""" + """ + Function to decrypt text using pseudo-random numbers. + >>> Onepad().decrypt([], []) + '' + >>> Onepad().decrypt([35], []) + '' + >>> Onepad().decrypt([], [35]) + Traceback (most recent call last): + ... + IndexError: list index out of range + >>> random.seed(1) + >>> Onepad().decrypt([9729, 114756, 4653, 31309, 10492], [69, 292, 33, 131, 61]) + 'Hello' + """ plain = [] for i in range(len(key)): p = int((cipher[i] - (key[i]) ** 2) / key[i]) diff --git a/maths/largest_of_very_large_numbers.py b/maths/largest_of_very_large_numbers.py index 7e7fea004958..eb5c121fd262 100644 --- a/maths/largest_of_very_large_numbers.py +++ b/maths/largest_of_very_large_numbers.py @@ -4,6 +4,19 @@ def res(x, y): + """ + Reduces large number to a more manageable number + >>> res(5, 7) + 4.892790030352132 + >>> res(0, 5) + 0 + >>> res(3, 0) + 1 + >>> res(-1, 5) + Traceback (most recent call last): + ... + ValueError: math domain error + """ if 0 not in (x, y): # We use the relation x^y = y*log10(x), where 10 is the base. return y * math.log10(x) diff --git a/strings/word_patterns.py b/strings/word_patterns.py index d12d267e7b35..ed603e9fefeb 100644 --- a/strings/word_patterns.py +++ b/strings/word_patterns.py @@ -1,11 +1,32 @@ def get_word_pattern(word: str) -> str: """ + Returns numerical pattern of character appearances in given word + >>> get_word_pattern("") + '' + >>> get_word_pattern(" ") + '0' >>> get_word_pattern("pattern") '0.1.2.2.3.4.5' >>> get_word_pattern("word pattern") '0.1.2.3.4.5.6.7.7.8.2.9' >>> get_word_pattern("get word pattern") '0.1.2.3.4.5.6.7.3.8.9.2.2.1.6.10' + >>> get_word_pattern() + Traceback (most recent call last): + ... + TypeError: get_word_pattern() missing 1 required positional argument: 'word' + >>> get_word_pattern(1) + Traceback (most recent call last): + ... + AttributeError: 'int' object has no attribute 'upper' + >>> get_word_pattern(1.1) + Traceback (most recent call last): + ... + AttributeError: 'float' object has no attribute 'upper' + >>> get_word_pattern([]) + Traceback (most recent call last): + ... + AttributeError: 'list' object has no attribute 'upper' """ word = word.upper() next_num = 0 From 7d0f6e012acb42271652f9a398675305b7e270d2 Mon Sep 17 00:00:00 2001 From: Kento <75509362+nkstonks@users.noreply.github.com> Date: Sun, 22 Oct 2023 20:08:08 +1100 Subject: [PATCH 435/757] Updated doctests for nor_gate (#10791) * added other possible cases * added test for correct output of truth table * updating DIRECTORY.md * Update nor_gate.py --------- Co-authored-by: = <=> Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: Christian Clauss --- DIRECTORY.md | 6 ++-- boolean_algebra/nor_gate.py | 55 +++++++++++++++++++++++++------------ 2 files changed, 41 insertions(+), 20 deletions(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index 9e0166ad80c5..c37c4f99ba88 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -541,7 +541,7 @@ * [Dimensionality Reduction](machine_learning/dimensionality_reduction.py) * Forecasting * [Run](machine_learning/forecasting/run.py) - * [Frequent Pattern Growth Algorithm](machine_learning/frequent_pattern_growth.py) + * [Frequent Pattern Growth](machine_learning/frequent_pattern_growth.py) * [Gradient Descent](machine_learning/gradient_descent.py) * [K Means Clust](machine_learning/k_means_clust.py) * [K Nearest Neighbours](machine_learning/k_nearest_neighbours.py) @@ -649,6 +649,7 @@ * [Numerical Integration](maths/numerical_integration.py) * [Odd Sieve](maths/odd_sieve.py) * [Perfect Cube](maths/perfect_cube.py) + * [Perfect Number](maths/perfect_number.py) * [Perfect Square](maths/perfect_square.py) * [Persistence](maths/persistence.py) * [Pi Generator](maths/pi_generator.py) @@ -767,7 +768,6 @@ * [Swish](neural_network/activation_functions/swish.py) * [Back Propagation Neural Network](neural_network/back_propagation_neural_network.py) * [Convolution Neural Network](neural_network/convolution_neural_network.py) - * [Perceptron](neural_network/perceptron.py) * [Simple Neural Network](neural_network/simple_neural_network.py) ## Other @@ -803,8 +803,10 @@ * [Archimedes Principle Of Buoyant Force](physics/archimedes_principle_of_buoyant_force.py) * [Basic Orbital Capture](physics/basic_orbital_capture.py) * [Casimir Effect](physics/casimir_effect.py) + * [Center Of Mass](physics/center_of_mass.py) * [Centripetal Force](physics/centripetal_force.py) * [Coulombs Law](physics/coulombs_law.py) + * [Doppler Frequency](physics/doppler_frequency.py) * [Grahams Law](physics/grahams_law.py) * [Horizontal Projectile Motion](physics/horizontal_projectile_motion.py) * [Hubble Parameter](physics/hubble_parameter.py) diff --git a/boolean_algebra/nor_gate.py b/boolean_algebra/nor_gate.py index 2c27b80afdbe..0c8ab1c0af61 100644 --- a/boolean_algebra/nor_gate.py +++ b/boolean_algebra/nor_gate.py @@ -1,15 +1,18 @@ """ -A NOR Gate is a logic gate in boolean algebra which results to false(0) -if any of the input is 1, and True(1) if both the inputs are 0. +A NOR Gate is a logic gate in boolean algebra which results in false(0) if any of the +inputs is 1, and True(1) if all inputs are 0. Following is the truth table of a NOR Gate: - | Input 1 | Input 2 | Output | - | 0 | 0 | 1 | - | 0 | 1 | 0 | - | 1 | 0 | 0 | - | 1 | 1 | 0 | + Truth Table of NOR Gate: + | Input 1 | Input 2 | Output | + | 0 | 0 | 1 | + | 0 | 1 | 0 | + | 1 | 0 | 0 | + | 1 | 1 | 0 | -Following is the code implementation of the NOR Gate + Code provided by Akshaj Vishwanathan +https://www.geeksforgeeks.org/logic-gates-in-python """ +from collections.abc import Callable def nor_gate(input_1: int, input_2: int) -> int: @@ -30,19 +33,35 @@ def nor_gate(input_1: int, input_2: int) -> int: return int(input_1 == input_2 == 0) -def main() -> None: - print("Truth Table of NOR Gate:") - print("| Input 1 | Input 2 | Output |") - print(f"| 0 | 0 | {nor_gate(0, 0)} |") - print(f"| 0 | 1 | {nor_gate(0, 1)} |") - print(f"| 1 | 0 | {nor_gate(1, 0)} |") - print(f"| 1 | 1 | {nor_gate(1, 1)} |") +def truth_table(func: Callable) -> str: + """ + >>> print(truth_table(nor_gate)) + Truth Table of NOR Gate: + | Input 1 | Input 2 | Output | + | 0 | 0 | 1 | + | 0 | 1 | 0 | + | 1 | 0 | 0 | + | 1 | 1 | 0 | + """ + + def make_table_row(items: list | tuple) -> str: + """ + >>> make_table_row(("One", "Two", "Three")) + '| One | Two | Three |' + """ + return f"| {' | '.join(f'{item:^8}' for item in items)} |" + + return "\n".join( + ( + "Truth Table of NOR Gate:", + make_table_row(("Input 1", "Input 2", "Output")), + *[make_table_row((i, j, func(i, j))) for i in (0, 1) for j in (0, 1)], + ) + ) if __name__ == "__main__": import doctest doctest.testmod() - main() -"""Code provided by Akshaj Vishwanathan""" -"""Reference: https://www.geeksforgeeks.org/logic-gates-in-python/""" + print(truth_table(nor_gate)) From 6c8743f1e62c785e58a45f785b380f27693aadf9 Mon Sep 17 00:00:00 2001 From: Jeel Gajera <83470656+JeelGajera@users.noreply.github.com> Date: Sun, 22 Oct 2023 19:21:30 +0530 Subject: [PATCH 436/757] Add: Time Conversion Function (#10749) * Add: Time Conversion Function * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update conversions/time_conversions.py Co-authored-by: Christian Clauss * fix: required changes * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix: err * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update time_conversions.py --------- Co-authored-by: Jeel Gajera Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- DIRECTORY.md | 1 + conversions/time_conversions.py | 86 +++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 conversions/time_conversions.py diff --git a/DIRECTORY.md b/DIRECTORY.md index c37c4f99ba88..f45102ae1ce1 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -174,6 +174,7 @@ * [Roman Numerals](conversions/roman_numerals.py) * [Speed Conversions](conversions/speed_conversions.py) * [Temperature Conversions](conversions/temperature_conversions.py) + * [Time Conversions](conversions/time_conversions.py) * [Volume Conversions](conversions/volume_conversions.py) * [Weight Conversion](conversions/weight_conversion.py) diff --git a/conversions/time_conversions.py b/conversions/time_conversions.py new file mode 100644 index 000000000000..8c30f5bc4a45 --- /dev/null +++ b/conversions/time_conversions.py @@ -0,0 +1,86 @@ +""" +A unit of time is any particular time interval, used as a standard way of measuring or +expressing duration. The base unit of time in the International System of Units (SI), +and by extension most of the Western world, is the second, defined as about 9 billion +oscillations of the caesium atom. + +https://en.wikipedia.org/wiki/Unit_of_time +""" + +time_chart: dict[str, float] = { + "seconds": 1.0, + "minutes": 60.0, # 1 minute = 60 sec + "hours": 3600.0, # 1 hour = 60 minutes = 3600 seconds + "days": 86400.0, # 1 day = 24 hours = 1440 min = 86400 sec + "weeks": 604800.0, # 1 week=7d=168hr=10080min = 604800 sec + "months": 2629800.0, # Approximate value for a month in seconds + "years": 31557600.0, # Approximate value for a year in seconds +} + +time_chart_inverse: dict[str, float] = { + key: 1 / value for key, value in time_chart.items() +} + + +def convert_time(time_value: float, unit_from: str, unit_to: str) -> float: + """ + Convert time from one unit to another using the time_chart above. + + >>> convert_time(3600, "seconds", "hours") + 1.0 + >>> convert_time(3500, "Seconds", "Hours") + 0.972 + >>> convert_time(1, "DaYs", "hours") + 24.0 + >>> convert_time(120, "minutes", "SeCoNdS") + 7200.0 + >>> convert_time(2, "WEEKS", "days") + 14.0 + >>> convert_time(0.5, "hours", "MINUTES") + 30.0 + >>> convert_time(-3600, "seconds", "hours") + Traceback (most recent call last): + ... + ValueError: 'time_value' must be a non-negative number. + >>> convert_time("Hello", "hours", "minutes") + Traceback (most recent call last): + ... + ValueError: 'time_value' must be a non-negative number. + >>> convert_time([0, 1, 2], "weeks", "days") + Traceback (most recent call last): + ... + ValueError: 'time_value' must be a non-negative number. + >>> convert_time(1, "cool", "century") # doctest: +ELLIPSIS + Traceback (most recent call last): + ... + ValueError: Invalid unit cool is not in seconds, minutes, hours, days, weeks, ... + >>> convert_time(1, "seconds", "hot") # doctest: +ELLIPSIS + Traceback (most recent call last): + ... + ValueError: Invalid unit hot is not in seconds, minutes, hours, days, weeks, ... + """ + if not isinstance(time_value, (int, float)) or time_value < 0: + msg = "'time_value' must be a non-negative number." + raise ValueError(msg) + + unit_from = unit_from.lower() + unit_to = unit_to.lower() + if unit_from not in time_chart or unit_to not in time_chart: + invalid_unit = unit_from if unit_from not in time_chart else unit_to + msg = f"Invalid unit {invalid_unit} is not in {', '.join(time_chart)}." + raise ValueError(msg) + + return round( + time_value * time_chart[unit_from] * time_chart_inverse[unit_to], + 3, + ) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + print(f"{convert_time(3600,'seconds', 'hours') = :,}") + print(f"{convert_time(360, 'days', 'months') = :,}") + print(f"{convert_time(360, 'months', 'years') = :,}") + print(f"{convert_time(1, 'years', 'seconds') = :,}") From a8b94abc8b9131e260a5281f4c95a0d4f2d03325 Mon Sep 17 00:00:00 2001 From: Hardik Pawar <97388607+Hardvan@users.noreply.github.com> Date: Mon, 23 Oct 2023 00:21:56 +0530 Subject: [PATCH 437/757] Enhance readability of N Queens (#9265) * Enhance readability of N Queens * Simplify is_safe code * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Apply suggestions from code review --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Tianyi Zheng --- backtracking/n_queens.py | 53 ++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/backtracking/n_queens.py b/backtracking/n_queens.py index bbf0ce44f91c..0f237d95e7c8 100644 --- a/backtracking/n_queens.py +++ b/backtracking/n_queens.py @@ -17,40 +17,39 @@ def is_safe(board: list[list[int]], row: int, column: int) -> bool: This function returns a boolean value True if it is safe to place a queen there considering the current state of the board. - Parameters : - board(2D matrix) : board - row ,column : coordinates of the cell on a board + Parameters: + board (2D matrix): The chessboard + row, column: Coordinates of the cell on the board - Returns : + Returns: Boolean Value """ - for i in range(len(board)): - if board[row][i] == 1: - return False - for i in range(len(board)): - if board[i][column] == 1: - return False - for i, j in zip(range(row, -1, -1), range(column, -1, -1)): - if board[i][j] == 1: - return False - for i, j in zip(range(row, -1, -1), range(column, len(board))): - if board[i][j] == 1: - return False - return True + + n = len(board) # Size of the board + + # Check if there is any queen in the same row, column, + # left upper diagonal, and right upper diagonal + return ( + all(board[i][j] != 1 for i, j in zip(range(row, -1, -1), range(column, n))) + and all( + board[i][j] != 1 for i, j in zip(range(row, -1, -1), range(column, -1, -1)) + ) + and all(board[i][j] != 1 for i, j in zip(range(row, n), range(column, n))) + and all(board[i][j] != 1 for i, j in zip(range(row, n), range(column, -1, -1))) + ) def solve(board: list[list[int]], row: int) -> bool: """ - It creates a state space tree and calls the safe function until it receives a - False Boolean and terminates that branch and backtracks to the next + This function creates a state space tree and calls the safe function until it + receives a False Boolean and terminates that branch and backtracks to the next possible solution branch. """ if row >= len(board): """ - If the row number exceeds N we have board with a successful combination + If the row number exceeds N, we have a board with a successful combination and that combination is appended to the solution list and the board is printed. - """ solution.append(board) printboard(board) @@ -58,9 +57,9 @@ def solve(board: list[list[int]], row: int) -> bool: return True for i in range(len(board)): """ - For every row it iterates through each column to check if it is feasible to + For every row, it iterates through each column to check if it is feasible to place a queen there. - If all the combinations for that particular branch are successful the board is + If all the combinations for that particular branch are successful, the board is reinitialized for the next possible combination. """ if is_safe(board, row, i): @@ -77,14 +76,14 @@ def printboard(board: list[list[int]]) -> None: for i in range(len(board)): for j in range(len(board)): if board[i][j] == 1: - print("Q", end=" ") + print("Q", end=" ") # Queen is present else: - print(".", end=" ") + print(".", end=" ") # Empty cell print() -# n=int(input("The no. of queens")) +# Number of queens (e.g., n=8 for an 8x8 board) n = 8 board = [[0 for i in range(n)] for j in range(n)] solve(board, 0) -print("The total no. of solutions are :", len(solution)) +print("The total number of solutions are:", len(solution)) From fdb0635c71318da758fafcda80154d03dbbd5c5a Mon Sep 17 00:00:00 2001 From: Anshu Sharma <142900182+AnshuSharma111@users.noreply.github.com> Date: Mon, 23 Oct 2023 03:09:31 +0530 Subject: [PATCH 438/757] added doctest to playfair_cipher.py (#10823) * added doctest to playfair_cipher.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Added newline to EOF andremoved trailing whitespace * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Apply suggestions from code review --------- Co-authored-by: Keyboard-1 <142900182+Keyboard-1@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Tianyi Zheng --- ciphers/playfair_cipher.py | 59 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 2 deletions(-) diff --git a/ciphers/playfair_cipher.py b/ciphers/playfair_cipher.py index 7279fb23ecb2..86b45bc4fb6a 100644 --- a/ciphers/playfair_cipher.py +++ b/ciphers/playfair_cipher.py @@ -1,3 +1,24 @@ +""" +https://en.wikipedia.org/wiki/Playfair_cipher#Description + +The Playfair cipher was developed by Charles Wheatstone in 1854 +It's use was heavily promotedby Lord Playfair, hence its name + +Some features of the Playfair cipher are: + +1) It was the first literal diagram substitution cipher +2) It is a manual symmetric encryption technique +3) It is a multiple letter encryption cipher + +The implementation in the code below encodes alphabets only. +It removes spaces, special characters and numbers from the +code. + +Playfair is no longer used by military forces because of known +insecurities and of the advent of automated encryption devices. +This cipher is regarded as insecure since before World War I. +""" + import itertools import string from collections.abc import Generator, Iterable @@ -60,11 +81,26 @@ def generate_table(key: str) -> list[str]: def encode(plaintext: str, key: str) -> str: + """ + Encode the given plaintext using the Playfair cipher. + Takes the plaintext and the key as input and returns the encoded string. + + >>> encode("Hello", "MONARCHY") + 'CFSUPM' + >>> encode("attack on the left flank", "EMERGENCY") + 'DQZSBYFSDZFMFNLOHFDRSG' + >>> encode("Sorry!", "SPECIAL") + 'AVXETX' + >>> encode("Number 1", "NUMBER") + 'UMBENF' + >>> encode("Photosynthesis!", "THE SUN") + 'OEMHQHVCHESUKE' + """ + table = generate_table(key) plaintext = prepare_input(plaintext) ciphertext = "" - # https://en.wikipedia.org/wiki/Playfair_cipher#Description for char1, char2 in chunker(plaintext, 2): row1, col1 = divmod(table.index(char1), 5) row2, col2 = divmod(table.index(char2), 5) @@ -83,10 +119,20 @@ def encode(plaintext: str, key: str) -> str: def decode(ciphertext: str, key: str) -> str: + """ + Decode the input string using the provided key. + + >>> decode("BMZFAZRZDH", "HAZARD") + 'FIREHAZARD' + >>> decode("HNBWBPQT", "AUTOMOBILE") + 'DRIVINGX' + >>> decode("SLYSSAQS", "CASTLE") + 'ATXTACKX' + """ + table = generate_table(key) plaintext = "" - # https://en.wikipedia.org/wiki/Playfair_cipher#Description for char1, char2 in chunker(ciphertext, 2): row1, col1 = divmod(table.index(char1), 5) row2, col2 = divmod(table.index(char2), 5) @@ -102,3 +148,12 @@ def decode(ciphertext: str, key: str) -> str: plaintext += table[row2 * 5 + col1] return plaintext + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + + print("Encoded:", encode("BYE AND THANKS", "GREETING")) + print("Decoded:", decode("CXRBANRLBALQ", "GREETING")) From abd6bca074e8a846d5e306311845b46f7581012e Mon Sep 17 00:00:00 2001 From: Ankit Avinash <128812932+Void426@users.noreply.github.com> Date: Mon, 23 Oct 2023 10:54:51 +0530 Subject: [PATCH 439/757] Added Binary Focal Cross Entropy (#10674) * Added Binary Focal Cross Entropy * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fixed Issue * Fixed Issue * Added BFCE loss to loss_functions.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update machine_learning/loss_functions.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Tianyi Zheng --- machine_learning/loss_functions.py | 51 ++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/machine_learning/loss_functions.py b/machine_learning/loss_functions.py index 0fa0956ed572..ef34296360e2 100644 --- a/machine_learning/loss_functions.py +++ b/machine_learning/loss_functions.py @@ -39,6 +39,57 @@ def binary_cross_entropy( return np.mean(bce_loss) +def binary_focal_cross_entropy( + y_true: np.ndarray, + y_pred: np.ndarray, + gamma: float = 2.0, + alpha: float = 0.25, + epsilon: float = 1e-15, +) -> float: + """ + Calculate the mean binary focal cross-entropy (BFCE) loss between true labels + and predicted probabilities. + + BFCE loss quantifies dissimilarity between true labels (0 or 1) and predicted + probabilities. It's a variation of binary cross-entropy that addresses class + imbalance by focusing on hard examples. + + BCFE = -Σ(alpha * (1 - y_pred)**gamma * y_true * log(y_pred) + + (1 - alpha) * y_pred**gamma * (1 - y_true) * log(1 - y_pred)) + + Reference: [Lin et al., 2018](https://arxiv.org/pdf/1708.02002.pdf) + + Parameters: + - y_true: True binary labels (0 or 1). + - y_pred: Predicted probabilities for class 1. + - gamma: Focusing parameter for modulating the loss (default: 2.0). + - alpha: Weighting factor for class 1 (default: 0.25). + - epsilon: Small constant to avoid numerical instability. + + >>> true_labels = np.array([0, 1, 1, 0, 1]) + >>> predicted_probs = np.array([0.2, 0.7, 0.9, 0.3, 0.8]) + >>> binary_focal_cross_entropy(true_labels, predicted_probs) + 0.008257977659239775 + >>> true_labels = np.array([0, 1, 1, 0, 1]) + >>> predicted_probs = np.array([0.3, 0.8, 0.9, 0.2]) + >>> binary_focal_cross_entropy(true_labels, predicted_probs) + Traceback (most recent call last): + ... + ValueError: Input arrays must have the same length. + """ + if len(y_true) != len(y_pred): + raise ValueError("Input arrays must have the same length.") + # Clip predicted probabilities to avoid log(0) + y_pred = np.clip(y_pred, epsilon, 1 - epsilon) + + bcfe_loss = -( + alpha * (1 - y_pred) ** gamma * y_true * np.log(y_pred) + + (1 - alpha) * y_pred**gamma * (1 - y_true) * np.log(1 - y_pred) + ) + + return np.mean(bcfe_loss) + + def categorical_cross_entropy( y_true: np.ndarray, y_pred: np.ndarray, epsilon: float = 1e-15 ) -> float: From dc4e89805a642d1c6e3fe031276edbfde3c1f40c Mon Sep 17 00:00:00 2001 From: Suyash Dongre <109069262+Suyashd999@users.noreply.github.com> Date: Mon, 23 Oct 2023 10:57:33 +0530 Subject: [PATCH 440/757] Added docstring/documentation for sigmoid_function (#10756) * Added doctest for sigmoid_function * Added doctest for sigmoid_function * Added doctest for sigmoid_function --- machine_learning/logistic_regression.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/machine_learning/logistic_regression.py b/machine_learning/logistic_regression.py index 87bc8f6681cc..f9da0104ab4b 100644 --- a/machine_learning/logistic_regression.py +++ b/machine_learning/logistic_regression.py @@ -28,6 +28,21 @@ def sigmoid_function(z): + """ + Also known as Logistic Function. + + 1 + f(x) = ------- + 1 + e⁻ˣ + + The sigmoid function approaches a value of 1 as its input 'x' becomes + increasing positive. Opposite for negative values. + + Reference: https://en.wikipedia.org/wiki/Sigmoid_function + + @param z: input to the function + @returns: returns value in the range 0 to 1 + """ return 1 / (1 + np.exp(-z)) From 68faebe711899bf6072ceedb16ccf1fbdc7d2434 Mon Sep 17 00:00:00 2001 From: Pratik Tripathy <117454569+SilverDragonOfR@users.noreply.github.com> Date: Mon, 23 Oct 2023 11:05:10 +0530 Subject: [PATCH 441/757] feat: Add mass energy equivalence in physics and doctests (#10202) * updating DIRECTORY.md * feat: Add mass energy equivalence in physics * updating DIRECTORY.md * updating DIRECTORY.md * Apply suggestions from code review * Update physics/mass_energy_equivalence.py * Update mass_energy_equivalence.py --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: Tianyi Zheng --- DIRECTORY.md | 1 + physics/mass_energy_equivalence.py | 77 ++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 physics/mass_energy_equivalence.py diff --git a/DIRECTORY.md b/DIRECTORY.md index f45102ae1ce1..c07e1550d1eb 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -815,6 +815,7 @@ * [Kinetic Energy](physics/kinetic_energy.py) * [Lorentz Transformation Four Vector](physics/lorentz_transformation_four_vector.py) * [Malus Law](physics/malus_law.py) + * [Mass Energy Equivalence](physics/mass_energy_equivalence.py) * [Mirror Formulae](physics/mirror_formulae.py) * [N Body Simulation](physics/n_body_simulation.py) * [Newtons Law Of Gravitation](physics/newtons_law_of_gravitation.py) diff --git a/physics/mass_energy_equivalence.py b/physics/mass_energy_equivalence.py new file mode 100644 index 000000000000..4a4c7890f4e0 --- /dev/null +++ b/physics/mass_energy_equivalence.py @@ -0,0 +1,77 @@ +""" +Title: +Finding the energy equivalence of mass and mass equivalence of energy +by Einstein's equation. + +Description: +Einstein's mass-energy equivalence is a pivotal concept in theoretical physics. +It asserts that energy (E) and mass (m) are directly related by the speed of +light in vacuum (c) squared, as described in the equation E = mc². This means that +mass and energy are interchangeable; a mass increase corresponds to an energy increase, +and vice versa. This principle has profound implications in nuclear reactions, +explaining the release of immense energy from minuscule changes in atomic nuclei. + +Equations: +E = mc² and m = E/c², where m is mass, E is Energy, c is speed of light in vacuum. + +Reference: +https://en.wikipedia.org/wiki/Mass%E2%80%93energy_equivalence +""" + +from scipy.constants import c # speed of light in vacuum (299792458 m/s) + + +def energy_from_mass(mass: float) -> float: + """ + Calculates the Energy equivalence of the Mass using E = mc² + in SI units J from Mass in kg. + + mass (float): Mass of body. + + Usage example: + >>> energy_from_mass(124.56) + 1.11948945063458e+19 + >>> energy_from_mass(320) + 2.8760165719578165e+19 + >>> energy_from_mass(0) + 0.0 + >>> energy_from_mass(-967.9) + Traceback (most recent call last): + ... + ValueError: Mass can't be negative. + + """ + if mass < 0: + raise ValueError("Mass can't be negative.") + return mass * c**2 + + +def mass_from_energy(energy: float) -> float: + """ + Calculates the Mass equivalence of the Energy using m = E/c² + in SI units kg from Energy in J. + + energy (float): Mass of body. + + Usage example: + >>> mass_from_energy(124.56) + 1.3859169098203872e-15 + >>> mass_from_energy(320) + 3.560480179371579e-15 + >>> mass_from_energy(0) + 0.0 + >>> mass_from_energy(-967.9) + Traceback (most recent call last): + ... + ValueError: Energy can't be negative. + + """ + if energy < 0: + raise ValueError("Energy can't be negative.") + return energy / c**2 + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From abc390967d5479ec74bfd384a86cefa5ddbf6d40 Mon Sep 17 00:00:00 2001 From: Paarth Goyal <138299656+pluto-tofu@users.noreply.github.com> Date: Mon, 23 Oct 2023 11:13:30 +0530 Subject: [PATCH 442/757] =?UTF-8?q?Added=20the=20algorithm=20to=20compute?= =?UTF-8?q?=20the=20terminal=20velocity=20of=20an=20object=20fal=E2=80=A6?= =?UTF-8?q?=20(#10237)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * added the algorithm to compute the terminal velocity of an object falling in a fluid * fixed spelling mistake * fixed issues in topic description * imported the value of g from scipy and changed the doctests accordingly * fixed formatting * Apply suggestions from code review --------- Co-authored-by: Tianyi Zheng --- physics/terminal_velocity.py | 60 ++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 physics/terminal_velocity.py diff --git a/physics/terminal_velocity.py b/physics/terminal_velocity.py new file mode 100644 index 000000000000..cec54162e2b4 --- /dev/null +++ b/physics/terminal_velocity.py @@ -0,0 +1,60 @@ +""" +Title : Computing the terminal velocity of an object falling + through a fluid. + +Terminal velocity is defined as the highest velocity attained by an +object falling through a fluid. It is observed when the sum of drag force +and buoyancy is equal to the downward gravity force acting on the +object. The acceleration of the object is zero as the net force acting on +the object is zero. + +Vt = ((2 * m * g)/(ρ * A * Cd))^0.5 + +where : +Vt = Terminal velocity (in m/s) +m = Mass of the falling object (in Kg) +g = Acceleration due to gravity (value taken : imported from scipy) +ρ = Density of the fluid through which the object is falling (in Kg/m^3) +A = Projected area of the object (in m^2) +Cd = Drag coefficient (dimensionless) + +Reference : https://byjus.com/physics/derivation-of-terminal-velocity/ +""" + +from scipy.constants import g + + +def terminal_velocity( + mass: float, density: float, area: float, drag_coefficient: float +) -> float: + """ + >>> terminal_velocity(1, 25, 0.6, 0.77) + 1.3031197996044768 + >>> terminal_velocity(2, 100, 0.45, 0.23) + 1.9467947148674276 + >>> terminal_velocity(5, 50, 0.2, 0.5) + 4.428690551393267 + >>> terminal_velocity(-5, 50, -0.2, -2) + Traceback (most recent call last): + ... + ValueError: mass, density, area and the drag coefficient all need to be positive + >>> terminal_velocity(3, -20, -1, 2) + Traceback (most recent call last): + ... + ValueError: mass, density, area and the drag coefficient all need to be positive + >>> terminal_velocity(-2, -1, -0.44, -1) + Traceback (most recent call last): + ... + ValueError: mass, density, area and the drag coefficient all need to be positive + """ + if mass <= 0 or density <= 0 or area <= 0 or drag_coefficient <= 0: + raise ValueError( + "mass, density, area and the drag coefficient all need to be positive" + ) + return ((2 * mass * g) / (density * area * drag_coefficient)) ** 0.5 + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From a9cee1d933606092eb966a601eb1d9efd6e054af Mon Sep 17 00:00:00 2001 From: Dale Dai <145884899+CouldNot@users.noreply.github.com> Date: Sun, 22 Oct 2023 22:56:59 -0700 Subject: [PATCH 443/757] Add perfect cube binary search (#10477) * Add perfect cube binary search algorithm * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Add support for testing negative perfect cubes * Add TypeError check for invalid inputs * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- maths/perfect_cube.py | 43 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/maths/perfect_cube.py b/maths/perfect_cube.py index 9ad287e41e75..a732b7cce6c8 100644 --- a/maths/perfect_cube.py +++ b/maths/perfect_cube.py @@ -11,6 +11,45 @@ def perfect_cube(n: int) -> bool: return (val * val * val) == n +def perfect_cube_binary_search(n: int) -> bool: + """ + Check if a number is a perfect cube or not using binary search. + Time complexity : O(Log(n)) + Space complexity: O(1) + + >>> perfect_cube_binary_search(27) + True + >>> perfect_cube_binary_search(64) + True + >>> perfect_cube_binary_search(4) + False + >>> perfect_cube_binary_search("a") + Traceback (most recent call last): + ... + TypeError: perfect_cube_binary_search() only accepts integers + >>> perfect_cube_binary_search(0.1) + Traceback (most recent call last): + ... + TypeError: perfect_cube_binary_search() only accepts integers + """ + if not isinstance(n, int): + raise TypeError("perfect_cube_binary_search() only accepts integers") + if n < 0: + n = -n + left = 0 + right = n + while left <= right: + mid = left + (right - left) // 2 + if mid * mid * mid == n: + return True + elif mid * mid * mid < n: + left = mid + 1 + else: + right = mid - 1 + return False + + if __name__ == "__main__": - print(perfect_cube(27)) - print(perfect_cube(4)) + import doctest + + doctest.testmod() From a8b6bda993484b3be9fd541a10dd9ac9c4111dda Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Mon, 23 Oct 2023 03:31:30 -0400 Subject: [PATCH 444/757] Delete `arithmetic_analysis/` directory and relocate its contents (#10824) * Remove eval from arithmetic_analysis/newton_raphson.py * Relocate contents of arithmetic_analysis/ Delete the arithmetic_analysis/ directory and relocate its files because the purpose of the directory was always ill-defined. "Arithmetic analysis" isn't a field of math, and the directory's files contained algorithms for linear algebra, numerical analysis, and physics. Relocated the directory's linear algebra algorithms to linear_algebra/, its numerical analysis algorithms to a new subdirectory called maths/numerical_analysis/, and its single physics algorithm to physics/. * updating DIRECTORY.md --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 43 +- arithmetic_analysis/README.md | 7 - arithmetic_analysis/image_data/__init__.py | 0 .../gaussian_elimination.py | 0 .../jacobi_iteration_method.py | 406 +++++++++--------- .../lu_decomposition.py | 0 .../numerical_analysis}/bisection.py | 0 .../bisection_2.py} | 0 .../integration_by_simpson_approx.py | 0 .../numerical_analysis}/intersection.py | 0 .../nevilles_method.py | 0 .../newton_forward_interpolation.py | 0 .../numerical_analysis}/newton_method.py | 0 .../numerical_analysis}/newton_raphson.py | 35 +- .../newton_raphson_2.py} | 0 .../numerical_analysis}/newton_raphson_new.py | 0 .../numerical_integration.py | 0 maths/{ => numerical_analysis}/runge_kutta.py | 0 .../runge_kutta_fehlberg_45.py | 0 .../numerical_analysis}/secant_method.py | 0 .../{ => numerical_analysis}/simpson_rule.py | 0 maths/{ => numerical_analysis}/square_root.py | 0 .../image_data/2D_problems.jpg | Bin .../image_data/2D_problems_1.jpg | Bin .../image_data}/__init__.py | 0 .../in_static_equilibrium.py | 188 ++++---- 26 files changed, 335 insertions(+), 344 deletions(-) delete mode 100644 arithmetic_analysis/README.md delete mode 100644 arithmetic_analysis/image_data/__init__.py rename {arithmetic_analysis => linear_algebra}/gaussian_elimination.py (100%) rename {arithmetic_analysis => linear_algebra}/jacobi_iteration_method.py (96%) rename {arithmetic_analysis => linear_algebra}/lu_decomposition.py (100%) rename {arithmetic_analysis => maths/numerical_analysis}/bisection.py (100%) rename maths/{bisection.py => numerical_analysis/bisection_2.py} (100%) rename maths/{ => numerical_analysis}/integration_by_simpson_approx.py (100%) rename {arithmetic_analysis => maths/numerical_analysis}/intersection.py (100%) rename maths/{ => numerical_analysis}/nevilles_method.py (100%) rename {arithmetic_analysis => maths/numerical_analysis}/newton_forward_interpolation.py (100%) rename {arithmetic_analysis => maths/numerical_analysis}/newton_method.py (100%) rename {arithmetic_analysis => maths/numerical_analysis}/newton_raphson.py (61%) rename maths/{newton_raphson.py => numerical_analysis/newton_raphson_2.py} (100%) rename {arithmetic_analysis => maths/numerical_analysis}/newton_raphson_new.py (100%) rename maths/{ => numerical_analysis}/numerical_integration.py (100%) rename maths/{ => numerical_analysis}/runge_kutta.py (100%) rename maths/{ => numerical_analysis}/runge_kutta_fehlberg_45.py (100%) rename {arithmetic_analysis => maths/numerical_analysis}/secant_method.py (100%) rename maths/{ => numerical_analysis}/simpson_rule.py (100%) rename maths/{ => numerical_analysis}/square_root.py (100%) rename {arithmetic_analysis => physics}/image_data/2D_problems.jpg (100%) rename {arithmetic_analysis => physics}/image_data/2D_problems_1.jpg (100%) rename {arithmetic_analysis => physics/image_data}/__init__.py (100%) rename {arithmetic_analysis => physics}/in_static_equilibrium.py (96%) diff --git a/DIRECTORY.md b/DIRECTORY.md index c07e1550d1eb..1e3711fe8dda 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -1,17 +1,4 @@ -## Arithmetic Analysis - * [Bisection](arithmetic_analysis/bisection.py) - * [Gaussian Elimination](arithmetic_analysis/gaussian_elimination.py) - * [In Static Equilibrium](arithmetic_analysis/in_static_equilibrium.py) - * [Intersection](arithmetic_analysis/intersection.py) - * [Jacobi Iteration Method](arithmetic_analysis/jacobi_iteration_method.py) - * [Lu Decomposition](arithmetic_analysis/lu_decomposition.py) - * [Newton Forward Interpolation](arithmetic_analysis/newton_forward_interpolation.py) - * [Newton Method](arithmetic_analysis/newton_method.py) - * [Newton Raphson](arithmetic_analysis/newton_raphson.py) - * [Newton Raphson New](arithmetic_analysis/newton_raphson_new.py) - * [Secant Method](arithmetic_analysis/secant_method.py) - ## Audio Filters * [Butterworth Filter](audio_filters/butterworth_filter.py) * [Iir Filter](audio_filters/iir_filter.py) @@ -520,6 +507,9 @@ * [Test Knapsack](knapsack/tests/test_knapsack.py) ## Linear Algebra + * [Gaussian Elimination](linear_algebra/gaussian_elimination.py) + * [Jacobi Iteration Method](linear_algebra/jacobi_iteration_method.py) + * [Lu Decomposition](linear_algebra/lu_decomposition.py) * Src * [Conjugate Gradient](linear_algebra/src/conjugate_gradient.py) * [Lib](linear_algebra/src/lib.py) @@ -583,7 +573,6 @@ * [Binary Multiplication](maths/binary_multiplication.py) * [Binomial Coefficient](maths/binomial_coefficient.py) * [Binomial Distribution](maths/binomial_distribution.py) - * [Bisection](maths/bisection.py) * [Ceil](maths/ceil.py) * [Chebyshev Distance](maths/chebyshev_distance.py) * [Check Polygon](maths/check_polygon.py) @@ -617,7 +606,6 @@ * [Germain Primes](maths/germain_primes.py) * [Greatest Common Divisor](maths/greatest_common_divisor.py) * [Hardy Ramanujanalgo](maths/hardy_ramanujanalgo.py) - * [Integration By Simpson Approx](maths/integration_by_simpson_approx.py) * [Interquartile Range](maths/interquartile_range.py) * [Is Int Palindrome](maths/is_int_palindrome.py) * [Is Ip V4 Address Valid](maths/is_ip_v4_address_valid.py) @@ -644,10 +632,24 @@ * [Modular Exponential](maths/modular_exponential.py) * [Monte Carlo](maths/monte_carlo.py) * [Monte Carlo Dice](maths/monte_carlo_dice.py) - * [Nevilles Method](maths/nevilles_method.py) - * [Newton Raphson](maths/newton_raphson.py) * [Number Of Digits](maths/number_of_digits.py) - * [Numerical Integration](maths/numerical_integration.py) + * Numerical Analysis + * [Bisection](maths/numerical_analysis/bisection.py) + * [Bisection 2](maths/numerical_analysis/bisection_2.py) + * [Integration By Simpson Approx](maths/numerical_analysis/integration_by_simpson_approx.py) + * [Intersection](maths/numerical_analysis/intersection.py) + * [Nevilles Method](maths/numerical_analysis/nevilles_method.py) + * [Newton Forward Interpolation](maths/numerical_analysis/newton_forward_interpolation.py) + * [Newton Method](maths/numerical_analysis/newton_method.py) + * [Newton Raphson](maths/numerical_analysis/newton_raphson.py) + * [Newton Raphson 2](maths/numerical_analysis/newton_raphson_2.py) + * [Newton Raphson New](maths/numerical_analysis/newton_raphson_new.py) + * [Numerical Integration](maths/numerical_analysis/numerical_integration.py) + * [Runge Kutta](maths/numerical_analysis/runge_kutta.py) + * [Runge Kutta Fehlberg 45](maths/numerical_analysis/runge_kutta_fehlberg_45.py) + * [Secant Method](maths/numerical_analysis/secant_method.py) + * [Simpson Rule](maths/numerical_analysis/simpson_rule.py) + * [Square Root](maths/numerical_analysis/square_root.py) * [Odd Sieve](maths/odd_sieve.py) * [Perfect Cube](maths/perfect_cube.py) * [Perfect Number](maths/perfect_number.py) @@ -673,8 +675,6 @@ * [Radians](maths/radians.py) * [Radix2 Fft](maths/radix2_fft.py) * [Remove Digit](maths/remove_digit.py) - * [Runge Kutta](maths/runge_kutta.py) - * [Runge Kutta Fehlberg 45](maths/runge_kutta_fehlberg_45.py) * [Segmented Sieve](maths/segmented_sieve.py) * Series * [Arithmetic](maths/series/arithmetic.py) @@ -687,7 +687,6 @@ * [Sieve Of Eratosthenes](maths/sieve_of_eratosthenes.py) * [Sigmoid](maths/sigmoid.py) * [Signum](maths/signum.py) - * [Simpson Rule](maths/simpson_rule.py) * [Simultaneous Linear Equation Solver](maths/simultaneous_linear_equation_solver.py) * [Sin](maths/sin.py) * [Sock Merchant](maths/sock_merchant.py) @@ -709,7 +708,6 @@ * [Proth Number](maths/special_numbers/proth_number.py) * [Ugly Numbers](maths/special_numbers/ugly_numbers.py) * [Weird Number](maths/special_numbers/weird_number.py) - * [Square Root](maths/square_root.py) * [Sum Of Arithmetic Series](maths/sum_of_arithmetic_series.py) * [Sum Of Digits](maths/sum_of_digits.py) * [Sum Of Geometric Progression](maths/sum_of_geometric_progression.py) @@ -812,6 +810,7 @@ * [Horizontal Projectile Motion](physics/horizontal_projectile_motion.py) * [Hubble Parameter](physics/hubble_parameter.py) * [Ideal Gas Law](physics/ideal_gas_law.py) + * [In Static Equilibrium](physics/in_static_equilibrium.py) * [Kinetic Energy](physics/kinetic_energy.py) * [Lorentz Transformation Four Vector](physics/lorentz_transformation_four_vector.py) * [Malus Law](physics/malus_law.py) diff --git a/arithmetic_analysis/README.md b/arithmetic_analysis/README.md deleted file mode 100644 index 45cf321eb6ad..000000000000 --- a/arithmetic_analysis/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# Arithmetic analysis - -Arithmetic analysis is a branch of mathematics that deals with solving linear equations. - -* -* -* diff --git a/arithmetic_analysis/image_data/__init__.py b/arithmetic_analysis/image_data/__init__.py deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/arithmetic_analysis/gaussian_elimination.py b/linear_algebra/gaussian_elimination.py similarity index 100% rename from arithmetic_analysis/gaussian_elimination.py rename to linear_algebra/gaussian_elimination.py diff --git a/arithmetic_analysis/jacobi_iteration_method.py b/linear_algebra/jacobi_iteration_method.py similarity index 96% rename from arithmetic_analysis/jacobi_iteration_method.py rename to linear_algebra/jacobi_iteration_method.py index 44c52dd44640..8c91a19ef1b0 100644 --- a/arithmetic_analysis/jacobi_iteration_method.py +++ b/linear_algebra/jacobi_iteration_method.py @@ -1,203 +1,203 @@ -""" -Jacobi Iteration Method - https://en.wikipedia.org/wiki/Jacobi_method -""" -from __future__ import annotations - -import numpy as np -from numpy import float64 -from numpy.typing import NDArray - - -# Method to find solution of system of linear equations -def jacobi_iteration_method( - coefficient_matrix: NDArray[float64], - constant_matrix: NDArray[float64], - init_val: list[float], - iterations: int, -) -> list[float]: - """ - Jacobi Iteration Method: - An iterative algorithm to determine the solutions of strictly diagonally dominant - system of linear equations - - 4x1 + x2 + x3 = 2 - x1 + 5x2 + 2x3 = -6 - x1 + 2x2 + 4x3 = -4 - - x_init = [0.5, -0.5 , -0.5] - - Examples: - - >>> coefficient = np.array([[4, 1, 1], [1, 5, 2], [1, 2, 4]]) - >>> constant = np.array([[2], [-6], [-4]]) - >>> init_val = [0.5, -0.5, -0.5] - >>> iterations = 3 - >>> jacobi_iteration_method(coefficient, constant, init_val, iterations) - [0.909375, -1.14375, -0.7484375] - - - >>> coefficient = np.array([[4, 1, 1], [1, 5, 2]]) - >>> constant = np.array([[2], [-6], [-4]]) - >>> init_val = [0.5, -0.5, -0.5] - >>> iterations = 3 - >>> jacobi_iteration_method(coefficient, constant, init_val, iterations) - Traceback (most recent call last): - ... - ValueError: Coefficient matrix dimensions must be nxn but received 2x3 - - >>> coefficient = np.array([[4, 1, 1], [1, 5, 2], [1, 2, 4]]) - >>> constant = np.array([[2], [-6]]) - >>> init_val = [0.5, -0.5, -0.5] - >>> iterations = 3 - >>> jacobi_iteration_method( - ... coefficient, constant, init_val, iterations - ... ) # doctest: +NORMALIZE_WHITESPACE - Traceback (most recent call last): - ... - ValueError: Coefficient and constant matrices dimensions must be nxn and nx1 but - received 3x3 and 2x1 - - >>> coefficient = np.array([[4, 1, 1], [1, 5, 2], [1, 2, 4]]) - >>> constant = np.array([[2], [-6], [-4]]) - >>> init_val = [0.5, -0.5] - >>> iterations = 3 - >>> jacobi_iteration_method( - ... coefficient, constant, init_val, iterations - ... ) # doctest: +NORMALIZE_WHITESPACE - Traceback (most recent call last): - ... - ValueError: Number of initial values must be equal to number of rows in coefficient - matrix but received 2 and 3 - - >>> coefficient = np.array([[4, 1, 1], [1, 5, 2], [1, 2, 4]]) - >>> constant = np.array([[2], [-6], [-4]]) - >>> init_val = [0.5, -0.5, -0.5] - >>> iterations = 0 - >>> jacobi_iteration_method(coefficient, constant, init_val, iterations) - Traceback (most recent call last): - ... - ValueError: Iterations must be at least 1 - """ - - rows1, cols1 = coefficient_matrix.shape - rows2, cols2 = constant_matrix.shape - - if rows1 != cols1: - msg = f"Coefficient matrix dimensions must be nxn but received {rows1}x{cols1}" - raise ValueError(msg) - - if cols2 != 1: - msg = f"Constant matrix must be nx1 but received {rows2}x{cols2}" - raise ValueError(msg) - - if rows1 != rows2: - msg = ( - "Coefficient and constant matrices dimensions must be nxn and nx1 but " - f"received {rows1}x{cols1} and {rows2}x{cols2}" - ) - raise ValueError(msg) - - if len(init_val) != rows1: - msg = ( - "Number of initial values must be equal to number of rows in coefficient " - f"matrix but received {len(init_val)} and {rows1}" - ) - raise ValueError(msg) - - if iterations <= 0: - raise ValueError("Iterations must be at least 1") - - table: NDArray[float64] = np.concatenate( - (coefficient_matrix, constant_matrix), axis=1 - ) - - rows, cols = table.shape - - strictly_diagonally_dominant(table) - - """ - # Iterates the whole matrix for given number of times - for _ in range(iterations): - new_val = [] - for row in range(rows): - temp = 0 - for col in range(cols): - if col == row: - denom = table[row][col] - elif col == cols - 1: - val = table[row][col] - else: - temp += (-1) * table[row][col] * init_val[col] - temp = (temp + val) / denom - new_val.append(temp) - init_val = new_val - """ - - # denominator - a list of values along the diagonal - denominator = np.diag(coefficient_matrix) - - # val_last - values of the last column of the table array - val_last = table[:, -1] - - # masks - boolean mask of all strings without diagonal - # elements array coefficient_matrix - masks = ~np.eye(coefficient_matrix.shape[0], dtype=bool) - - # no_diagonals - coefficient_matrix array values without diagonal elements - no_diagonals = coefficient_matrix[masks].reshape(-1, rows - 1) - - # Here we get 'i_col' - these are the column numbers, for each row - # without diagonal elements, except for the last column. - i_row, i_col = np.where(masks) - ind = i_col.reshape(-1, rows - 1) - - #'i_col' is converted to a two-dimensional list 'ind', which will be - # used to make selections from 'init_val' ('arr' array see below). - - # Iterates the whole matrix for given number of times - for _ in range(iterations): - arr = np.take(init_val, ind) - sum_product_rows = np.sum((-1) * no_diagonals * arr, axis=1) - new_val = (sum_product_rows + val_last) / denominator - init_val = new_val - - return new_val.tolist() - - -# Checks if the given matrix is strictly diagonally dominant -def strictly_diagonally_dominant(table: NDArray[float64]) -> bool: - """ - >>> table = np.array([[4, 1, 1, 2], [1, 5, 2, -6], [1, 2, 4, -4]]) - >>> strictly_diagonally_dominant(table) - True - - >>> table = np.array([[4, 1, 1, 2], [1, 5, 2, -6], [1, 2, 3, -4]]) - >>> strictly_diagonally_dominant(table) - Traceback (most recent call last): - ... - ValueError: Coefficient matrix is not strictly diagonally dominant - """ - - rows, cols = table.shape - - is_diagonally_dominant = True - - for i in range(rows): - total = 0 - for j in range(cols - 1): - if i == j: - continue - else: - total += table[i][j] - - if table[i][i] <= total: - raise ValueError("Coefficient matrix is not strictly diagonally dominant") - - return is_diagonally_dominant - - -# Test Cases -if __name__ == "__main__": - import doctest - - doctest.testmod() +""" +Jacobi Iteration Method - https://en.wikipedia.org/wiki/Jacobi_method +""" +from __future__ import annotations + +import numpy as np +from numpy import float64 +from numpy.typing import NDArray + + +# Method to find solution of system of linear equations +def jacobi_iteration_method( + coefficient_matrix: NDArray[float64], + constant_matrix: NDArray[float64], + init_val: list[float], + iterations: int, +) -> list[float]: + """ + Jacobi Iteration Method: + An iterative algorithm to determine the solutions of strictly diagonally dominant + system of linear equations + + 4x1 + x2 + x3 = 2 + x1 + 5x2 + 2x3 = -6 + x1 + 2x2 + 4x3 = -4 + + x_init = [0.5, -0.5 , -0.5] + + Examples: + + >>> coefficient = np.array([[4, 1, 1], [1, 5, 2], [1, 2, 4]]) + >>> constant = np.array([[2], [-6], [-4]]) + >>> init_val = [0.5, -0.5, -0.5] + >>> iterations = 3 + >>> jacobi_iteration_method(coefficient, constant, init_val, iterations) + [0.909375, -1.14375, -0.7484375] + + + >>> coefficient = np.array([[4, 1, 1], [1, 5, 2]]) + >>> constant = np.array([[2], [-6], [-4]]) + >>> init_val = [0.5, -0.5, -0.5] + >>> iterations = 3 + >>> jacobi_iteration_method(coefficient, constant, init_val, iterations) + Traceback (most recent call last): + ... + ValueError: Coefficient matrix dimensions must be nxn but received 2x3 + + >>> coefficient = np.array([[4, 1, 1], [1, 5, 2], [1, 2, 4]]) + >>> constant = np.array([[2], [-6]]) + >>> init_val = [0.5, -0.5, -0.5] + >>> iterations = 3 + >>> jacobi_iteration_method( + ... coefficient, constant, init_val, iterations + ... ) # doctest: +NORMALIZE_WHITESPACE + Traceback (most recent call last): + ... + ValueError: Coefficient and constant matrices dimensions must be nxn and nx1 but + received 3x3 and 2x1 + + >>> coefficient = np.array([[4, 1, 1], [1, 5, 2], [1, 2, 4]]) + >>> constant = np.array([[2], [-6], [-4]]) + >>> init_val = [0.5, -0.5] + >>> iterations = 3 + >>> jacobi_iteration_method( + ... coefficient, constant, init_val, iterations + ... ) # doctest: +NORMALIZE_WHITESPACE + Traceback (most recent call last): + ... + ValueError: Number of initial values must be equal to number of rows in coefficient + matrix but received 2 and 3 + + >>> coefficient = np.array([[4, 1, 1], [1, 5, 2], [1, 2, 4]]) + >>> constant = np.array([[2], [-6], [-4]]) + >>> init_val = [0.5, -0.5, -0.5] + >>> iterations = 0 + >>> jacobi_iteration_method(coefficient, constant, init_val, iterations) + Traceback (most recent call last): + ... + ValueError: Iterations must be at least 1 + """ + + rows1, cols1 = coefficient_matrix.shape + rows2, cols2 = constant_matrix.shape + + if rows1 != cols1: + msg = f"Coefficient matrix dimensions must be nxn but received {rows1}x{cols1}" + raise ValueError(msg) + + if cols2 != 1: + msg = f"Constant matrix must be nx1 but received {rows2}x{cols2}" + raise ValueError(msg) + + if rows1 != rows2: + msg = ( + "Coefficient and constant matrices dimensions must be nxn and nx1 but " + f"received {rows1}x{cols1} and {rows2}x{cols2}" + ) + raise ValueError(msg) + + if len(init_val) != rows1: + msg = ( + "Number of initial values must be equal to number of rows in coefficient " + f"matrix but received {len(init_val)} and {rows1}" + ) + raise ValueError(msg) + + if iterations <= 0: + raise ValueError("Iterations must be at least 1") + + table: NDArray[float64] = np.concatenate( + (coefficient_matrix, constant_matrix), axis=1 + ) + + rows, cols = table.shape + + strictly_diagonally_dominant(table) + + """ + # Iterates the whole matrix for given number of times + for _ in range(iterations): + new_val = [] + for row in range(rows): + temp = 0 + for col in range(cols): + if col == row: + denom = table[row][col] + elif col == cols - 1: + val = table[row][col] + else: + temp += (-1) * table[row][col] * init_val[col] + temp = (temp + val) / denom + new_val.append(temp) + init_val = new_val + """ + + # denominator - a list of values along the diagonal + denominator = np.diag(coefficient_matrix) + + # val_last - values of the last column of the table array + val_last = table[:, -1] + + # masks - boolean mask of all strings without diagonal + # elements array coefficient_matrix + masks = ~np.eye(coefficient_matrix.shape[0], dtype=bool) + + # no_diagonals - coefficient_matrix array values without diagonal elements + no_diagonals = coefficient_matrix[masks].reshape(-1, rows - 1) + + # Here we get 'i_col' - these are the column numbers, for each row + # without diagonal elements, except for the last column. + i_row, i_col = np.where(masks) + ind = i_col.reshape(-1, rows - 1) + + #'i_col' is converted to a two-dimensional list 'ind', which will be + # used to make selections from 'init_val' ('arr' array see below). + + # Iterates the whole matrix for given number of times + for _ in range(iterations): + arr = np.take(init_val, ind) + sum_product_rows = np.sum((-1) * no_diagonals * arr, axis=1) + new_val = (sum_product_rows + val_last) / denominator + init_val = new_val + + return new_val.tolist() + + +# Checks if the given matrix is strictly diagonally dominant +def strictly_diagonally_dominant(table: NDArray[float64]) -> bool: + """ + >>> table = np.array([[4, 1, 1, 2], [1, 5, 2, -6], [1, 2, 4, -4]]) + >>> strictly_diagonally_dominant(table) + True + + >>> table = np.array([[4, 1, 1, 2], [1, 5, 2, -6], [1, 2, 3, -4]]) + >>> strictly_diagonally_dominant(table) + Traceback (most recent call last): + ... + ValueError: Coefficient matrix is not strictly diagonally dominant + """ + + rows, cols = table.shape + + is_diagonally_dominant = True + + for i in range(rows): + total = 0 + for j in range(cols - 1): + if i == j: + continue + else: + total += table[i][j] + + if table[i][i] <= total: + raise ValueError("Coefficient matrix is not strictly diagonally dominant") + + return is_diagonally_dominant + + +# Test Cases +if __name__ == "__main__": + import doctest + + doctest.testmod() diff --git a/arithmetic_analysis/lu_decomposition.py b/linear_algebra/lu_decomposition.py similarity index 100% rename from arithmetic_analysis/lu_decomposition.py rename to linear_algebra/lu_decomposition.py diff --git a/arithmetic_analysis/bisection.py b/maths/numerical_analysis/bisection.py similarity index 100% rename from arithmetic_analysis/bisection.py rename to maths/numerical_analysis/bisection.py diff --git a/maths/bisection.py b/maths/numerical_analysis/bisection_2.py similarity index 100% rename from maths/bisection.py rename to maths/numerical_analysis/bisection_2.py diff --git a/maths/integration_by_simpson_approx.py b/maths/numerical_analysis/integration_by_simpson_approx.py similarity index 100% rename from maths/integration_by_simpson_approx.py rename to maths/numerical_analysis/integration_by_simpson_approx.py diff --git a/arithmetic_analysis/intersection.py b/maths/numerical_analysis/intersection.py similarity index 100% rename from arithmetic_analysis/intersection.py rename to maths/numerical_analysis/intersection.py diff --git a/maths/nevilles_method.py b/maths/numerical_analysis/nevilles_method.py similarity index 100% rename from maths/nevilles_method.py rename to maths/numerical_analysis/nevilles_method.py diff --git a/arithmetic_analysis/newton_forward_interpolation.py b/maths/numerical_analysis/newton_forward_interpolation.py similarity index 100% rename from arithmetic_analysis/newton_forward_interpolation.py rename to maths/numerical_analysis/newton_forward_interpolation.py diff --git a/arithmetic_analysis/newton_method.py b/maths/numerical_analysis/newton_method.py similarity index 100% rename from arithmetic_analysis/newton_method.py rename to maths/numerical_analysis/newton_method.py diff --git a/arithmetic_analysis/newton_raphson.py b/maths/numerical_analysis/newton_raphson.py similarity index 61% rename from arithmetic_analysis/newton_raphson.py rename to maths/numerical_analysis/newton_raphson.py index 1b90ad4177f6..8491ca8003bc 100644 --- a/arithmetic_analysis/newton_raphson.py +++ b/maths/numerical_analysis/newton_raphson.py @@ -5,42 +5,41 @@ from __future__ import annotations from decimal import Decimal -from math import * # noqa: F403 -from sympy import diff +from sympy import diff, lambdify, symbols -def newton_raphson( - func: str, a: float | Decimal, precision: float = 10**-10 -) -> float: +def newton_raphson(func: str, a: float | Decimal, precision: float = 1e-10) -> float: """Finds root from the point 'a' onwards by Newton-Raphson method >>> newton_raphson("sin(x)", 2) 3.1415926536808043 - >>> newton_raphson("x**2 - 5*x +2", 0.4) + >>> newton_raphson("x**2 - 5*x + 2", 0.4) 0.4384471871911695 >>> newton_raphson("x**2 - 5", 0.1) 2.23606797749979 - >>> newton_raphson("log(x)- 1", 2) + >>> newton_raphson("log(x) - 1", 2) 2.718281828458938 """ - x = a + x = symbols("x") + f = lambdify(x, func, "math") + f_derivative = lambdify(x, diff(func), "math") + x_curr = a while True: - x = Decimal(x) - ( - Decimal(eval(func)) / Decimal(eval(str(diff(func)))) # noqa: S307 - ) - # This number dictates the accuracy of the answer - if abs(eval(func)) < precision: # noqa: S307 - return float(x) + x_curr = Decimal(x_curr) - Decimal(f(x_curr)) / Decimal(f_derivative(x_curr)) + if abs(f(x_curr)) < precision: + return float(x_curr) -# Let's Execute if __name__ == "__main__": - # Find root of trigonometric function + import doctest + + doctest.testmod() + # Find value of pi print(f"The root of sin(x) = 0 is {newton_raphson('sin(x)', 2)}") # Find root of polynomial print(f"The root of x**2 - 5*x + 2 = 0 is {newton_raphson('x**2 - 5*x + 2', 0.4)}") - # Find Square Root of 5 + # Find value of e print(f"The root of log(x) - 1 = 0 is {newton_raphson('log(x) - 1', 2)}") - # Exponential Roots + # Find root of exponential function print(f"The root of exp(x) - 1 = 0 is {newton_raphson('exp(x) - 1', 0)}") diff --git a/maths/newton_raphson.py b/maths/numerical_analysis/newton_raphson_2.py similarity index 100% rename from maths/newton_raphson.py rename to maths/numerical_analysis/newton_raphson_2.py diff --git a/arithmetic_analysis/newton_raphson_new.py b/maths/numerical_analysis/newton_raphson_new.py similarity index 100% rename from arithmetic_analysis/newton_raphson_new.py rename to maths/numerical_analysis/newton_raphson_new.py diff --git a/maths/numerical_integration.py b/maths/numerical_analysis/numerical_integration.py similarity index 100% rename from maths/numerical_integration.py rename to maths/numerical_analysis/numerical_integration.py diff --git a/maths/runge_kutta.py b/maths/numerical_analysis/runge_kutta.py similarity index 100% rename from maths/runge_kutta.py rename to maths/numerical_analysis/runge_kutta.py diff --git a/maths/runge_kutta_fehlberg_45.py b/maths/numerical_analysis/runge_kutta_fehlberg_45.py similarity index 100% rename from maths/runge_kutta_fehlberg_45.py rename to maths/numerical_analysis/runge_kutta_fehlberg_45.py diff --git a/arithmetic_analysis/secant_method.py b/maths/numerical_analysis/secant_method.py similarity index 100% rename from arithmetic_analysis/secant_method.py rename to maths/numerical_analysis/secant_method.py diff --git a/maths/simpson_rule.py b/maths/numerical_analysis/simpson_rule.py similarity index 100% rename from maths/simpson_rule.py rename to maths/numerical_analysis/simpson_rule.py diff --git a/maths/square_root.py b/maths/numerical_analysis/square_root.py similarity index 100% rename from maths/square_root.py rename to maths/numerical_analysis/square_root.py diff --git a/arithmetic_analysis/image_data/2D_problems.jpg b/physics/image_data/2D_problems.jpg similarity index 100% rename from arithmetic_analysis/image_data/2D_problems.jpg rename to physics/image_data/2D_problems.jpg diff --git a/arithmetic_analysis/image_data/2D_problems_1.jpg b/physics/image_data/2D_problems_1.jpg similarity index 100% rename from arithmetic_analysis/image_data/2D_problems_1.jpg rename to physics/image_data/2D_problems_1.jpg diff --git a/arithmetic_analysis/__init__.py b/physics/image_data/__init__.py similarity index 100% rename from arithmetic_analysis/__init__.py rename to physics/image_data/__init__.py diff --git a/arithmetic_analysis/in_static_equilibrium.py b/physics/in_static_equilibrium.py similarity index 96% rename from arithmetic_analysis/in_static_equilibrium.py rename to physics/in_static_equilibrium.py index 7aaecf174a5e..d56299f60858 100644 --- a/arithmetic_analysis/in_static_equilibrium.py +++ b/physics/in_static_equilibrium.py @@ -1,94 +1,94 @@ -""" -Checks if a system of forces is in static equilibrium. -""" -from __future__ import annotations - -from numpy import array, cos, cross, float64, radians, sin -from numpy.typing import NDArray - - -def polar_force( - magnitude: float, angle: float, radian_mode: bool = False -) -> list[float]: - """ - Resolves force along rectangular components. - (force, angle) => (force_x, force_y) - >>> import math - >>> force = polar_force(10, 45) - >>> math.isclose(force[0], 7.071067811865477) - True - >>> math.isclose(force[1], 7.0710678118654755) - True - >>> force = polar_force(10, 3.14, radian_mode=True) - >>> math.isclose(force[0], -9.999987317275396) - True - >>> math.isclose(force[1], 0.01592652916486828) - True - """ - if radian_mode: - return [magnitude * cos(angle), magnitude * sin(angle)] - return [magnitude * cos(radians(angle)), magnitude * sin(radians(angle))] - - -def in_static_equilibrium( - forces: NDArray[float64], location: NDArray[float64], eps: float = 10**-1 -) -> bool: - """ - Check if a system is in equilibrium. - It takes two numpy.array objects. - forces ==> [ - [force1_x, force1_y], - [force2_x, force2_y], - ....] - location ==> [ - [x1, y1], - [x2, y2], - ....] - >>> force = array([[1, 1], [-1, 2]]) - >>> location = array([[1, 0], [10, 0]]) - >>> in_static_equilibrium(force, location) - False - """ - # summation of moments is zero - moments: NDArray[float64] = cross(location, forces) - sum_moments: float = sum(moments) - return abs(sum_moments) < eps - - -if __name__ == "__main__": - # Test to check if it works - forces = array( - [ - polar_force(718.4, 180 - 30), - polar_force(879.54, 45), - polar_force(100, -90), - ] - ) - - location: NDArray[float64] = array([[0, 0], [0, 0], [0, 0]]) - - assert in_static_equilibrium(forces, location) - - # Problem 1 in image_data/2D_problems.jpg - forces = array( - [ - polar_force(30 * 9.81, 15), - polar_force(215, 180 - 45), - polar_force(264, 90 - 30), - ] - ) - - location = array([[0, 0], [0, 0], [0, 0]]) - - assert in_static_equilibrium(forces, location) - - # Problem in image_data/2D_problems_1.jpg - forces = array([[0, -2000], [0, -1200], [0, 15600], [0, -12400]]) - - location = array([[0, 0], [6, 0], [10, 0], [12, 0]]) - - assert in_static_equilibrium(forces, location) - - import doctest - - doctest.testmod() +""" +Checks if a system of forces is in static equilibrium. +""" +from __future__ import annotations + +from numpy import array, cos, cross, float64, radians, sin +from numpy.typing import NDArray + + +def polar_force( + magnitude: float, angle: float, radian_mode: bool = False +) -> list[float]: + """ + Resolves force along rectangular components. + (force, angle) => (force_x, force_y) + >>> import math + >>> force = polar_force(10, 45) + >>> math.isclose(force[0], 7.071067811865477) + True + >>> math.isclose(force[1], 7.0710678118654755) + True + >>> force = polar_force(10, 3.14, radian_mode=True) + >>> math.isclose(force[0], -9.999987317275396) + True + >>> math.isclose(force[1], 0.01592652916486828) + True + """ + if radian_mode: + return [magnitude * cos(angle), magnitude * sin(angle)] + return [magnitude * cos(radians(angle)), magnitude * sin(radians(angle))] + + +def in_static_equilibrium( + forces: NDArray[float64], location: NDArray[float64], eps: float = 10**-1 +) -> bool: + """ + Check if a system is in equilibrium. + It takes two numpy.array objects. + forces ==> [ + [force1_x, force1_y], + [force2_x, force2_y], + ....] + location ==> [ + [x1, y1], + [x2, y2], + ....] + >>> force = array([[1, 1], [-1, 2]]) + >>> location = array([[1, 0], [10, 0]]) + >>> in_static_equilibrium(force, location) + False + """ + # summation of moments is zero + moments: NDArray[float64] = cross(location, forces) + sum_moments: float = sum(moments) + return abs(sum_moments) < eps + + +if __name__ == "__main__": + # Test to check if it works + forces = array( + [ + polar_force(718.4, 180 - 30), + polar_force(879.54, 45), + polar_force(100, -90), + ] + ) + + location: NDArray[float64] = array([[0, 0], [0, 0], [0, 0]]) + + assert in_static_equilibrium(forces, location) + + # Problem 1 in image_data/2D_problems.jpg + forces = array( + [ + polar_force(30 * 9.81, 15), + polar_force(215, 180 - 45), + polar_force(264, 90 - 30), + ] + ) + + location = array([[0, 0], [0, 0], [0, 0]]) + + assert in_static_equilibrium(forces, location) + + # Problem in image_data/2D_problems_1.jpg + forces = array([[0, -2000], [0, -1200], [0, 15600], [0, -12400]]) + + location = array([[0, 0], [6, 0], [10, 0], [12, 0]]) + + assert in_static_equilibrium(forces, location) + + import doctest + + doctest.testmod() From 417b7edfc3fdfe9534a56e3e7d0a368f76b3edb4 Mon Sep 17 00:00:00 2001 From: Krishna-singhal <65902764+Krishna-Singhal@users.noreply.github.com> Date: Mon, 23 Oct 2023 13:21:27 +0530 Subject: [PATCH 445/757] code enhancement in `sort.double_sort` (#10798) * don't need to return list because list is mutable * Don't need to return list as list is mutable * use advantage of python in swapping * filter blank inputs from input list * minor changes * minor mistake * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * more readable * Update double_sort.py * last fixes * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Apply suggestions from code review * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- sorts/double_sort.py | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/sorts/double_sort.py b/sorts/double_sort.py index a19641d94752..bd5fdca1e63c 100644 --- a/sorts/double_sort.py +++ b/sorts/double_sort.py @@ -1,4 +1,7 @@ -def double_sort(lst): +from typing import Any + + +def double_sort(collection: list[Any]) -> list[Any]: """This sorting algorithm sorts an array using the principle of bubble sort, but does it both from left to right and right to left. Hence, it's called "Double sort" @@ -14,29 +17,28 @@ def double_sort(lst): >>> double_sort([-3, 10, 16, -42, 29]) == sorted([-3, 10, 16, -42, 29]) True """ - no_of_elements = len(lst) + no_of_elements = len(collection) for _ in range( int(((no_of_elements - 1) / 2) + 1) ): # we don't need to traverse to end of list as for j in range(no_of_elements - 1): - if ( - lst[j + 1] < lst[j] - ): # applying bubble sort algorithm from left to right (or forwards) - temp = lst[j + 1] - lst[j + 1] = lst[j] - lst[j] = temp - if ( - lst[no_of_elements - 1 - j] < lst[no_of_elements - 2 - j] - ): # applying bubble sort algorithm from right to left (or backwards) - temp = lst[no_of_elements - 1 - j] - lst[no_of_elements - 1 - j] = lst[no_of_elements - 2 - j] - lst[no_of_elements - 2 - j] = temp - return lst + # apply the bubble sort algorithm from left to right (or forwards) + if collection[j + 1] < collection[j]: + collection[j], collection[j + 1] = collection[j + 1], collection[j] + # apply the bubble sort algorithm from right to left (or backwards) + if collection[no_of_elements - 1 - j] < collection[no_of_elements - 2 - j]: + ( + collection[no_of_elements - 1 - j], + collection[no_of_elements - 2 - j], + ) = ( + collection[no_of_elements - 2 - j], + collection[no_of_elements - 1 - j], + ) + return collection if __name__ == "__main__": - print("enter the list to be sorted") - lst = [int(x) for x in input().split()] # inputing elements of the list in one line - sorted_lst = double_sort(lst) + # allow the user to input the elements of the list on one line + unsorted = [int(x) for x in input("Enter the list to be sorted: ").split() if x] print("the sorted list is") - print(sorted_lst) + print(f"{double_sort(unsorted) = }") From d051db1f14cbb0edd2b0db1e4edef76cce6c7823 Mon Sep 17 00:00:00 2001 From: Berat Osman Demiralay Date: Mon, 23 Oct 2023 16:25:07 +0300 Subject: [PATCH 446/757] Add Simple Moving Average (SMA) Calculation (#9300) * Add Simple Moving Average (SMA) Calculation This commit adds a Python script for calculating the Simple Moving Average (SMA) of a time series data. The script also includes a doctest that verifies the correctness of the SMA calculations for a sample dataset. Usage: - Run the script with your own time series data and specify the window size for SMA calculations. * Update financial/simple_moving_average.py Co-authored-by: Tianyi Zheng * Update financial/simple_moving_average.py Co-authored-by: Tianyi Zheng * Update financial/simple_moving_average.py Co-authored-by: Tianyi Zheng * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update simple_moving_average.py * Update financial/simple_moving_average.py * Update simple_moving_average.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: Tianyi Zheng Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- financial/simple_moving_average.py | 68 ++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 financial/simple_moving_average.py diff --git a/financial/simple_moving_average.py b/financial/simple_moving_average.py new file mode 100644 index 000000000000..d5d68ffd3dab --- /dev/null +++ b/financial/simple_moving_average.py @@ -0,0 +1,68 @@ +""" +The Simple Moving Average (SMA) is a statistical calculation used to analyze data points +by creating a constantly updated average price over a specific time period. +In finance, SMA is often used in time series analysis to smooth out price data +and identify trends. + +Reference: https://en.wikipedia.org/wiki/Moving_average +""" +from collections.abc import Sequence + + +def simple_moving_average( + data: Sequence[float], window_size: int +) -> list[float | None]: + """ + Calculate the simple moving average (SMA) for some given time series data. + + :param data: A list of numerical data points. + :param window_size: An integer representing the size of the SMA window. + :return: A list of SMA values with the same length as the input data. + + Examples: + >>> sma = simple_moving_average([10, 12, 15, 13, 14, 16, 18, 17, 19, 21], 3) + >>> [round(value, 2) if value is not None else None for value in sma] + [None, None, 12.33, 13.33, 14.0, 14.33, 16.0, 17.0, 18.0, 19.0] + >>> simple_moving_average([10, 12, 15], 5) + [None, None, None] + >>> simple_moving_average([10, 12, 15, 13, 14, 16, 18, 17, 19, 21], 0) + Traceback (most recent call last): + ... + ValueError: Window size must be a positive integer + """ + if window_size < 1: + raise ValueError("Window size must be a positive integer") + + sma: list[float | None] = [] + + for i in range(len(data)): + if i < window_size - 1: + sma.append(None) # SMA not available for early data points + else: + window = data[i - window_size + 1 : i + 1] + sma_value = sum(window) / window_size + sma.append(sma_value) + return sma + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + + # Example data (replace with your own time series data) + data = [10, 12, 15, 13, 14, 16, 18, 17, 19, 21] + + # Specify the window size for the SMA + window_size = 3 + + # Calculate the Simple Moving Average + sma_values = simple_moving_average(data, window_size) + + # Print the SMA values + print("Simple Moving Average (SMA) Values:") + for i, value in enumerate(sma_values): + if value is not None: + print(f"Day {i + 1}: {value:.2f}") + else: + print(f"Day {i + 1}: Not enough data for SMA") From 4cbefadbd7adee486e33a6b66014a2474e81f82e Mon Sep 17 00:00:00 2001 From: Tauseef Hilal Tantary Date: Mon, 23 Oct 2023 19:21:09 +0530 Subject: [PATCH 447/757] [New Algorithm] - Triangular Numbers (#10663) * Add New Algorithm: Triangular Numbers * Calculate nth triangular number instead of generating a list * Handle 0th position and update function name and docstring --- maths/special_numbers/triangular_numbers.py | 43 +++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 maths/special_numbers/triangular_numbers.py diff --git a/maths/special_numbers/triangular_numbers.py b/maths/special_numbers/triangular_numbers.py new file mode 100644 index 000000000000..5be89e6108b2 --- /dev/null +++ b/maths/special_numbers/triangular_numbers.py @@ -0,0 +1,43 @@ +""" +A triangular number or triangle number counts objects arranged in an +equilateral triangle. This module provides a function to generate n'th +triangular number. + +For more information about triangular numbers, refer to: +https://en.wikipedia.org/wiki/Triangular_number +""" + + +def triangular_number(position: int) -> int: + """ + Generate the triangular number at the specified position. + + Args: + position (int): The position of the triangular number to generate. + + Returns: + int: The triangular number at the specified position. + + Raises: + ValueError: If `position` is negative. + + Examples: + >>> triangular_number(1) + 1 + >>> triangular_number(3) + 6 + >>> triangular_number(-1) + Traceback (most recent call last): + ... + ValueError: param `position` must be non-negative + """ + if position < 0: + raise ValueError("param `position` must be non-negative") + + return position * (position + 1) // 2 + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 54e2aa67e8f74435b15e2a2864a7fb00981979af Mon Sep 17 00:00:00 2001 From: Hardik Pawar <97388607+Hardvan@users.noreply.github.com> Date: Mon, 23 Oct 2023 23:12:02 +0530 Subject: [PATCH 448/757] Enhance readability of Minimax (#10838) * Enhance readability of Minimax * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Reduce line overflow * Update backtracking/minimax.py Co-authored-by: Tianyi Zheng * Update backtracking/minimax.py Co-authored-by: Tianyi Zheng * Update backtracking/minimax.py Co-authored-by: Tianyi Zheng * Remove line overflow --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Tianyi Zheng --- backtracking/minimax.py | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/backtracking/minimax.py b/backtracking/minimax.py index 6e310131e069..6dece2990a1c 100644 --- a/backtracking/minimax.py +++ b/backtracking/minimax.py @@ -16,6 +16,22 @@ def minimax( depth: int, node_index: int, is_max: bool, scores: list[int], height: float ) -> int: """ + This function implements the minimax algorithm, which helps achieve the optimal + score for a player in a two-player game by checking all possible moves. + If the player is the maximizer, then the score is maximized. + If the player is the minimizer, then the score is minimized. + + Parameters: + - depth: Current depth in the game tree. + - node_index: Index of the current node in the scores list. + - is_max: A boolean indicating whether the current move + is for the maximizer (True) or minimizer (False). + - scores: A list containing the scores of the leaves of the game tree. + - height: The maximum height of the game tree. + + Returns: + - An integer representing the optimal score for the current player. + >>> import math >>> scores = [90, 23, 6, 33, 21, 65, 123, 34423] >>> height = math.log(len(scores), 2) @@ -37,19 +53,24 @@ def minimax( if depth < 0: raise ValueError("Depth cannot be less than 0") - if len(scores) == 0: raise ValueError("Scores cannot be empty") + # Base case: If the current depth equals the height of the tree, + # return the score of the current node. if depth == height: return scores[node_index] + # If it's the maximizer's turn, choose the maximum score + # between the two possible moves. if is_max: return max( minimax(depth + 1, node_index * 2, False, scores, height), minimax(depth + 1, node_index * 2 + 1, False, scores, height), ) + # If it's the minimizer's turn, choose the minimum score + # between the two possible moves. return min( minimax(depth + 1, node_index * 2, True, scores, height), minimax(depth + 1, node_index * 2 + 1, True, scores, height), @@ -57,8 +78,11 @@ def minimax( def main() -> None: + # Sample scores and height calculation scores = [90, 23, 6, 33, 21, 65, 123, 34423] height = math.log(len(scores), 2) + + # Calculate and print the optimal value using the minimax algorithm print("Optimal value : ", end="") print(minimax(0, 0, True, scores, height)) From 30122062b93cdeba8bacb0a4a3c783bc8069b7a0 Mon Sep 17 00:00:00 2001 From: Aqib Javid Bhat Date: Mon, 23 Oct 2023 23:26:43 +0530 Subject: [PATCH 449/757] Add Floyd's Cycle Detection Algorithm (#10833) * Add Floyd's Cycle Detection Algorithm * Add tests for add_node function * Apply suggestions from code review * Update floyds_cycle_detection.py --------- Co-authored-by: Tianyi Zheng --- .../linked_list/floyds_cycle_detection.py | 150 ++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100644 data_structures/linked_list/floyds_cycle_detection.py diff --git a/data_structures/linked_list/floyds_cycle_detection.py b/data_structures/linked_list/floyds_cycle_detection.py new file mode 100644 index 000000000000..6c3f13760260 --- /dev/null +++ b/data_structures/linked_list/floyds_cycle_detection.py @@ -0,0 +1,150 @@ +""" +Floyd's cycle detection algorithm is a popular algorithm used to detect cycles +in a linked list. It uses two pointers, a slow pointer and a fast pointer, +to traverse the linked list. The slow pointer moves one node at a time while the fast +pointer moves two nodes at a time. If there is a cycle in the linked list, +the fast pointer will eventually catch up to the slow pointer and they will +meet at the same node. If there is no cycle, the fast pointer will reach the end of +the linked list and the algorithm will terminate. + +For more information: https://en.wikipedia.org/wiki/Cycle_detection#Floyd's_tortoise_and_hare +""" + +from collections.abc import Iterator +from dataclasses import dataclass +from typing import Any, Self + + +@dataclass +class Node: + """ + A class representing a node in a singly linked list. + """ + + data: Any + next_node: Self | None = None + + +@dataclass +class LinkedList: + """ + A class representing a singly linked list. + """ + + head: Node | None = None + + def __iter__(self) -> Iterator: + """ + Iterates through the linked list. + + Returns: + Iterator: An iterator over the linked list. + + Examples: + >>> linked_list = LinkedList() + >>> list(linked_list) + [] + >>> linked_list.add_node(1) + >>> tuple(linked_list) + (1,) + """ + visited = [] + node = self.head + while node: + # Avoid infinite loop in there's a cycle + if node in visited: + return + visited.append(node) + yield node.data + node = node.next_node + + def add_node(self, data: Any) -> None: + """ + Adds a new node to the end of the linked list. + + Args: + data (Any): The data to be stored in the new node. + + Examples: + >>> linked_list = LinkedList() + >>> linked_list.add_node(1) + >>> linked_list.add_node(2) + >>> linked_list.add_node(3) + >>> linked_list.add_node(4) + >>> tuple(linked_list) + (1, 2, 3, 4) + """ + new_node = Node(data) + + if self.head is None: + self.head = new_node + return + + current_node = self.head + while current_node.next_node is not None: + current_node = current_node.next_node + + current_node.next_node = new_node + + def detect_cycle(self) -> bool: + """ + Detects if there is a cycle in the linked list using + Floyd's cycle detection algorithm. + + Returns: + bool: True if there is a cycle, False otherwise. + + Examples: + >>> linked_list = LinkedList() + >>> linked_list.add_node(1) + >>> linked_list.add_node(2) + >>> linked_list.add_node(3) + >>> linked_list.add_node(4) + + >>> linked_list.detect_cycle() + False + + # Create a cycle in the linked list + >>> linked_list.head.next_node.next_node.next_node = linked_list.head.next_node + + >>> linked_list.detect_cycle() + True + """ + if self.head is None: + return False + + slow_pointer: Node | None = self.head + fast_pointer: Node | None = self.head + + while fast_pointer is not None and fast_pointer.next_node is not None: + slow_pointer = slow_pointer.next_node if slow_pointer else None + fast_pointer = fast_pointer.next_node.next_node + if slow_pointer == fast_pointer: + return True + + return False + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + + linked_list = LinkedList() + linked_list.add_node(1) + linked_list.add_node(2) + linked_list.add_node(3) + linked_list.add_node(4) + + # Create a cycle in the linked list + # It first checks if the head, next_node, and next_node.next_node attributes of the + # linked list are not None to avoid any potential type errors. + if ( + linked_list.head + and linked_list.head.next_node + and linked_list.head.next_node.next_node + ): + linked_list.head.next_node.next_node.next_node = linked_list.head.next_node + + has_cycle = linked_list.detect_cycle() + print(has_cycle) # Output: True From ffd3a56c35f5ec274c819e8f2596ab5134cf9c36 Mon Sep 17 00:00:00 2001 From: SEIKH NABAB UDDIN <93948993+nababuddin@users.noreply.github.com> Date: Mon, 23 Oct 2023 23:42:28 +0530 Subject: [PATCH 450/757] Updated Selection Sort (#10855) * Update selection_sort.py * Update selection_sort.py --- sorts/selection_sort.py | 34 +++++++++++----------------------- 1 file changed, 11 insertions(+), 23 deletions(-) diff --git a/sorts/selection_sort.py b/sorts/selection_sort.py index 28971a5e1aad..506836b53e44 100644 --- a/sorts/selection_sort.py +++ b/sorts/selection_sort.py @@ -1,22 +1,9 @@ -""" -This is a pure Python implementation of the selection sort algorithm - -For doctests run following command: -python -m doctest -v selection_sort.py -or -python3 -m doctest -v selection_sort.py - -For manual testing run: -python selection_sort.py -""" - - def selection_sort(collection: list[int]) -> list[int]: - """Pure implementation of the selection sort algorithm in Python - :param collection: some mutable ordered collection with heterogeneous - comparable items inside - :return: the same collection ordered by ascending + """ + Sorts a list in ascending order using the selection sort algorithm. + :param collection: A list of integers to be sorted. + :return: The sorted list. Examples: >>> selection_sort([0, 5, 3, 2, 2]) @@ -31,16 +18,17 @@ def selection_sort(collection: list[int]) -> list[int]: length = len(collection) for i in range(length - 1): - least = i + min_index = i for k in range(i + 1, length): - if collection[k] < collection[least]: - least = k - if least != i: - collection[least], collection[i] = (collection[i], collection[least]) + if collection[k] < collection[min_index]: + min_index = k + if min_index != i: + collection[i], collection[min_index] = collection[min_index], collection[i] return collection if __name__ == "__main__": user_input = input("Enter numbers separated by a comma:\n").strip() unsorted = [int(item) for item in user_input.split(",")] - print(selection_sort(unsorted)) + sorted_list = selection_sort(unsorted) + print("Sorted List:", sorted_list) From e5d6969f38ecf03f3e3a1e35fcfd3ae2484b6e08 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 23 Oct 2023 22:29:16 +0200 Subject: [PATCH 451/757] [pre-commit.ci] pre-commit autoupdate (#10856) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/astral-sh/ruff-pre-commit: v0.0.292 → v0.1.1](https://github.com/astral-sh/ruff-pre-commit/compare/v0.0.292...v0.1.1) - [github.com/psf/black: 23.9.1 → 23.10.0](https://github.com/psf/black/compare/23.9.1...23.10.0) - [github.com/pre-commit/mirrors-mypy: v1.6.0 → v1.6.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.6.0...v1.6.1) * updating DIRECTORY.md --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- .pre-commit-config.yaml | 6 +++--- DIRECTORY.md | 4 ++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b3def463ded2..e0b9922fae7e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,12 +16,12 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.0.292 + rev: v0.1.1 hooks: - id: ruff - repo: https://github.com/psf/black - rev: 23.9.1 + rev: 23.10.0 hooks: - id: black @@ -51,7 +51,7 @@ repos: - id: validate-pyproject - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.6.0 + rev: v1.6.1 hooks: - id: mypy args: diff --git a/DIRECTORY.md b/DIRECTORY.md index 1e3711fe8dda..dfd1a2c0c809 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -233,6 +233,7 @@ * [Deque Doubly](data_structures/linked_list/deque_doubly.py) * [Doubly Linked List](data_structures/linked_list/doubly_linked_list.py) * [Doubly Linked List Two](data_structures/linked_list/doubly_linked_list_two.py) + * [Floyds Cycle Detection](data_structures/linked_list/floyds_cycle_detection.py) * [From Sequence](data_structures/linked_list/from_sequence.py) * [Has Loop](data_structures/linked_list/has_loop.py) * [Is Palindrome](data_structures/linked_list/is_palindrome.py) @@ -394,6 +395,7 @@ * [Interest](financial/interest.py) * [Present Value](financial/present_value.py) * [Price Plus Tax](financial/price_plus_tax.py) + * [Simple Moving Average](financial/simple_moving_average.py) ## Fractals * [Julia Sets](fractals/julia_sets.py) @@ -706,6 +708,7 @@ * [Polygonal Numbers](maths/special_numbers/polygonal_numbers.py) * [Pronic Number](maths/special_numbers/pronic_number.py) * [Proth Number](maths/special_numbers/proth_number.py) + * [Triangular Numbers](maths/special_numbers/triangular_numbers.py) * [Ugly Numbers](maths/special_numbers/ugly_numbers.py) * [Weird Number](maths/special_numbers/weird_number.py) * [Sum Of Arithmetic Series](maths/sum_of_arithmetic_series.py) @@ -826,6 +829,7 @@ * [Shear Stress](physics/shear_stress.py) * [Speed Of Sound](physics/speed_of_sound.py) * [Speeds Of Gas Molecules](physics/speeds_of_gas_molecules.py) + * [Terminal Velocity](physics/terminal_velocity.py) ## Project Euler * Problem 001 From b98312ca9f2df491017e189b353e6b382b323eed Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Mon, 23 Oct 2023 16:37:17 -0400 Subject: [PATCH 452/757] Consolidate Newton-Raphson implementations (#10859) * updating DIRECTORY.md * updating DIRECTORY.md * Consolidate Newton-Raphson duplicates * Rename consolidated Newton-Raphson file * updating DIRECTORY.md * updating DIRECTORY.md * Fix doctest precision * Fix doctest precision again --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 3 - maths/numerical_analysis/newton_method.py | 54 ------- maths/numerical_analysis/newton_raphson.py | 142 +++++++++++++----- maths/numerical_analysis/newton_raphson_2.py | 64 -------- .../numerical_analysis/newton_raphson_new.py | 83 ---------- 5 files changed, 105 insertions(+), 241 deletions(-) delete mode 100644 maths/numerical_analysis/newton_method.py delete mode 100644 maths/numerical_analysis/newton_raphson_2.py delete mode 100644 maths/numerical_analysis/newton_raphson_new.py diff --git a/DIRECTORY.md b/DIRECTORY.md index dfd1a2c0c809..f0b1f7c13c2b 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -642,10 +642,7 @@ * [Intersection](maths/numerical_analysis/intersection.py) * [Nevilles Method](maths/numerical_analysis/nevilles_method.py) * [Newton Forward Interpolation](maths/numerical_analysis/newton_forward_interpolation.py) - * [Newton Method](maths/numerical_analysis/newton_method.py) * [Newton Raphson](maths/numerical_analysis/newton_raphson.py) - * [Newton Raphson 2](maths/numerical_analysis/newton_raphson_2.py) - * [Newton Raphson New](maths/numerical_analysis/newton_raphson_new.py) * [Numerical Integration](maths/numerical_analysis/numerical_integration.py) * [Runge Kutta](maths/numerical_analysis/runge_kutta.py) * [Runge Kutta Fehlberg 45](maths/numerical_analysis/runge_kutta_fehlberg_45.py) diff --git a/maths/numerical_analysis/newton_method.py b/maths/numerical_analysis/newton_method.py deleted file mode 100644 index 5127bfcafd9a..000000000000 --- a/maths/numerical_analysis/newton_method.py +++ /dev/null @@ -1,54 +0,0 @@ -"""Newton's Method.""" - -# Newton's Method - https://en.wikipedia.org/wiki/Newton%27s_method -from collections.abc import Callable - -RealFunc = Callable[[float], float] # type alias for a real -> real function - - -# function is the f(x) and derivative is the f'(x) -def newton( - function: RealFunc, - derivative: RealFunc, - starting_int: int, -) -> float: - """ - >>> newton(lambda x: x ** 3 - 2 * x - 5, lambda x: 3 * x ** 2 - 2, 3) - 2.0945514815423474 - >>> newton(lambda x: x ** 3 - 1, lambda x: 3 * x ** 2, -2) - 1.0 - >>> newton(lambda x: x ** 3 - 1, lambda x: 3 * x ** 2, -4) - 1.0000000000000102 - >>> import math - >>> newton(math.sin, math.cos, 1) - 0.0 - >>> newton(math.sin, math.cos, 2) - 3.141592653589793 - >>> newton(math.cos, lambda x: -math.sin(x), 2) - 1.5707963267948966 - >>> newton(math.cos, lambda x: -math.sin(x), 0) - Traceback (most recent call last): - ... - ZeroDivisionError: Could not find root - """ - prev_guess = float(starting_int) - while True: - try: - next_guess = prev_guess - function(prev_guess) / derivative(prev_guess) - except ZeroDivisionError: - raise ZeroDivisionError("Could not find root") from None - if abs(prev_guess - next_guess) < 10**-5: - return next_guess - prev_guess = next_guess - - -def f(x: float) -> float: - return (x**3) - (2 * x) - 5 - - -def f1(x: float) -> float: - return 3 * (x**2) - 2 - - -if __name__ == "__main__": - print(newton(f, f1, 3)) diff --git a/maths/numerical_analysis/newton_raphson.py b/maths/numerical_analysis/newton_raphson.py index 8491ca8003bc..feee38f905dd 100644 --- a/maths/numerical_analysis/newton_raphson.py +++ b/maths/numerical_analysis/newton_raphson.py @@ -1,45 +1,113 @@ -# Implementing Newton Raphson method in Python -# Author: Syed Haseeb Shah (github.com/QuantumNovice) -# The Newton-Raphson method (also known as Newton's method) is a way to -# quickly find a good approximation for the root of a real-valued function -from __future__ import annotations - -from decimal import Decimal - -from sympy import diff, lambdify, symbols - - -def newton_raphson(func: str, a: float | Decimal, precision: float = 1e-10) -> float: - """Finds root from the point 'a' onwards by Newton-Raphson method - >>> newton_raphson("sin(x)", 2) - 3.1415926536808043 - >>> newton_raphson("x**2 - 5*x + 2", 0.4) - 0.4384471871911695 - >>> newton_raphson("x**2 - 5", 0.1) - 2.23606797749979 - >>> newton_raphson("log(x) - 1", 2) - 2.718281828458938 +""" +The Newton-Raphson method (aka the Newton method) is a root-finding algorithm that +approximates a root of a given real-valued function f(x). It is an iterative method +given by the formula + +x_{n + 1} = x_n + f(x_n) / f'(x_n) + +with the precision of the approximation increasing as the number of iterations increase. + +Reference: https://en.wikipedia.org/wiki/Newton%27s_method +""" +from collections.abc import Callable + +RealFunc = Callable[[float], float] + + +def calc_derivative(f: RealFunc, x: float, delta_x: float = 1e-3) -> float: + """ + Approximate the derivative of a function f(x) at a point x using the finite + difference method + + >>> import math + >>> tolerance = 1e-5 + >>> derivative = calc_derivative(lambda x: x**2, 2) + >>> math.isclose(derivative, 4, abs_tol=tolerance) + True + >>> derivative = calc_derivative(math.sin, 0) + >>> math.isclose(derivative, 1, abs_tol=tolerance) + True + """ + return (f(x + delta_x / 2) - f(x - delta_x / 2)) / delta_x + + +def newton_raphson( + f: RealFunc, + x0: float = 0, + max_iter: int = 100, + step: float = 1e-6, + max_error: float = 1e-6, + log_steps: bool = False, +) -> tuple[float, float, list[float]]: """ - x = symbols("x") - f = lambdify(x, func, "math") - f_derivative = lambdify(x, diff(func), "math") - x_curr = a - while True: - x_curr = Decimal(x_curr) - Decimal(f(x_curr)) / Decimal(f_derivative(x_curr)) - if abs(f(x_curr)) < precision: - return float(x_curr) + Find a root of the given function f using the Newton-Raphson method. + + :param f: A real-valued single-variable function + :param x0: Initial guess + :param max_iter: Maximum number of iterations + :param step: Step size of x, used to approximate f'(x) + :param max_error: Maximum approximation error + :param log_steps: bool denoting whether to log intermediate steps + + :return: A tuple containing the approximation, the error, and the intermediate + steps. If log_steps is False, then an empty list is returned for the third + element of the tuple. + + :raises ZeroDivisionError: The derivative approaches 0. + :raises ArithmeticError: No solution exists, or the solution isn't found before the + iteration limit is reached. + + >>> import math + >>> tolerance = 1e-15 + >>> root, *_ = newton_raphson(lambda x: x**2 - 5*x + 2, 0.4, max_error=tolerance) + >>> math.isclose(root, (5 - math.sqrt(17)) / 2, abs_tol=tolerance) + True + >>> root, *_ = newton_raphson(lambda x: math.log(x) - 1, 2, max_error=tolerance) + >>> math.isclose(root, math.e, abs_tol=tolerance) + True + >>> root, *_ = newton_raphson(math.sin, 1, max_error=tolerance) + >>> math.isclose(root, 0, abs_tol=tolerance) + True + >>> newton_raphson(math.cos, 0) + Traceback (most recent call last): + ... + ZeroDivisionError: No converging solution found, zero derivative + >>> newton_raphson(lambda x: x**2 + 1, 2) + Traceback (most recent call last): + ... + ArithmeticError: No converging solution found, iteration limit reached + """ + + def f_derivative(x: float) -> float: + return calc_derivative(f, x, step) + + a = x0 # Set initial guess + steps = [] + for _ in range(max_iter): + if log_steps: # Log intermediate steps + steps.append(a) + + error = abs(f(a)) + if error < max_error: + return a, error, steps + + if f_derivative(a) == 0: + raise ZeroDivisionError("No converging solution found, zero derivative") + a -= f(a) / f_derivative(a) # Calculate next estimate + raise ArithmeticError("No converging solution found, iteration limit reached") if __name__ == "__main__": import doctest + from math import exp, tanh doctest.testmod() - # Find value of pi - print(f"The root of sin(x) = 0 is {newton_raphson('sin(x)', 2)}") - # Find root of polynomial - print(f"The root of x**2 - 5*x + 2 = 0 is {newton_raphson('x**2 - 5*x + 2', 0.4)}") - # Find value of e - print(f"The root of log(x) - 1 = 0 is {newton_raphson('log(x) - 1', 2)}") - # Find root of exponential function - print(f"The root of exp(x) - 1 = 0 is {newton_raphson('exp(x) - 1', 0)}") + def func(x: float) -> float: + return tanh(x) ** 2 - exp(3 * x) + + solution, err, steps = newton_raphson( + func, x0=10, max_iter=100, step=1e-6, log_steps=True + ) + print(f"{solution=}, {err=}") + print("\n".join(str(x) for x in steps)) diff --git a/maths/numerical_analysis/newton_raphson_2.py b/maths/numerical_analysis/newton_raphson_2.py deleted file mode 100644 index f6b227b5c9c1..000000000000 --- a/maths/numerical_analysis/newton_raphson_2.py +++ /dev/null @@ -1,64 +0,0 @@ -""" -Author: P Shreyas Shetty -Implementation of Newton-Raphson method for solving equations of kind -f(x) = 0. It is an iterative method where solution is found by the expression - x[n+1] = x[n] + f(x[n])/f'(x[n]) -If no solution exists, then either the solution will not be found when iteration -limit is reached or the gradient f'(x[n]) approaches zero. In both cases, exception -is raised. If iteration limit is reached, try increasing maxiter. -""" - -import math as m -from collections.abc import Callable - -DerivativeFunc = Callable[[float], float] - - -def calc_derivative(f: DerivativeFunc, a: float, h: float = 0.001) -> float: - """ - Calculates derivative at point a for function f using finite difference - method - """ - return (f(a + h) - f(a - h)) / (2 * h) - - -def newton_raphson( - f: DerivativeFunc, - x0: float = 0, - maxiter: int = 100, - step: float = 0.0001, - maxerror: float = 1e-6, - logsteps: bool = False, -) -> tuple[float, float, list[float]]: - a = x0 # set the initial guess - steps = [a] - error = abs(f(a)) - f1 = lambda x: calc_derivative(f, x, h=step) # noqa: E731 Derivative of f(x) - for _ in range(maxiter): - if f1(a) == 0: - raise ValueError("No converging solution found") - a = a - f(a) / f1(a) # Calculate the next estimate - if logsteps: - steps.append(a) - if error < maxerror: - break - else: - raise ValueError("Iteration limit reached, no converging solution found") - if logsteps: - # If logstep is true, then log intermediate steps - return a, error, steps - return a, error, [] - - -if __name__ == "__main__": - from matplotlib import pyplot as plt - - f = lambda x: m.tanh(x) ** 2 - m.exp(3 * x) # noqa: E731 - solution, error, steps = newton_raphson( - f, x0=10, maxiter=1000, step=1e-6, logsteps=True - ) - plt.plot([abs(f(x)) for x in steps]) - plt.xlabel("step") - plt.ylabel("error") - plt.show() - print(f"solution = {{{solution:f}}}, error = {{{error:f}}}") diff --git a/maths/numerical_analysis/newton_raphson_new.py b/maths/numerical_analysis/newton_raphson_new.py deleted file mode 100644 index f61841e2eb84..000000000000 --- a/maths/numerical_analysis/newton_raphson_new.py +++ /dev/null @@ -1,83 +0,0 @@ -# Implementing Newton Raphson method in Python -# Author: Saksham Gupta -# -# The Newton-Raphson method (also known as Newton's method) is a way to -# quickly find a good approximation for the root of a functreal-valued ion -# The method can also be extended to complex functions -# -# Newton's Method - https://en.wikipedia.org/wiki/Newton's_method - -from sympy import diff, lambdify, symbols -from sympy.functions import * # noqa: F403 - - -def newton_raphson( - function: str, - starting_point: complex, - variable: str = "x", - precision: float = 10**-10, - multiplicity: int = 1, -) -> complex: - """Finds root from the 'starting_point' onwards by Newton-Raphson method - Refer to https://docs.sympy.org/latest/modules/functions/index.html - for usable mathematical functions - - >>> newton_raphson("sin(x)", 2) - 3.141592653589793 - >>> newton_raphson("x**4 -5", 0.4 + 5j) - (-7.52316384526264e-37+1.4953487812212207j) - >>> newton_raphson('log(y) - 1', 2, variable='y') - 2.7182818284590455 - >>> newton_raphson('exp(x) - 1', 10, precision=0.005) - 1.2186556186174883e-10 - >>> newton_raphson('cos(x)', 0) - Traceback (most recent call last): - ... - ZeroDivisionError: Could not find root - """ - - x = symbols(variable) - func = lambdify(x, function) - diff_function = lambdify(x, diff(function, x)) - - prev_guess = starting_point - - while True: - if diff_function(prev_guess) != 0: - next_guess = prev_guess - multiplicity * func(prev_guess) / diff_function( - prev_guess - ) - else: - raise ZeroDivisionError("Could not find root") from None - - # Precision is checked by comparing the difference of consecutive guesses - if abs(next_guess - prev_guess) < precision: - return next_guess - - prev_guess = next_guess - - -# Let's Execute -if __name__ == "__main__": - # Find root of trigonometric function - # Find value of pi - print(f"The root of sin(x) = 0 is {newton_raphson('sin(x)', 2)}") - - # Find root of polynomial - # Find fourth Root of 5 - print(f"The root of x**4 - 5 = 0 is {newton_raphson('x**4 -5', 0.4 +5j)}") - - # Find value of e - print( - "The root of log(y) - 1 = 0 is ", - f"{newton_raphson('log(y) - 1', 2, variable='y')}", - ) - - # Exponential Roots - print( - "The root of exp(x) - 1 = 0 is", - f"{newton_raphson('exp(x) - 1', 10, precision=0.005)}", - ) - - # Find root of cos(x) - print(f"The root of cos(x) = 0 is {newton_raphson('cos(x)', 0)}") From 6971af2416af051b13f888bebdfefa222c89c15d Mon Sep 17 00:00:00 2001 From: Marek Mazij <112333347+Mrk-Mzj@users.noreply.github.com> Date: Tue, 24 Oct 2023 00:22:09 +0200 Subject: [PATCH 453/757] feat: RGB to CMYK color converter (#10741) * feat: code functional, commented, tested * fix: compering types, exception msg, line length * fix: type hints --- conversions/rgb_cmyk_conversion.py | 71 ++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 conversions/rgb_cmyk_conversion.py diff --git a/conversions/rgb_cmyk_conversion.py b/conversions/rgb_cmyk_conversion.py new file mode 100644 index 000000000000..07d65b704c44 --- /dev/null +++ b/conversions/rgb_cmyk_conversion.py @@ -0,0 +1,71 @@ +def rgb_to_cmyk(r_input: int, g_input: int, b_input: int) -> tuple[int, int, int, int]: + """ + Simple RGB to CMYK conversion. Returns percentages of CMYK paint. + https://www.programmingalgorithms.com/algorithm/rgb-to-cmyk/ + + Note: this is a very popular algorithm that converts colors linearly and gives + only approximate results. Actual preparation for printing requires advanced color + conversion considering the color profiles and parameters of the target device. + + >>> rgb_to_cmyk(255, 200, "a") + Traceback (most recent call last): + ... + ValueError: Expected int, found (, , ) + + >>> rgb_to_cmyk(255, 255, 999) + Traceback (most recent call last): + ... + ValueError: Expected int of the range 0..255 + + >>> rgb_to_cmyk(255, 255, 255) # white + (0, 0, 0, 0) + + >>> rgb_to_cmyk(128, 128, 128) # gray + (0, 0, 0, 50) + + >>> rgb_to_cmyk(0, 0, 0) # black + (0, 0, 0, 100) + + >>> rgb_to_cmyk(255, 0, 0) # red + (0, 100, 100, 0) + + >>> rgb_to_cmyk(0, 255, 0) # green + (100, 0, 100, 0) + + >>> rgb_to_cmyk(0, 0, 255) # blue + (100, 100, 0, 0) + """ + + if ( + not isinstance(r_input, int) + or not isinstance(g_input, int) + or not isinstance(b_input, int) + ): + msg = f"Expected int, found {type(r_input), type(g_input), type(b_input)}" + raise ValueError(msg) + + if not 0 <= r_input < 256 or not 0 <= g_input < 256 or not 0 <= b_input < 256: + raise ValueError("Expected int of the range 0..255") + + # changing range from 0..255 to 0..1 + r = r_input / 255 + g = g_input / 255 + b = b_input / 255 + + k = 1 - max(r, g, b) + + if k == 1: # pure black + return 0, 0, 0, 100 + + c = round(100 * (1 - r - k) / (1 - k)) + m = round(100 * (1 - g - k) / (1 - k)) + y = round(100 * (1 - b - k) / (1 - k)) + k = round(100 * k) + + return c, m, y, k + + +if __name__ == "__main__": + from doctest import testmod + + testmod() From 481aff7928b6a352c3cfa49045f0dd390d9d0868 Mon Sep 17 00:00:00 2001 From: Gourav Raj <59208847+gouravrajbit@users.noreply.github.com> Date: Tue, 24 Oct 2023 13:24:38 +0530 Subject: [PATCH 454/757] Add `Mirror a Binary Tree` solution (#9534) * Add `Invert a Binary Tree` solution * Add type * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Add `doctest` * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Add `test` to `get_tree_inorder` * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Add `test` changes * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix lint errors * Fix precommit errors * Update and rename invert_binary_tree.py to mirror_binary_tree.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- .../binary_tree/mirror_binary_tree.py | 153 ++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 data_structures/binary_tree/mirror_binary_tree.py diff --git a/data_structures/binary_tree/mirror_binary_tree.py b/data_structures/binary_tree/mirror_binary_tree.py new file mode 100644 index 000000000000..39305c2a9da2 --- /dev/null +++ b/data_structures/binary_tree/mirror_binary_tree.py @@ -0,0 +1,153 @@ +""" +Given the root of a binary tree, mirror the tree, and return its root. + +Leetcode problem reference: https://leetcode.com/problems/mirror-binary-tree/ +""" +from __future__ import annotations + +from collections.abc import Iterator +from dataclasses import dataclass + + +@dataclass +class Node: + """ + A Node has value variable and pointers to Nodes to its left and right. + """ + + value: int + left: Node | None = None + right: Node | None = None + + def __iter__(self) -> Iterator[int]: + if self.left: + yield from self.left + yield self.value + if self.right: + yield from self.right + + def __len__(self) -> int: + return sum(1 for _ in self) + + def mirror(self) -> Node: + """ + Mirror the binary tree rooted at this node by swapping left and right children. + + >>> tree = Node(0) + >>> list(tree) + [0] + >>> list(tree.mirror()) + [0] + >>> tree = Node(1, Node(0), Node(3, Node(2), Node(4, None, Node(5)))) + >>> tuple(tree) + (0, 1, 2, 3, 4, 5) + >>> tuple(tree.mirror()) + (5, 4, 3, 2, 1, 0) + """ + self.left, self.right = self.right, self.left + if self.left: + self.left.mirror() + if self.right: + self.right.mirror() + return self + + +def make_tree_seven() -> Node: + r""" + Return a binary tree with 7 nodes that looks like this: + 1 + / \ + 2 3 + / \ / \ + 4 5 6 7 + + >>> tree_seven = make_tree_seven() + >>> len(tree_seven) + 7 + >>> list(tree_seven) + [4, 2, 5, 1, 6, 3, 7] + """ + tree = Node(1) + tree.left = Node(2) + tree.right = Node(3) + tree.left.left = Node(4) + tree.left.right = Node(5) + tree.right.left = Node(6) + tree.right.right = Node(7) + return tree + + +def make_tree_nine() -> Node: + r""" + Return a binary tree with 9 nodes that looks like this: + 1 + / \ + 2 3 + / \ \ + 4 5 6 + / \ \ + 7 8 9 + + >>> tree_nine = make_tree_nine() + >>> len(tree_nine) + 9 + >>> list(tree_nine) + [7, 4, 8, 2, 5, 9, 1, 3, 6] + """ + tree = Node(1) + tree.left = Node(2) + tree.right = Node(3) + tree.left.left = Node(4) + tree.left.right = Node(5) + tree.right.right = Node(6) + tree.left.left.left = Node(7) + tree.left.left.right = Node(8) + tree.left.right.right = Node(9) + return tree + + +def main() -> None: + r""" + Mirror binary trees with the given root and returns the root + + >>> tree = make_tree_nine() + >>> tuple(tree) + (7, 4, 8, 2, 5, 9, 1, 3, 6) + >>> tuple(tree.mirror()) + (6, 3, 1, 9, 5, 2, 8, 4, 7) + + nine_tree: + 1 + / \ + 2 3 + / \ \ + 4 5 6 + / \ \ + 7 8 9 + + The mirrored tree looks like this: + 1 + / \ + 3 2 + / / \ + 6 5 4 + / / \ + 9 8 7 + """ + trees = {"zero": Node(0), "seven": make_tree_seven(), "nine": make_tree_nine()} + for name, tree in trees.items(): + print(f" The {name} tree: {tuple(tree)}") + # (0,) + # (4, 2, 5, 1, 6, 3, 7) + # (7, 4, 8, 2, 5, 9, 1, 3, 6) + print(f"Mirror of {name} tree: {tuple(tree.mirror())}") + # (0,) + # (7, 3, 6, 1, 5, 2, 4) + # (6, 3, 1, 9, 5, 2, 8, 4, 7) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + main() From 17059b7ece0e9b2aa0f6e1789d635d6c3eef93ca Mon Sep 17 00:00:00 2001 From: Bhavesh Mathur <130584844+bhavesh1oo@users.noreply.github.com> Date: Tue, 24 Oct 2023 14:33:22 +0530 Subject: [PATCH 455/757] Added doctests , type hints for other/nested_brackets.py (#10872) * Added doctests , type hints * Update nested_brackets.py --------- Co-authored-by: Christian Clauss --- other/nested_brackets.py | 69 ++++++++++++++++++++++++++++------------ 1 file changed, 49 insertions(+), 20 deletions(-) diff --git a/other/nested_brackets.py b/other/nested_brackets.py index 19c6dd53c8b2..5760fa29b2fd 100644 --- a/other/nested_brackets.py +++ b/other/nested_brackets.py @@ -3,9 +3,9 @@ brackets are properly nested. A sequence of brackets s is considered properly nested if any of the following conditions are true: - - s is empty - - s has the form (U) or [U] or {U} where U is a properly nested string - - s has the form VW where V and W are properly nested strings + - s is empty + - s has the form (U) or [U] or {U} where U is a properly nested string + - s has the form VW where V and W are properly nested strings For example, the string "()()[()]" is properly nested but "[(()]" is not. @@ -14,31 +14,60 @@ """ -def is_balanced(s): - stack = [] - open_brackets = set({"(", "[", "{"}) - closed_brackets = set({")", "]", "}"}) +def is_balanced(s: str) -> bool: + """ + >>> is_balanced("") + True + >>> is_balanced("()") + True + >>> is_balanced("[]") + True + >>> is_balanced("{}") + True + >>> is_balanced("()[]{}") + True + >>> is_balanced("(())") + True + >>> is_balanced("[[") + False + >>> is_balanced("([{}])") + True + >>> is_balanced("(()[)]") + False + >>> is_balanced("([)]") + False + >>> is_balanced("[[()]]") + True + >>> is_balanced("(()(()))") + True + >>> is_balanced("]") + False + >>> is_balanced("Life is a bowl of cherries.") + True + >>> is_balanced("Life is a bowl of che{}ies.") + True + >>> is_balanced("Life is a bowl of che}{ies.") + False + """ open_to_closed = {"{": "}", "[": "]", "(": ")"} - - for i in range(len(s)): - if s[i] in open_brackets: - stack.append(s[i]) - - elif s[i] in closed_brackets and ( - len(stack) == 0 or (len(stack) > 0 and open_to_closed[stack.pop()] != s[i]) + stack = [] + for symbol in s: + if symbol in open_to_closed: + stack.append(symbol) + elif symbol in open_to_closed.values() and ( + not stack or open_to_closed[stack.pop()] != symbol ): return False - - return len(stack) == 0 + return not stack # stack should be empty def main(): s = input("Enter sequence of brackets: ") - if is_balanced(s): - print(s, "is balanced") - else: - print(s, "is not balanced") + print(f"'{s}' is {'' if is_balanced(s) else 'not '}balanced.") if __name__ == "__main__": + from doctest import testmod + + testmod() main() From eb17fcf8f5e77a6d3c870427db02b258515b4997 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Tue, 24 Oct 2023 14:45:36 +0200 Subject: [PATCH 456/757] Use dataclasses in circular_linked_list.py (#10884) * Use dataclasses in circular_linked_list.py * updating DIRECTORY.md --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 2 + .../linked_list/circular_linked_list.py | 49 ++++++++----------- 2 files changed, 23 insertions(+), 28 deletions(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index f0b1f7c13c2b..5f8eabb6df88 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -157,6 +157,7 @@ * [Prefix Conversions](conversions/prefix_conversions.py) * [Prefix Conversions String](conversions/prefix_conversions_string.py) * [Pressure Conversions](conversions/pressure_conversions.py) + * [Rgb Cmyk Conversion](conversions/rgb_cmyk_conversion.py) * [Rgb Hsv Conversion](conversions/rgb_hsv_conversion.py) * [Roman Numerals](conversions/roman_numerals.py) * [Speed Conversions](conversions/speed_conversions.py) @@ -198,6 +199,7 @@ * [Lowest Common Ancestor](data_structures/binary_tree/lowest_common_ancestor.py) * [Maximum Fenwick Tree](data_structures/binary_tree/maximum_fenwick_tree.py) * [Merge Two Binary Trees](data_structures/binary_tree/merge_two_binary_trees.py) + * [Mirror Binary Tree](data_structures/binary_tree/mirror_binary_tree.py) * [Non Recursive Segment Tree](data_structures/binary_tree/non_recursive_segment_tree.py) * [Number Of Possible Binary Trees](data_structures/binary_tree/number_of_possible_binary_trees.py) * [Red Black Tree](data_structures/binary_tree/red_black_tree.py) diff --git a/data_structures/linked_list/circular_linked_list.py b/data_structures/linked_list/circular_linked_list.py index 54343c80a30f..bb64441d4560 100644 --- a/data_structures/linked_list/circular_linked_list.py +++ b/data_structures/linked_list/circular_linked_list.py @@ -1,27 +1,20 @@ from __future__ import annotations from collections.abc import Iterator +from dataclasses import dataclass from typing import Any +@dataclass class Node: - def __init__(self, data: Any): - """ - Initialize a new Node with the given data. - Args: - data: The data to be stored in the node. - """ - self.data: Any = data - self.next: Node | None = None # Reference to the next node + data: Any + next_node: Node | None = None +@dataclass class CircularLinkedList: - def __init__(self) -> None: - """ - Initialize an empty Circular Linked List. - """ - self.head: Node | None = None # Reference to the head (first node) - self.tail: Node | None = None # Reference to the tail (last node) + head: Node | None = None # Reference to the head (first node) + tail: Node | None = None # Reference to the tail (last node) def __iter__(self) -> Iterator[Any]: """ @@ -32,7 +25,7 @@ def __iter__(self) -> Iterator[Any]: node = self.head while node: yield node.data - node = node.next + node = node.next_node if node == self.head: break @@ -76,20 +69,20 @@ def insert_nth(self, index: int, data: Any) -> None: raise IndexError("list index out of range.") new_node: Node = Node(data) if self.head is None: - new_node.next = new_node # First node points to itself + new_node.next_node = new_node # First node points to itself self.tail = self.head = new_node elif index == 0: # Insert at the head - new_node.next = self.head + new_node.next_node = self.head assert self.tail is not None # List is not empty, tail exists - self.head = self.tail.next = new_node + self.head = self.tail.next_node = new_node else: temp: Node | None = self.head for _ in range(index - 1): assert temp is not None - temp = temp.next + temp = temp.next_node assert temp is not None - new_node.next = temp.next - temp.next = new_node + new_node.next_node = temp.next_node + temp.next_node = new_node if index == len(self) - 1: # Insert at the tail self.tail = new_node @@ -130,18 +123,18 @@ def delete_nth(self, index: int = 0) -> Any: if self.head == self.tail: # Just one node self.head = self.tail = None elif index == 0: # Delete head node - assert self.tail.next is not None - self.tail.next = self.tail.next.next - self.head = self.head.next + assert self.tail.next_node is not None + self.tail.next_node = self.tail.next_node.next_node + self.head = self.head.next_node else: temp: Node | None = self.head for _ in range(index - 1): assert temp is not None - temp = temp.next + temp = temp.next_node assert temp is not None - assert temp.next is not None - delete_node = temp.next - temp.next = temp.next.next + assert temp.next_node is not None + delete_node = temp.next_node + temp.next_node = temp.next_node.next_node if index == len(self) - 1: # Delete at tail self.tail = temp return delete_node.data From a23dd7ecbea89be8f6b3c7fcf214425274db0d02 Mon Sep 17 00:00:00 2001 From: SEIKH NABAB UDDIN <93948993+nababuddin@users.noreply.github.com> Date: Tue, 24 Oct 2023 18:42:32 +0530 Subject: [PATCH 457/757] Change from only weatherstack to both (#10882) * Update current_weather.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update current_weather.py * Update current_weather.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update current_weather.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update current_weather.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update current_weather.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update current_weather.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update current_weather.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update current_weather.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update current_weather.py * Update current_weather.py * Update current_weather.py * Update current_weather.py * import requests --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- web_programming/current_weather.py | 64 ++++++++++++++++++++---------- 1 file changed, 42 insertions(+), 22 deletions(-) diff --git a/web_programming/current_weather.py b/web_programming/current_weather.py index 3ed4c8a95a0c..3b6cd177cdfb 100644 --- a/web_programming/current_weather.py +++ b/web_programming/current_weather.py @@ -1,30 +1,50 @@ import requests -APPID = "" # <-- Put your OpenWeatherMap appid here! -URL_BASE = "https://api.openweathermap.org/data/2.5/" - - -def current_weather(q: str = "Chicago", appid: str = APPID) -> dict: - """https://openweathermap.org/api""" - return requests.get(URL_BASE + "weather", params=locals()).json() - - -def weather_forecast(q: str = "Kolkata, India", appid: str = APPID) -> dict: - """https://openweathermap.org/forecast5""" - return requests.get(URL_BASE + "forecast", params=locals()).json() - - -def weather_onecall(lat: float = 55.68, lon: float = 12.57, appid: str = APPID) -> dict: - """https://openweathermap.org/api/one-call-api""" - return requests.get(URL_BASE + "onecall", params=locals()).json() +# Put your API key(s) here +OPENWEATHERMAP_API_KEY = "" +WEATHERSTACK_API_KEY = "" + +# Define the URL for the APIs with placeholders +OPENWEATHERMAP_URL_BASE = "https://api.openweathermap.org/data/2.5/weather" +WEATHERSTACK_URL_BASE = "http://api.weatherstack.com/current" + + +def current_weather(location: str) -> list[dict]: + """ + >>> current_weather("location") + Traceback (most recent call last): + ... + ValueError: No API keys provided or no valid data returned. + """ + weather_data = [] + if OPENWEATHERMAP_API_KEY: + params_openweathermap = {"q": location, "appid": OPENWEATHERMAP_API_KEY} + response_openweathermap = requests.get( + OPENWEATHERMAP_URL_BASE, params=params_openweathermap + ) + weather_data.append({"OpenWeatherMap": response_openweathermap.json()}) + if WEATHERSTACK_API_KEY: + params_weatherstack = {"query": location, "access_key": WEATHERSTACK_API_KEY} + response_weatherstack = requests.get( + WEATHERSTACK_URL_BASE, params=params_weatherstack + ) + weather_data.append({"Weatherstack": response_weatherstack.json()}) + if not weather_data: + raise ValueError("No API keys provided or no valid data returned.") + return weather_data if __name__ == "__main__": from pprint import pprint - while True: - location = input("Enter a location:").strip() + location = "to be determined..." + while location: + location = input("Enter a location (city name or latitude,longitude): ").strip() if location: - pprint(current_weather(location)) - else: - break + try: + weather_data = current_weather(location) + for forecast in weather_data: + pprint(forecast) + except ValueError as e: + print(repr(e)) + location = "" From 28f4c16132170bf1e00d414809aff0c31d043e22 Mon Sep 17 00:00:00 2001 From: Saptadeep Banerjee <69459134+imSanko@users.noreply.github.com> Date: Tue, 24 Oct 2023 19:16:00 +0530 Subject: [PATCH 458/757] Tried new TESTS for the binomial_coefficient (#10822) * Tried new TESTS for the binomial_coefficient * Fix the tests request * Update binomial_coefficient.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update binomial_coefficient.py --------- Co-authored-by: Christian Clauss Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- maths/binomial_coefficient.py | 46 +++++++++++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/maths/binomial_coefficient.py b/maths/binomial_coefficient.py index 6d5b46cb5861..24c54326e305 100644 --- a/maths/binomial_coefficient.py +++ b/maths/binomial_coefficient.py @@ -1,10 +1,48 @@ def binomial_coefficient(n: int, r: int) -> int: """ - Find binomial coefficient using pascals triangle. + Find binomial coefficient using Pascal's triangle. + + Calculate C(n, r) using Pascal's triangle. + + :param n: The total number of items. + :param r: The number of items to choose. + :return: The binomial coefficient C(n, r). >>> binomial_coefficient(10, 5) 252 + >>> binomial_coefficient(10, 0) + 1 + >>> binomial_coefficient(0, 10) + 1 + >>> binomial_coefficient(10, 10) + 1 + >>> binomial_coefficient(5, 2) + 10 + >>> binomial_coefficient(5, 6) + 0 + >>> binomial_coefficient(3, 5) + 0 + >>> binomial_coefficient(-2, 3) + Traceback (most recent call last): + ... + ValueError: n and r must be non-negative integers + >>> binomial_coefficient(5, -1) + Traceback (most recent call last): + ... + ValueError: n and r must be non-negative integers + >>> binomial_coefficient(10.1, 5) + Traceback (most recent call last): + ... + TypeError: 'float' object cannot be interpreted as an integer + >>> binomial_coefficient(10, 5.1) + Traceback (most recent call last): + ... + TypeError: 'float' object cannot be interpreted as an integer """ + if n < 0 or r < 0: + raise ValueError("n and r must be non-negative integers") + if 0 in (n, r): + return 1 c = [0 for i in range(r + 1)] # nc0 = 1 c[0] = 1 @@ -17,4 +55,8 @@ def binomial_coefficient(n: int, r: int) -> int: return c[r] -print(binomial_coefficient(n=10, r=5)) +if __name__ == "__main__": + from doctest import testmod + + testmod() + print(binomial_coefficient(n=10, r=5)) From aeee0f42a5684e42cb77b664570dd2d29e04b7c1 Mon Sep 17 00:00:00 2001 From: Hardik Pawar <97388607+Hardvan@users.noreply.github.com> Date: Tue, 24 Oct 2023 20:06:24 +0530 Subject: [PATCH 459/757] Add doctests for fractional knapsack (#10891) * Add doctests for fractional knapsack * Update greedy_methods/fractional_knapsack.py Co-authored-by: Christian Clauss * Run doctests * Update greedy_methods/fractional_knapsack.py Co-authored-by: Christian Clauss * Update greedy_methods/fractional_knapsack.py --------- Co-authored-by: Christian Clauss --- greedy_methods/fractional_knapsack.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/greedy_methods/fractional_knapsack.py b/greedy_methods/fractional_knapsack.py index 58976d40c02b..d52b56f23569 100644 --- a/greedy_methods/fractional_knapsack.py +++ b/greedy_methods/fractional_knapsack.py @@ -6,6 +6,30 @@ def frac_knapsack(vl, wt, w, n): """ >>> frac_knapsack([60, 100, 120], [10, 20, 30], 50, 3) 240.0 + >>> frac_knapsack([10, 40, 30, 50], [5, 4, 6, 3], 10, 4) + 105.0 + >>> frac_knapsack([10, 40, 30, 50], [5, 4, 6, 3], 8, 4) + 95.0 + >>> frac_knapsack([10, 40, 30, 50], [5, 4, 6], 8, 4) + 60.0 + >>> frac_knapsack([10, 40, 30], [5, 4, 6, 3], 8, 4) + 60.0 + >>> frac_knapsack([10, 40, 30, 50], [5, 4, 6, 3], 0, 4) + 0 + >>> frac_knapsack([10, 40, 30, 50], [5, 4, 6, 3], 8, 0) + 95.0 + >>> frac_knapsack([10, 40, 30, 50], [5, 4, 6, 3], -8, 4) + 0 + >>> frac_knapsack([10, 40, 30, 50], [5, 4, 6, 3], 8, -4) + 95.0 + >>> frac_knapsack([10, 40, 30, 50], [5, 4, 6, 3], 800, 4) + 130 + >>> frac_knapsack([10, 40, 30, 50], [5, 4, 6, 3], 8, 400) + 95.0 + >>> frac_knapsack("ABCD", [5, 4, 6, 3], 8, 400) + Traceback (most recent call last): + ... + TypeError: unsupported operand type(s) for /: 'str' and 'int' """ r = sorted(zip(vl, wt), key=lambda x: x[0] / x[1], reverse=True) From 28302db9417daf769bec3aface9016afabeb5133 Mon Sep 17 00:00:00 2001 From: Dhruv Manilawala Date: Tue, 24 Oct 2023 21:23:17 +0530 Subject: [PATCH 460/757] Remove myself from CODEOWNERS (#10220) --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 05cd709a8f62..a0531cdeec69 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -7,7 +7,7 @@ # Order is important. The last matching pattern has the most precedence. -/.* @cclauss @dhruvmanila +/.* @cclauss # /arithmetic_analysis/ From fd227d802661d4be4babae66075542dc153b4569 Mon Sep 17 00:00:00 2001 From: Hardik Pawar <97388607+Hardvan@users.noreply.github.com> Date: Wed, 25 Oct 2023 03:05:38 +0530 Subject: [PATCH 461/757] Add function docstrings, comments and type hints (#10893) * Add function docstrings, comments and type hints * Fix type mismatch * Fix type hint error * Fix float to int error * Update ford_fulkerson.py * Update ford_fulkerson.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update ford_fulkerson.py --------- Co-authored-by: Christian Clauss Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- networking_flow/ford_fulkerson.py | 109 ++++++++++++++++++++++-------- 1 file changed, 80 insertions(+), 29 deletions(-) diff --git a/networking_flow/ford_fulkerson.py b/networking_flow/ford_fulkerson.py index 716ed508e679..7d5fb522e012 100644 --- a/networking_flow/ford_fulkerson.py +++ b/networking_flow/ford_fulkerson.py @@ -1,39 +1,95 @@ -# Ford-Fulkerson Algorithm for Maximum Flow Problem """ +Ford-Fulkerson Algorithm for Maximum Flow Problem +* https://en.wikipedia.org/wiki/Ford%E2%80%93Fulkerson_algorithm + Description: - (1) Start with initial flow as 0; - (2) Choose augmenting path from source to sink and add path to flow; + (1) Start with initial flow as 0 + (2) Choose the augmenting path from source to sink and add the path to flow """ +graph = [ + [0, 16, 13, 0, 0, 0], + [0, 0, 10, 12, 0, 0], + [0, 4, 0, 0, 14, 0], + [0, 0, 9, 0, 0, 20], + [0, 0, 0, 7, 0, 4], + [0, 0, 0, 0, 0, 0], +] + + +def breadth_first_search(graph: list, source: int, sink: int, parents: list) -> bool: + """ + This function returns True if there is a node that has not iterated. + + Args: + graph: Adjacency matrix of graph + source: Source + sink: Sink + parents: Parent list + + Returns: + True if there is a node that has not iterated. + >>> breadth_first_search(graph, 0, 5, [-1, -1, -1, -1, -1, -1]) + True + >>> breadth_first_search(graph, 0, 6, [-1, -1, -1, -1, -1, -1]) + Traceback (most recent call last): + ... + IndexError: list index out of range + """ + visited = [False] * len(graph) # Mark all nodes as not visited + queue = [] # breadth-first search queue -def bfs(graph, s, t, parent): - # Return True if there is node that has not iterated. - visited = [False] * len(graph) - queue = [] - queue.append(s) - visited[s] = True + # Source node + queue.append(source) + visited[source] = True while queue: - u = queue.pop(0) - for ind in range(len(graph[u])): - if visited[ind] is False and graph[u][ind] > 0: + u = queue.pop(0) # Pop the front node + # Traverse all adjacent nodes of u + for ind, node in enumerate(graph[u]): + if visited[ind] is False and node > 0: queue.append(ind) visited[ind] = True - parent[ind] = u + parents[ind] = u + return visited[sink] - return visited[t] +def ford_fulkerson(graph: list, source: int, sink: int) -> int: + """ + This function returns the maximum flow from source to sink in the given graph. -def ford_fulkerson(graph, source, sink): - # This array is filled by BFS and to store path + CAUTION: This function changes the given graph. + + Args: + graph: Adjacency matrix of graph + source: Source + sink: Sink + + Returns: + Maximum flow + + >>> test_graph = [ + ... [0, 16, 13, 0, 0, 0], + ... [0, 0, 10, 12, 0, 0], + ... [0, 4, 0, 0, 14, 0], + ... [0, 0, 9, 0, 0, 20], + ... [0, 0, 0, 7, 0, 4], + ... [0, 0, 0, 0, 0, 0], + ... ] + >>> ford_fulkerson(test_graph, 0, 5) + 23 + """ + # This array is filled by breadth-first search and to store path parent = [-1] * (len(graph)) max_flow = 0 - while bfs(graph, source, sink, parent): - path_flow = float("Inf") + + # While there is a path from source to sink + while breadth_first_search(graph, source, sink, parent): + path_flow = int(1e9) # Infinite value s = sink while s != source: - # Find the minimum value in select path + # Find the minimum value in the selected path path_flow = min(path_flow, graph[parent[s]][s]) s = parent[s] @@ -45,17 +101,12 @@ def ford_fulkerson(graph, source, sink): graph[u][v] -= path_flow graph[v][u] += path_flow v = parent[v] + return max_flow -graph = [ - [0, 16, 13, 0, 0, 0], - [0, 0, 10, 12, 0, 0], - [0, 4, 0, 0, 14, 0], - [0, 0, 9, 0, 0, 20], - [0, 0, 0, 7, 0, 4], - [0, 0, 0, 0, 0, 0], -] +if __name__ == "__main__": + from doctest import testmod -source, sink = 0, 5 -print(ford_fulkerson(graph, source, sink)) + testmod() + print(f"{ford_fulkerson(graph, source=0, sink=5) = }") From dab4e648965a92a7f73aa5fe6ad8b8afc0fde7f9 Mon Sep 17 00:00:00 2001 From: Bisma nadeem <130698042+Bisma-Nadeemm@users.noreply.github.com> Date: Wed, 25 Oct 2023 02:51:04 +0500 Subject: [PATCH 462/757] Code enhancements in binary_insertion_sort.py (#10918) * Code enhancements in binary_insertion_sort.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Apply suggestions from code review --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- sorts/binary_insertion_sort.py | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/sorts/binary_insertion_sort.py b/sorts/binary_insertion_sort.py index 8d41025583b1..50653a99e7ce 100644 --- a/sorts/binary_insertion_sort.py +++ b/sorts/binary_insertion_sort.py @@ -12,10 +12,11 @@ def binary_insertion_sort(collection: list) -> list: - """Pure implementation of the binary insertion sort algorithm in Python - :param collection: some mutable ordered collection with heterogeneous - comparable items inside - :return: the same collection ordered by ascending + """ + Sorts a list using the binary insertion sort algorithm. + + :param collection: A mutable ordered collection with comparable items. + :return: The same collection ordered in ascending order. Examples: >>> binary_insertion_sort([0, 4, 1234, 4, 1]) @@ -39,23 +40,27 @@ def binary_insertion_sort(collection: list) -> list: n = len(collection) for i in range(1, n): - val = collection[i] + value_to_insert = collection[i] low = 0 high = i - 1 while low <= high: mid = (low + high) // 2 - if val < collection[mid]: + if value_to_insert < collection[mid]: high = mid - 1 else: low = mid + 1 for j in range(i, low, -1): collection[j] = collection[j - 1] - collection[low] = val + collection[low] = value_to_insert return collection -if __name__ == "__main__": +if __name__ == "__main": user_input = input("Enter numbers separated by a comma:\n").strip() - unsorted = [int(item) for item in user_input.split(",")] - print(binary_insertion_sort(unsorted)) + try: + unsorted = [int(item) for item in user_input.split(",")] + except ValueError: + print("Invalid input. Please enter valid integers separated by commas.") + raise + print(f"{binary_insertion_sort(unsorted) = }") From 76acc6de607eebdc0d0d5c68396030d8e240a6ea Mon Sep 17 00:00:00 2001 From: Iyiola Aloko <48067557+ialoko@users.noreply.github.com> Date: Tue, 24 Oct 2023 17:53:31 -0400 Subject: [PATCH 463/757] Adding doctests to frequency_finder.py (#10341) * Update frequency_finder.py * Update frequency_finder.py --------- Co-authored-by: Christian Clauss --- strings/frequency_finder.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/strings/frequency_finder.py b/strings/frequency_finder.py index 19f97afbbe37..8479c81ae464 100644 --- a/strings/frequency_finder.py +++ b/strings/frequency_finder.py @@ -49,6 +49,15 @@ def get_item_at_index_zero(x: tuple) -> str: def get_frequency_order(message: str) -> str: + """ + Get the frequency order of the letters in the given string + >>> get_frequency_order('Hello World') + 'LOWDRHEZQXJKVBPYGFMUCSNIAT' + >>> get_frequency_order('Hello@') + 'LHOEZQXJKVBPYGFWMUCDRSNIAT' + >>> get_frequency_order('h') + 'HZQXJKVBPYGFWMUCLDRSNIOATE' + """ letter_to_freq = get_letter_count(message) freq_to_letter: dict[int, list[str]] = { freq: [] for letter, freq in letter_to_freq.items() From c2c6cb0f5c46346cab99121d236b2f5748e3c1df Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Wed, 25 Oct 2023 22:28:23 +0200 Subject: [PATCH 464/757] Add dataclasses to binary_search_tree.py (#10920) --- .../binary_tree/binary_search_tree.py | 69 ++++++++++++++++--- 1 file changed, 60 insertions(+), 9 deletions(-) diff --git a/data_structures/binary_tree/binary_search_tree.py b/data_structures/binary_tree/binary_search_tree.py index a706d21e3bb2..38691c4755c9 100644 --- a/data_structures/binary_tree/binary_search_tree.py +++ b/data_structures/binary_tree/binary_search_tree.py @@ -14,6 +14,16 @@ >>> t.insert(8, 3, 6, 1, 10, 14, 13, 4, 7) >>> print(" ".join(repr(i.value) for i in t.traversal_tree())) 8 3 1 6 4 7 10 14 13 + +>>> tuple(i.value for i in t.traversal_tree(inorder)) +(1, 3, 4, 6, 7, 8, 10, 13, 14) +>>> tuple(t) +(1, 3, 4, 6, 7, 8, 10, 13, 14) +>>> t.find_kth_smallest(3, t.root) +4 +>>> tuple(t)[3-1] +4 + >>> print(" ".join(repr(i.value) for i in t.traversal_tree(postorder))) 1 4 7 6 3 13 14 10 8 >>> t.remove(20) @@ -39,8 +49,12 @@ Test existence >>> t.search(6) is not None True +>>> 6 in t +True >>> t.search(-1) is not None False +>>> -1 in t +False >>> t.search(6).is_right True @@ -49,26 +63,47 @@ >>> t.get_max().value 14 +>>> max(t) +14 >>> t.get_min().value 1 +>>> min(t) +1 >>> t.empty() False +>>> not t +False >>> for i in testlist: ... t.remove(i) >>> t.empty() True +>>> not t +True """ +from __future__ import annotations -from collections.abc import Iterable +from collections.abc import Iterable, Iterator +from dataclasses import dataclass from typing import Any +@dataclass class Node: - def __init__(self, value: int | None = None): - self.value = value - self.parent: Node | None = None # Added in order to delete a node easier - self.left: Node | None = None - self.right: Node | None = None + value: int + left: Node | None = None + right: Node | None = None + parent: Node | None = None # Added in order to delete a node easier + + def __iter__(self) -> Iterator[int]: + """ + >>> list(Node(0)) + [0] + >>> list(Node(0, Node(-1), Node(1), None)) + [-1, 0, 1] + """ + yield from self.left or [] + yield self.value + yield from self.right or [] def __repr__(self) -> str: from pprint import pformat @@ -79,12 +114,18 @@ def __repr__(self) -> str: @property def is_right(self) -> bool: - return self.parent is not None and self is self.parent.right + return bool(self.parent and self is self.parent.right) +@dataclass class BinarySearchTree: - def __init__(self, root: Node | None = None): - self.root = root + root: Node | None = None + + def __bool__(self) -> bool: + return bool(self.root) + + def __iter__(self) -> Iterator[int]: + yield from self.root or [] def __str__(self) -> str: """ @@ -227,6 +268,16 @@ def find_kth_smallest(self, k: int, node: Node) -> int: return arr[k - 1] +def inorder(curr_node: Node | None) -> list[Node]: + """ + inorder (left, self, right) + """ + node_list = [] + if curr_node is not None: + node_list = inorder(curr_node.left) + [curr_node] + inorder(curr_node.right) + return node_list + + def postorder(curr_node: Node | None) -> list[Node]: """ postOrder (left, right, self) From 3d0a409ce119e1b7734ebaa2ffea660f5359080b Mon Sep 17 00:00:00 2001 From: AdityaAtoZ <129844626+AdityaAtoZ@users.noreply.github.com> Date: Thu, 26 Oct 2023 02:03:35 +0530 Subject: [PATCH 465/757] Improved Equilibrium Index of an Array. (#10899) * Improved Equilibrium Index of an Array. This is the modifications made to the original code: 1. Create Doctest Instructions: Python "doctest" can be executed by running the following command: python -m doctest -v equilibrium_index.py. 2. Deleted Argument {size}: Deleted the `size` argument because `len(arr)} allows the array's length to be determined inside the function, simplifying and improving the readability of the function signature. 3. Used {enumerate}: To improve code readability and indicate that we're working with element-index pairs, we iterated through the array using both elements and their indices using the `enumerate` function. 4. Optimized the Loop: To prevent pointless additions, the loop was improved by initializing {left_sum} with the value of the first element (arr[0]). Furthermore, since the beginning and last items (0 and size - 1) cannot be equilibrium indices, there is no need to check them, saving further computations. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .../arrays/equilibrium_index_in_array.py | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/data_structures/arrays/equilibrium_index_in_array.py b/data_structures/arrays/equilibrium_index_in_array.py index 4099896d226d..8802db6206bb 100644 --- a/data_structures/arrays/equilibrium_index_in_array.py +++ b/data_structures/arrays/equilibrium_index_in_array.py @@ -2,7 +2,7 @@ Find the Equilibrium Index of an Array. Reference: https://www.geeksforgeeks.org/equilibrium-index-of-an-array/ -Python doctests can be run with the following command: +Python doctest can be run with the following command: python -m doctest -v equilibrium_index.py Given a sequence arr[] of size n, this function returns @@ -20,35 +20,34 @@ """ -def equilibrium_index(arr: list[int], size: int) -> int: +def equilibrium_index(arr: list[int]) -> int: """ Find the equilibrium index of an array. Args: - arr : The input array of integers. - size : The size of the array. + arr (list[int]): The input array of integers. Returns: int: The equilibrium index or -1 if no equilibrium index exists. Examples: - >>> equilibrium_index([-7, 1, 5, 2, -4, 3, 0], 7) + >>> equilibrium_index([-7, 1, 5, 2, -4, 3, 0]) 3 - >>> equilibrium_index([1, 2, 3, 4, 5], 5) + >>> equilibrium_index([1, 2, 3, 4, 5]) -1 - >>> equilibrium_index([1, 1, 1, 1, 1], 5) + >>> equilibrium_index([1, 1, 1, 1, 1]) 2 - >>> equilibrium_index([2, 4, 6, 8, 10, 3], 6) + >>> equilibrium_index([2, 4, 6, 8, 10, 3]) -1 """ total_sum = sum(arr) left_sum = 0 - for i in range(size): - total_sum -= arr[i] + for i, value in enumerate(arr): + total_sum -= value if left_sum == total_sum: return i - left_sum += arr[i] + left_sum += value return -1 From e1e5963812c3f59a60181307bccf15792ad2406c Mon Sep 17 00:00:00 2001 From: Bisma nadeem <130698042+Bisma-Nadeemm@users.noreply.github.com> Date: Thu, 26 Oct 2023 02:26:54 +0500 Subject: [PATCH 466/757] Code Enhancements in merge_sort.py (#10911) * Code Enhancements in merge_sort.py This enhanced code includes improved variable naming, error handling for user input, and more detailed docstrings. It's now more robust and readable. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- sorts/merge_sort.py | 47 ++++++++++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/sorts/merge_sort.py b/sorts/merge_sort.py index e80b1cb226ec..0628b848b794 100644 --- a/sorts/merge_sort.py +++ b/sorts/merge_sort.py @@ -12,9 +12,13 @@ def merge_sort(collection: list) -> list: """ - :param collection: some mutable ordered collection with heterogeneous - comparable items inside - :return: the same collection ordered by ascending + Sorts a list using the merge sort algorithm. + + :param collection: A mutable ordered collection with comparable items. + :return: The same collection ordered in ascending order. + + Time Complexity: O(n log n) + Examples: >>> merge_sort([0, 5, 3, 2, 2]) [0, 2, 2, 3, 5] @@ -26,31 +30,34 @@ def merge_sort(collection: list) -> list: def merge(left: list, right: list) -> list: """ - Merge left and right. + Merge two sorted lists into a single sorted list. - :param left: left collection - :param right: right collection - :return: merge result + :param left: Left collection + :param right: Right collection + :return: Merged result """ - - def _merge(): - while left and right: - yield (left if left[0] <= right[0] else right).pop(0) - yield from left - yield from right - - return list(_merge()) + result = [] + while left and right: + result.append(left.pop(0) if left[0] <= right[0] else right.pop(0)) + result.extend(left) + result.extend(right) + return result if len(collection) <= 1: return collection - mid = len(collection) // 2 - return merge(merge_sort(collection[:mid]), merge_sort(collection[mid:])) + mid_index = len(collection) // 2 + return merge(merge_sort(collection[:mid_index]), merge_sort(collection[mid_index:])) if __name__ == "__main__": import doctest doctest.testmod() - user_input = input("Enter numbers separated by a comma:\n").strip() - unsorted = [int(item) for item in user_input.split(",")] - print(*merge_sort(unsorted), sep=",") + + try: + user_input = input("Enter numbers separated by a comma:\n").strip() + unsorted = [int(item) for item in user_input.split(",")] + sorted_list = merge_sort(unsorted) + print(*sorted_list, sep=",") + except ValueError: + print("Invalid input. Please enter valid integers separated by commas.") From 0ffe506ea79fcd9820a6c9bf3194a3bfcd677b57 Mon Sep 17 00:00:00 2001 From: Humzafazal72 <125209604+Humzafazal72@users.noreply.github.com> Date: Thu, 26 Oct 2023 04:05:35 +0500 Subject: [PATCH 467/757] added mean absolute percentage error (#10464) * added mean absolute percentage error * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * added mean_absolute_percentage_error * added mean_absolute_percentage_error * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * added mean_absolute_percentage_error * added mean_absolute_percentage_error * added mean absolute percentage error * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * added mean absolute percentage error * added mean absolute percentage error * added mean absolute percentage error * added mean absolute percentage error * added mean absolute percentage error * Update machine_learning/loss_functions.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Tianyi Zheng --- machine_learning/loss_functions.py | 45 ++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/machine_learning/loss_functions.py b/machine_learning/loss_functions.py index ef34296360e2..e5b7a713b6f2 100644 --- a/machine_learning/loss_functions.py +++ b/machine_learning/loss_functions.py @@ -297,6 +297,51 @@ def mean_squared_logarithmic_error(y_true: np.ndarray, y_pred: np.ndarray) -> fl return np.mean(squared_logarithmic_errors) +def mean_absolute_percentage_error( + y_true: np.ndarray, y_pred: np.ndarray, epsilon: float = 1e-15 +) -> float: + """ + Calculate the Mean Absolute Percentage Error between y_true and y_pred. + + Mean Absolute Percentage Error calculates the average of the absolute + percentage differences between the predicted and true values. + + Formula = (Σ|y_true[i]-Y_pred[i]/y_true[i]|)/n + + Source: https://stephenallwright.com/good-mape-score/ + + Parameters: + y_true (np.ndarray): Numpy array containing true/target values. + y_pred (np.ndarray): Numpy array containing predicted values. + + Returns: + float: The Mean Absolute Percentage error between y_true and y_pred. + + Examples: + >>> y_true = np.array([10, 20, 30, 40]) + >>> y_pred = np.array([12, 18, 33, 45]) + >>> mean_absolute_percentage_error(y_true, y_pred) + 0.13125 + + >>> y_true = np.array([1, 2, 3, 4]) + >>> y_pred = np.array([2, 3, 4, 5]) + >>> mean_absolute_percentage_error(y_true, y_pred) + 0.5208333333333333 + + >>> y_true = np.array([34, 37, 44, 47, 48, 48, 46, 43, 32, 27, 26, 24]) + >>> y_pred = np.array([37, 40, 46, 44, 46, 50, 45, 44, 34, 30, 22, 23]) + >>> mean_absolute_percentage_error(y_true, y_pred) + 0.064671076436071 + """ + if len(y_true) != len(y_pred): + raise ValueError("The length of the two arrays should be the same.") + + y_true = np.where(y_true == 0, epsilon, y_true) + absolute_percentage_diff = np.abs((y_true - y_pred) / y_true) + + return np.mean(absolute_percentage_diff) + + if __name__ == "__main__": import doctest From 0e7f8284a32286534691e437d67405b6a09b10e1 Mon Sep 17 00:00:00 2001 From: Dale Dai <145884899+CouldNot@users.noreply.github.com> Date: Wed, 25 Oct 2023 22:27:46 -0700 Subject: [PATCH 468/757] Add error tests in doctest and fix error message (#10930) * Add error tests in doctest and fix error message * Change AssertationError to ValueError * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- maths/prime_check.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/maths/prime_check.py b/maths/prime_check.py index c17877a57705..f1bc4def2469 100644 --- a/maths/prime_check.py +++ b/maths/prime_check.py @@ -29,12 +29,19 @@ def is_prime(number: int) -> bool: True >>> is_prime(67483) False + >>> is_prime(16.1) + Traceback (most recent call last): + ... + ValueError: is_prime() only accepts positive integers + >>> is_prime(-4) + Traceback (most recent call last): + ... + ValueError: is_prime() only accepts positive integers """ # precondition - assert isinstance(number, int) and ( - number >= 0 - ), "'number' must been an int and positive" + if not isinstance(number, int) or not number >= 0: + raise ValueError("is_prime() only accepts positive integers") if 1 < number < 4: # 2 and 3 are primes @@ -64,7 +71,7 @@ def test_primes(self): assert is_prime(29) def test_not_primes(self): - with pytest.raises(AssertionError): + with pytest.raises(ValueError): is_prime(-19) assert not is_prime( 0 From 1a5d5cf93d30fc123af680ee9c58eb955932972b Mon Sep 17 00:00:00 2001 From: Megan Payne Date: Thu, 26 Oct 2023 07:31:47 +0200 Subject: [PATCH 469/757] Mean absolute error (#10927) * added mean absolute error to loss_functions.py * added doctest to mean absolute error to loss_functions.py * fixed long line in loss_functions.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fixed error in MAE * Update machine_learning/loss_functions.py Co-authored-by: Tianyi Zheng --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Tianyi Zheng --- machine_learning/loss_functions.py | 37 ++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/machine_learning/loss_functions.py b/machine_learning/loss_functions.py index e5b7a713b6f2..ea1f390e358a 100644 --- a/machine_learning/loss_functions.py +++ b/machine_learning/loss_functions.py @@ -261,6 +261,43 @@ def mean_squared_error(y_true: np.ndarray, y_pred: np.ndarray) -> float: return np.mean(squared_errors) +def mean_absolute_error(y_true: np.ndarray, y_pred: np.ndarray) -> float: + """ + Calculates the Mean Absolute Error (MAE) between ground truth (observed) + and predicted values. + + MAE measures the absolute difference between true values and predicted values. + + Equation: + MAE = (1/n) * Σ(abs(y_true - y_pred)) + + Reference: https://en.wikipedia.org/wiki/Mean_absolute_error + + Parameters: + - y_true: The true values (ground truth) + - y_pred: The predicted values + + >>> true_values = np.array([1.0, 2.0, 3.0, 4.0, 5.0]) + >>> predicted_values = np.array([0.8, 2.1, 2.9, 4.2, 5.2]) + >>> np.isclose(mean_absolute_error(true_values, predicted_values), 0.16) + True + >>> true_values = np.array([1.0, 2.0, 3.0, 4.0, 5.0]) + >>> predicted_values = np.array([0.8, 2.1, 2.9, 4.2, 5.2]) + >>> np.isclose(mean_absolute_error(true_values, predicted_values), 2.16) + False + >>> true_labels = np.array([1.0, 2.0, 3.0, 4.0, 5.0]) + >>> predicted_probs = np.array([0.3, 0.8, 0.9, 5.2]) + >>> mean_absolute_error(true_labels, predicted_probs) + Traceback (most recent call last): + ... + ValueError: Input arrays must have the same length. + """ + if len(y_true) != len(y_pred): + raise ValueError("Input arrays must have the same length.") + + return np.mean(abs(y_true - y_pred)) + + def mean_squared_logarithmic_error(y_true: np.ndarray, y_pred: np.ndarray) -> float: """ Calculate the mean squared logarithmic error (MSLE) between ground truth and From a8f05fe0a5d8b7e88d99c160b177ff3f3f07edcc Mon Sep 17 00:00:00 2001 From: Ed Date: Thu, 26 Oct 2023 00:02:35 -0700 Subject: [PATCH 470/757] Add doctests and type hints (#10974) * Add doctests and type hints * Apply suggestions from code review * Update tarjans_scc.py * Update tarjans_scc.py --------- Co-authored-by: Tianyi Zheng --- graphs/tarjans_scc.py | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/graphs/tarjans_scc.py b/graphs/tarjans_scc.py index dfd2e52704d5..a75dc4d2ca95 100644 --- a/graphs/tarjans_scc.py +++ b/graphs/tarjans_scc.py @@ -1,7 +1,7 @@ from collections import deque -def tarjan(g): +def tarjan(g: list[list[int]]) -> list[list[int]]: """ Tarjan's algo for finding strongly connected components in a directed graph @@ -19,15 +19,30 @@ def tarjan(g): Complexity: strong_connect() is called at most once for each node and has a complexity of O(|E|) as it is DFS. Therefore this has complexity O(|V| + |E|) for a graph G = (V, E) + + >>> tarjan([[2, 3, 4], [2, 3, 4], [0, 1, 3], [0, 1, 2], [1]]) + [[4, 3, 1, 2, 0]] + >>> tarjan([[], [], [], []]) + [[0], [1], [2], [3]] + >>> a = [0, 1, 2, 3, 4, 5, 4] + >>> b = [1, 0, 3, 2, 5, 4, 0] + >>> n = 7 + >>> sorted(tarjan(create_graph(n, list(zip(a, b))))) == sorted( + ... tarjan(create_graph(n, list(zip(a[::-1], b[::-1]))))) + True + >>> a = [0, 1, 2, 3, 4, 5, 6] + >>> b = [0, 1, 2, 3, 4, 5, 6] + >>> sorted(tarjan(create_graph(n, list(zip(a, b))))) + [[0], [1], [2], [3], [4], [5], [6]] """ n = len(g) - stack = deque() + stack: deque[int] = deque() on_stack = [False for _ in range(n)] index_of = [-1 for _ in range(n)] lowlink_of = index_of[:] - def strong_connect(v, index, components): + def strong_connect(v: int, index: int, components: list[list[int]]) -> int: index_of[v] = index # the number when this node is seen lowlink_of[v] = index # lowest rank node reachable from here index += 1 @@ -57,7 +72,7 @@ def strong_connect(v, index, components): components.append(component) return index - components = [] + components: list[list[int]] = [] for v in range(n): if index_of[v] == -1: strong_connect(v, 0, components) @@ -65,8 +80,16 @@ def strong_connect(v, index, components): return components -def create_graph(n, edges): - g = [[] for _ in range(n)] +def create_graph(n: int, edges: list[tuple[int, int]]) -> list[list[int]]: + """ + >>> n = 7 + >>> source = [0, 0, 1, 2, 3, 3, 4, 4, 6] + >>> target = [1, 3, 2, 0, 1, 4, 5, 6, 5] + >>> edges = list(zip(source, target)) + >>> create_graph(n, edges) + [[1, 3], [2], [0], [1, 4], [5, 6], [], [5]] + """ + g: list[list[int]] = [[] for _ in range(n)] for u, v in edges: g[u].append(v) return g From c71c280726fb4e9487833993042e54598fe94fd9 Mon Sep 17 00:00:00 2001 From: Ravi Kumar <119737193+ravi-ivar-7@users.noreply.github.com> Date: Thu, 26 Oct 2023 12:50:28 +0530 Subject: [PATCH 471/757] added runge kutta gills method to maths/ numerical_analysis (#10967) * added runge kutta gills method * Apply suggestions from code review --------- Co-authored-by: Tianyi Zheng --- maths/numerical_analysis/runge_kutta_gills.py | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 maths/numerical_analysis/runge_kutta_gills.py diff --git a/maths/numerical_analysis/runge_kutta_gills.py b/maths/numerical_analysis/runge_kutta_gills.py new file mode 100644 index 000000000000..2bd9cd6129b8 --- /dev/null +++ b/maths/numerical_analysis/runge_kutta_gills.py @@ -0,0 +1,89 @@ +""" +Use the Runge-Kutta-Gill's method of order 4 to solve Ordinary Differential Equations. + +https://www.geeksforgeeks.org/gills-4th-order-method-to-solve-differential-equations/ +Author : Ravi Kumar +""" +from collections.abc import Callable +from math import sqrt + +import numpy as np + + +def runge_kutta_gills( + func: Callable[[float, float], float], + x_initial: float, + y_initial: float, + step_size: float, + x_final: float, +) -> np.ndarray: + """ + Solve an Ordinary Differential Equations using Runge-Kutta-Gills Method of order 4. + + args: + func: An ordinary differential equation (ODE) as function of x and y. + x_initial: The initial value of x. + y_initial: The initial value of y. + step_size: The increment value of x. + x_final: The final value of x. + + Returns: + Solution of y at each nodal point + + >>> def f(x, y): + ... return (x-y)/2 + >>> y = runge_kutta_gills(f, 0, 3, 0.2, 5) + >>> y[-1] + 3.4104259225717537 + + >>> def f(x,y): + ... return x + >>> y = runge_kutta_gills(f, -1, 0, 0.2, 0) + >>> y + array([ 0. , -0.18, -0.32, -0.42, -0.48, -0.5 ]) + + >>> def f(x, y): + ... return x + y + >>> y = runge_kutta_gills(f, 0, 0, 0.2, -1) + Traceback (most recent call last): + ... + ValueError: The final value of x must be greater than initial value of x. + + >>> def f(x, y): + ... return x + >>> y = runge_kutta_gills(f, -1, 0, -0.2, 0) + Traceback (most recent call last): + ... + ValueError: Step size must be positive. + """ + if x_initial >= x_final: + raise ValueError( + "The final value of x must be greater than initial value of x." + ) + + if step_size <= 0: + raise ValueError("Step size must be positive.") + + n = int((x_final - x_initial) / step_size) + y = np.zeros(n + 1) + y[0] = y_initial + for i in range(n): + k1 = step_size * func(x_initial, y[i]) + k2 = step_size * func(x_initial + step_size / 2, y[i] + k1 / 2) + k3 = step_size * func( + x_initial + step_size / 2, + y[i] + (-0.5 + 1 / sqrt(2)) * k1 + (1 - 1 / sqrt(2)) * k2, + ) + k4 = step_size * func( + x_initial + step_size, y[i] - (1 / sqrt(2)) * k2 + (1 + 1 / sqrt(2)) * k3 + ) + + y[i + 1] = y[i] + (k1 + (2 - sqrt(2)) * k2 + (2 + sqrt(2)) * k3 + k4) / 6 + x_initial += step_size + return y + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From dd7d18d49e9edc635f692b1f3db933e8ea717023 Mon Sep 17 00:00:00 2001 From: Suyash Dongre <109069262+Suyashd999@users.noreply.github.com> Date: Thu, 26 Oct 2023 13:25:56 +0530 Subject: [PATCH 472/757] Added doctest, docstring and typehint for sigmoid_function & cost_function (#10828) * Added doctest for sigmoid_function & cost_function * Update logistic_regression.py * Update logistic_regression.py * Minor formatting changes in doctests * Apply suggestions from code review * Made requested changes in logistic_regression.py * Apply suggestions from code review --------- Co-authored-by: Tianyi Zheng --- machine_learning/logistic_regression.py | 60 ++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/machine_learning/logistic_regression.py b/machine_learning/logistic_regression.py index f9da0104ab4b..59a70fd65cf9 100644 --- a/machine_learning/logistic_regression.py +++ b/machine_learning/logistic_regression.py @@ -27,7 +27,7 @@ # classification problems -def sigmoid_function(z): +def sigmoid_function(z: float | np.ndarray) -> float | np.ndarray: """ Also known as Logistic Function. @@ -42,11 +42,63 @@ def sigmoid_function(z): @param z: input to the function @returns: returns value in the range 0 to 1 + + Examples: + >>> sigmoid_function(4) + 0.9820137900379085 + >>> sigmoid_function(np.array([-3, 3])) + array([0.04742587, 0.95257413]) + >>> sigmoid_function(np.array([-3, 3, 1])) + array([0.04742587, 0.95257413, 0.73105858]) + >>> sigmoid_function(np.array([-0.01, -2, -1.9])) + array([0.49750002, 0.11920292, 0.13010847]) + >>> sigmoid_function(np.array([-1.3, 5.3, 12])) + array([0.21416502, 0.9950332 , 0.99999386]) + >>> sigmoid_function(np.array([0.01, 0.02, 4.1])) + array([0.50249998, 0.50499983, 0.9836975 ]) + >>> sigmoid_function(np.array([0.8])) + array([0.68997448]) """ return 1 / (1 + np.exp(-z)) -def cost_function(h, y): +def cost_function(h: np.ndarray, y: np.ndarray) -> float: + """ + Cost function quantifies the error between predicted and expected values. + The cost function used in Logistic Regression is called Log Loss + or Cross Entropy Function. + + J(θ) = (1/m) * Σ [ -y * log(hθ(x)) - (1 - y) * log(1 - hθ(x)) ] + + Where: + - J(θ) is the cost that we want to minimize during training + - m is the number of training examples + - Σ represents the summation over all training examples + - y is the actual binary label (0 or 1) for a given example + - hθ(x) is the predicted probability that x belongs to the positive class + + @param h: the output of sigmoid function. It is the estimated probability + that the input example 'x' belongs to the positive class + + @param y: the actual binary label associated with input example 'x' + + Examples: + >>> estimations = sigmoid_function(np.array([0.3, -4.3, 8.1])) + >>> cost_function(h=estimations,y=np.array([1, 0, 1])) + 0.18937868932131605 + >>> estimations = sigmoid_function(np.array([4, 3, 1])) + >>> cost_function(h=estimations,y=np.array([1, 0, 0])) + 1.459999655669926 + >>> estimations = sigmoid_function(np.array([4, -3, -1])) + >>> cost_function(h=estimations,y=np.array([1,0,0])) + 0.1266663223365915 + >>> estimations = sigmoid_function(0) + >>> cost_function(h=estimations,y=np.array([1])) + 0.6931471805599453 + + References: + - https://en.wikipedia.org/wiki/Logistic_regression + """ return (-y * np.log(h) - (1 - y) * np.log(1 - h)).mean() @@ -75,6 +127,10 @@ def logistic_reg(alpha, x, y, max_iterations=70000): # In[68]: if __name__ == "__main__": + import doctest + + doctest.testmod() + iris = datasets.load_iris() x = iris.data[:, :2] y = (iris.target != 0) * 1 From e5a6a97c3277fbf849b77d1328720782128ecafd Mon Sep 17 00:00:00 2001 From: Sanjay <146640686+san-jay-14@users.noreply.github.com> Date: Thu, 26 Oct 2023 13:28:40 +0530 Subject: [PATCH 473/757] Added Lens formulae to the Physics repository (#10187) * Added Lens formulae to the Physics repository * Resolved the commented issues * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update lens_formulae.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Tianyi Zheng --- physics/lens_formulae.py | 131 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 physics/lens_formulae.py diff --git a/physics/lens_formulae.py b/physics/lens_formulae.py new file mode 100644 index 000000000000..162f3a8f3b29 --- /dev/null +++ b/physics/lens_formulae.py @@ -0,0 +1,131 @@ +""" +This module has functions which calculate focal length of lens, distance of +image from the lens and distance of object from the lens. +The above is calculated using the lens formula. + +In optics, the relationship between the distance of the image (v), +the distance of the object (u), and +the focal length (f) of the lens is given by the formula known as the Lens formula. +The Lens formula is applicable for convex as well as concave lenses. The formula +is given as follows: + +------------------- +| 1/f = 1/v + 1/u | +------------------- + +Where + f = focal length of the lens in meters. + v = distance of the image from the lens in meters. + u = distance of the object from the lens in meters. + +To make our calculations easy few assumptions are made while deriving the formula +which are important to keep in mind before solving this equation. +The assumptions are as follows: + 1. The object O is a point object lying somewhere on the principle axis. + 2. The lens is thin. + 3. The aperture of the lens taken must be small. + 4. The angles of incidence and angle of refraction should be small. + +Sign convention is a set of rules to set signs for image distance, object distance, +focal length, etc +for mathematical analysis of image formation. According to it: + 1. Object is always placed to the left of lens. + 2. All distances are measured from the optical centre of the mirror. + 3. Distances measured in the direction of the incident ray are positive and + the distances measured in the direction opposite + to that of the incident rays are negative. + 4. Distances measured along y-axis above the principal axis are positive and + that measured along y-axis below the principal + axis are negative. + +Note: Sign convention can be reversed and will still give the correct results. + +Reference for Sign convention: +https://www.toppr.com/ask/content/concept/sign-convention-for-lenses-210246/ + +Reference for assumptions: +https://testbook.com/physics/derivation-of-lens-maker-formula +""" + + +def focal_length_of_lens( + object_distance_from_lens: float, image_distance_from_lens: float +) -> float: + """ + Doctests: + >>> from math import isclose + >>> isclose(focal_length_of_lens(10,4), 6.666666666666667) + True + >>> from math import isclose + >>> isclose(focal_length_of_lens(2.7,5.8), -5.0516129032258075) + True + >>> focal_length_of_lens(0, 20) # doctest: +NORMALIZE_WHITESPACE + Traceback (most recent call last): + ... + ValueError: Invalid inputs. Enter non zero values with respect + to the sign convention. + """ + + if object_distance_from_lens == 0 or image_distance_from_lens == 0: + raise ValueError( + "Invalid inputs. Enter non zero values with respect to the sign convention." + ) + focal_length = 1 / ( + (1 / image_distance_from_lens) - (1 / object_distance_from_lens) + ) + return focal_length + + +def object_distance( + focal_length_of_lens: float, image_distance_from_lens: float +) -> float: + """ + Doctests: + >>> from math import isclose + >>> isclose(object_distance(10,40), -13.333333333333332) + True + + >>> from math import isclose + >>> isclose(object_distance(6.2,1.5), 1.9787234042553192) + True + + >>> object_distance(0, 20) # doctest: +NORMALIZE_WHITESPACE + Traceback (most recent call last): + ... + ValueError: Invalid inputs. Enter non zero values with respect + to the sign convention. + """ + + if image_distance_from_lens == 0 or focal_length_of_lens == 0: + raise ValueError( + "Invalid inputs. Enter non zero values with respect to the sign convention." + ) + + object_distance = 1 / ((1 / image_distance_from_lens) - (1 / focal_length_of_lens)) + return object_distance + + +def image_distance( + focal_length_of_lens: float, object_distance_from_lens: float +) -> float: + """ + Doctests: + >>> from math import isclose + >>> isclose(image_distance(50,40), 22.22222222222222) + True + >>> from math import isclose + >>> isclose(image_distance(5.3,7.9), 3.1719696969696973) + True + + >>> object_distance(0, 20) # doctest: +NORMALIZE_WHITESPACE + Traceback (most recent call last): + ... + ValueError: Invalid inputs. Enter non zero values with respect + to the sign convention. + """ + if object_distance_from_lens == 0 or focal_length_of_lens == 0: + raise ValueError( + "Invalid inputs. Enter non zero values with respect to the sign convention." + ) + image_distance = 1 / ((1 / object_distance_from_lens) + (1 / focal_length_of_lens)) + return image_distance From e791a2067baf3b23c0413f32c7388e3b2a95744e Mon Sep 17 00:00:00 2001 From: Mary-0165 <146911989+Mary-0165@users.noreply.github.com> Date: Thu, 26 Oct 2023 13:40:13 +0530 Subject: [PATCH 474/757] Capacitor equivalence algorithm (#9814) * capacitor equivalence algorithm * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Apply suggestions from code review * Update capacitor_equivalence.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Tianyi Zheng --- electronics/capacitor_equivalence.py | 53 ++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 electronics/capacitor_equivalence.py diff --git a/electronics/capacitor_equivalence.py b/electronics/capacitor_equivalence.py new file mode 100644 index 000000000000..274b18afb3ef --- /dev/null +++ b/electronics/capacitor_equivalence.py @@ -0,0 +1,53 @@ +# https://farside.ph.utexas.edu/teaching/316/lectures/node46.html + +from __future__ import annotations + + +def capacitor_parallel(capacitors: list[float]) -> float: + """ + Ceq = C1 + C2 + ... + Cn + Calculate the equivalent resistance for any number of capacitors in parallel. + >>> capacitor_parallel([5.71389, 12, 3]) + 20.71389 + >>> capacitor_parallel([5.71389, 12, -3]) + Traceback (most recent call last): + ... + ValueError: Capacitor at index 2 has a negative value! + """ + sum_c = 0.0 + for index, capacitor in enumerate(capacitors): + if capacitor < 0: + msg = f"Capacitor at index {index} has a negative value!" + raise ValueError(msg) + sum_c += capacitor + return sum_c + + +def capacitor_series(capacitors: list[float]) -> float: + """ + Ceq = 1/ (1/C1 + 1/C2 + ... + 1/Cn) + >>> capacitor_series([5.71389, 12, 3]) + 1.6901062252507735 + >>> capacitor_series([5.71389, 12, -3]) + Traceback (most recent call last): + ... + ValueError: Capacitor at index 2 has a negative or zero value! + >>> capacitor_series([5.71389, 12, 0.000]) + Traceback (most recent call last): + ... + ValueError: Capacitor at index 2 has a negative or zero value! + """ + + first_sum = 0.0 + for index, capacitor in enumerate(capacitors): + if capacitor <= 0: + msg = f"Capacitor at index {index} has a negative or zero value!" + raise ValueError(msg) + first_sum += 1 / capacitor + return 1 / first_sum + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From ade2837e410ec286819f0f4fd977bb411a95b379 Mon Sep 17 00:00:00 2001 From: Saurabh Mahapatra <98408932+its-100rabh@users.noreply.github.com> Date: Thu, 26 Oct 2023 13:55:08 +0530 Subject: [PATCH 475/757] Update capitalize.py (#10573) * Update capitalize.py * Update strings/capitalize.py --------- Co-authored-by: Tianyi Zheng --- strings/capitalize.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/strings/capitalize.py b/strings/capitalize.py index e7e97c2beb53..c0b45e0d9614 100644 --- a/strings/capitalize.py +++ b/strings/capitalize.py @@ -3,7 +3,8 @@ def capitalize(sentence: str) -> str: """ - This function will capitalize the first letter of a sentence or a word + Capitalizes the first letter of a sentence or word. + >>> capitalize("hello world") 'Hello world' >>> capitalize("123 hello world") @@ -17,6 +18,10 @@ def capitalize(sentence: str) -> str: """ if not sentence: return "" + + # Create a dictionary that maps lowercase letters to uppercase letters + # Capitalize the first character if it's a lowercase letter + # Concatenate the capitalized character with the rest of the string lower_to_upper = dict(zip(ascii_lowercase, ascii_uppercase)) return lower_to_upper.get(sentence[0], sentence[0]) + sentence[1:] From 6497917352c73371730e50f063acd61cf4268076 Mon Sep 17 00:00:00 2001 From: Neha <129765919+neha3423@users.noreply.github.com> Date: Thu, 26 Oct 2023 14:24:30 +0530 Subject: [PATCH 476/757] Added Kth largest element algorithm (#10687) * neha3423 * neha3423 * neha3423 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * neha3423 * neha3423 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * neha323 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * neha3423 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * neha3423 * neha3423 * neha3423 * neha3423 * Added test case for tuple * Update kth_largest_element.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- data_structures/arrays/kth_largest_element.py | 117 ++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 data_structures/arrays/kth_largest_element.py diff --git a/data_structures/arrays/kth_largest_element.py b/data_structures/arrays/kth_largest_element.py new file mode 100644 index 000000000000..f25cc68e9b72 --- /dev/null +++ b/data_structures/arrays/kth_largest_element.py @@ -0,0 +1,117 @@ +""" +Given an array of integers and an integer k, find the kth largest element in the array. + +https://stackoverflow.com/questions/251781 +""" + + +def partition(arr: list[int], low: int, high: int) -> int: + """ + Partitions list based on the pivot element. + + This function rearranges the elements in the input list 'elements' such that + all elements greater than or equal to the chosen pivot are on the right side + of the pivot, and all elements smaller than the pivot are on the left side. + + Args: + arr: The list to be partitioned + low: The lower index of the list + high: The higher index of the list + + Returns: + int: The index of pivot element after partitioning + + Examples: + >>> partition([3, 1, 4, 5, 9, 2, 6, 5, 3, 5], 0, 9) + 4 + >>> partition([7, 1, 4, 5, 9, 2, 6, 5, 8], 0, 8) + 1 + >>> partition(['apple', 'cherry', 'date', 'banana'], 0, 3) + 2 + >>> partition([3.1, 1.2, 5.6, 4.7], 0, 3) + 1 + """ + pivot = arr[high] + i = low - 1 + for j in range(low, high): + if arr[j] >= pivot: + i += 1 + arr[i], arr[j] = arr[j], arr[i] + arr[i + 1], arr[high] = arr[high], arr[i + 1] + return i + 1 + + +def kth_largest_element(arr: list[int], position: int) -> int: + """ + Finds the kth largest element in a list. + Should deliver similar results to: + ```python + def kth_largest_element(arr, position): + return sorted(arr)[-position] + ``` + + Args: + nums: The list of numbers. + k: The position of the desired kth largest element. + + Returns: + int: The kth largest element. + + Examples: + >>> kth_largest_element([3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5], 3) + 5 + >>> kth_largest_element([2, 5, 6, 1, 9, 3, 8, 4, 7, 3, 5], 1) + 9 + >>> kth_largest_element([2, 5, 6, 1, 9, 3, 8, 4, 7, 3, 5], -2) + Traceback (most recent call last): + ... + ValueError: Invalid value of 'position' + >>> kth_largest_element([9, 1, 3, 6, 7, 9, 8, 4, 2, 4, 9], 110) + Traceback (most recent call last): + ... + ValueError: Invalid value of 'position' + >>> kth_largest_element([1, 2, 4, 3, 5, 9, 7, 6, 5, 9, 3], 0) + Traceback (most recent call last): + ... + ValueError: Invalid value of 'position' + >>> kth_largest_element(['apple', 'cherry', 'date', 'banana'], 2) + 'cherry' + >>> kth_largest_element([3.1, 1.2, 5.6, 4.7,7.9,5,0], 2) + 5.6 + >>> kth_largest_element([-2, -5, -4, -1], 1) + -1 + >>> kth_largest_element([], 1) + -1 + >>> kth_largest_element([3.1, 1.2, 5.6, 4.7, 7.9, 5, 0], 1.5) + Traceback (most recent call last): + ... + ValueError: The position should be an integer + >>> kth_largest_element((4, 6, 1, 2), 4) + Traceback (most recent call last): + ... + TypeError: 'tuple' object does not support item assignment + """ + if not arr: + return -1 + if not isinstance(position, int): + raise ValueError("The position should be an integer") + if not 1 <= position <= len(arr): + raise ValueError("Invalid value of 'position'") + low, high = 0, len(arr) - 1 + while low <= high: + if low > len(arr) - 1 or high < 0: + return -1 + pivot_index = partition(arr, low, high) + if pivot_index == position - 1: + return arr[pivot_index] + elif pivot_index > position - 1: + high = pivot_index - 1 + else: + low = pivot_index + 1 + return -1 + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 42c49ee1174506dd04dc2dff422328cdb7dc7201 Mon Sep 17 00:00:00 2001 From: Habip Akyol <127725897+habipakyol@users.noreply.github.com> Date: Thu, 26 Oct 2023 14:24:17 +0300 Subject: [PATCH 477/757] Fix typo in haralick_descriptors.py (#10988) --- computer_vision/haralick_descriptors.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/computer_vision/haralick_descriptors.py b/computer_vision/haralick_descriptors.py index 413cea304f6c..007421e34263 100644 --- a/computer_vision/haralick_descriptors.py +++ b/computer_vision/haralick_descriptors.py @@ -253,13 +253,13 @@ def matrix_concurrency(image: np.ndarray, coordinate: tuple[int, int]) -> np.nda def haralick_descriptors(matrix: np.ndarray) -> list[float]: - """Calculates all 8 Haralick descriptors based on co-occurence input matrix. + """Calculates all 8 Haralick descriptors based on co-occurrence input matrix. All descriptors are as follows: Maximum probability, Inverse Difference, Homogeneity, Entropy, Energy, Dissimilarity, Contrast and Correlation Args: - matrix: Co-occurence matrix to use as base for calculating descriptors. + matrix: Co-occurrence matrix to use as base for calculating descriptors. Returns: Reverse ordered list of resulting descriptors From 29b8ccdc2f685e815f12fd6e9e8b9faee21e338d Mon Sep 17 00:00:00 2001 From: Suyash Dongre <109069262+Suyashd999@users.noreply.github.com> Date: Thu, 26 Oct 2023 17:42:28 +0530 Subject: [PATCH 478/757] Added doctest to hash_table.py (#10984) --- data_structures/hashing/hash_table.py | 81 +++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/data_structures/hashing/hash_table.py b/data_structures/hashing/hash_table.py index 7ca2f7c401cf..5bf431328da4 100644 --- a/data_structures/hashing/hash_table.py +++ b/data_structures/hashing/hash_table.py @@ -21,6 +21,29 @@ def __init__( self._keys: dict = {} def keys(self): + """ + The keys function returns a dictionary containing the key value pairs. + key being the index number in hash table and value being the data value. + + Examples: + 1. creating HashTable with size 10 and inserting 3 elements + >>> ht = HashTable(10) + >>> ht.insert_data(10) + >>> ht.insert_data(20) + >>> ht.insert_data(30) + >>> ht.keys() + {0: 10, 1: 20, 2: 30} + + 2. creating HashTable with size 5 and inserting 5 elements + >>> ht = HashTable(5) + >>> ht.insert_data(5) + >>> ht.insert_data(4) + >>> ht.insert_data(3) + >>> ht.insert_data(2) + >>> ht.insert_data(1) + >>> ht.keys() + {0: 5, 4: 4, 3: 3, 2: 2, 1: 1} + """ return self._keys def balanced_factor(self): @@ -37,6 +60,43 @@ def _step_by_step(self, step_ord): print(self.values) def bulk_insert(self, values): + """ + bulk_insert is used for entering more than one element at a time + in the HashTable. + + Examples: + 1. + >>> ht = HashTable(5) + >>> ht.bulk_insert((10,20,30)) + step 1 + [0, 1, 2, 3, 4] + [10, None, None, None, None] + step 2 + [0, 1, 2, 3, 4] + [10, 20, None, None, None] + step 3 + [0, 1, 2, 3, 4] + [10, 20, 30, None, None] + + 2. + >>> ht = HashTable(5) + >>> ht.bulk_insert([5,4,3,2,1]) + step 1 + [0, 1, 2, 3, 4] + [5, None, None, None, None] + step 2 + [0, 1, 2, 3, 4] + [5, None, None, None, 4] + step 3 + [0, 1, 2, 3, 4] + [5, None, None, 3, 4] + step 4 + [0, 1, 2, 3, 4] + [5, None, 2, 3, 4] + step 5 + [0, 1, 2, 3, 4] + [5, 1, 2, 3, 4] + """ i = 1 self.__aux_list = values for value in values: @@ -69,6 +129,21 @@ def rehashing(self): self.insert_data(value) def insert_data(self, data): + """ + insert_data is used for inserting a single element at a time in the HashTable. + + Examples: + + >>> ht = HashTable(3) + >>> ht.insert_data(5) + >>> ht.keys() + {2: 5} + >>> ht = HashTable(5) + >>> ht.insert_data(30) + >>> ht.insert_data(50) + >>> ht.keys() + {0: 30, 1: 50} + """ key = self.hash_function(data) if self.values[key] is None: @@ -84,3 +159,9 @@ def insert_data(self, data): else: self.rehashing() self.insert_data(data) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 69f7f3208e0297cea9ccd9d02b9fb690f2ee3b93 Mon Sep 17 00:00:00 2001 From: Akash_Jambulkar <97665573+Akash-Jambulkar@users.noreply.github.com> Date: Thu, 26 Oct 2023 17:57:31 +0530 Subject: [PATCH 479/757] Update cocktail_shaker_sort.py (#10987) * Update cocktail_shaker_sort.py Added a docstring with clear explanations of the function and its parameters. Changed variable names i, start, and end for better readability. Improved comments to describe the purpose of each section of the algorithm. Adjusted the loop ranges to make the code more concise and readable. Removed redundant comments and variable assignments. Provided a clear message when printing the sorted list. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update cocktail_shaker_sort.py * typing: ignore[operator] * Update cocktail_shaker_sort.py * Update cocktail_shaker_sort.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- sorts/cocktail_shaker_sort.py | 52 +++++++++++++++++++++++++---------- 1 file changed, 37 insertions(+), 15 deletions(-) diff --git a/sorts/cocktail_shaker_sort.py b/sorts/cocktail_shaker_sort.py index b738ff31d768..de126426d986 100644 --- a/sorts/cocktail_shaker_sort.py +++ b/sorts/cocktail_shaker_sort.py @@ -1,40 +1,62 @@ -""" https://en.wikipedia.org/wiki/Cocktail_shaker_sort """ +""" +An implementation of the cocktail shaker sort algorithm in pure Python. +https://en.wikipedia.org/wiki/Cocktail_shaker_sort +""" -def cocktail_shaker_sort(unsorted: list) -> list: + +def cocktail_shaker_sort(arr: list[int]) -> list[int]: """ - Pure implementation of the cocktail shaker sort algorithm in Python. + Sorts a list using the Cocktail Shaker Sort algorithm. + + :param arr: List of elements to be sorted. + :return: Sorted list. + >>> cocktail_shaker_sort([4, 5, 2, 1, 2]) [1, 2, 2, 4, 5] - >>> cocktail_shaker_sort([-4, 5, 0, 1, 2, 11]) [-4, 0, 1, 2, 5, 11] - >>> cocktail_shaker_sort([0.1, -2.4, 4.4, 2.2]) [-2.4, 0.1, 2.2, 4.4] - >>> cocktail_shaker_sort([1, 2, 3, 4, 5]) [1, 2, 3, 4, 5] - >>> cocktail_shaker_sort([-4, -5, -24, -7, -11]) [-24, -11, -7, -5, -4] + >>> cocktail_shaker_sort(["elderberry", "banana", "date", "apple", "cherry"]) + ['apple', 'banana', 'cherry', 'date', 'elderberry'] + >>> cocktail_shaker_sort((-4, -5, -24, -7, -11)) + Traceback (most recent call last): + ... + TypeError: 'tuple' object does not support item assignment """ - for i in range(len(unsorted) - 1, 0, -1): + start, end = 0, len(arr) - 1 + + while start < end: swapped = False - for j in range(i, 0, -1): - if unsorted[j] < unsorted[j - 1]: - unsorted[j], unsorted[j - 1] = unsorted[j - 1], unsorted[j] + # Pass from left to right + for i in range(start, end): + if arr[i] > arr[i + 1]: + arr[i], arr[i + 1] = arr[i + 1], arr[i] swapped = True - for j in range(i): - if unsorted[j] > unsorted[j + 1]: - unsorted[j], unsorted[j + 1] = unsorted[j + 1], unsorted[j] + if not swapped: + break + + end -= 1 # Decrease the end pointer after each pass + + # Pass from right to left + for i in range(end, start, -1): + if arr[i] < arr[i - 1]: + arr[i], arr[i - 1] = arr[i - 1], arr[i] swapped = True if not swapped: break - return unsorted + + start += 1 # Increase the start pointer after each pass + + return arr if __name__ == "__main__": From 579250363db1975440c75f4f6d486b88ff568cdb Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Thu, 26 Oct 2023 08:36:53 -0400 Subject: [PATCH 480/757] Speed up `dijkstra_bankers_algorithm.py` (#10861) * updating DIRECTORY.md * Rename dijkstra_bankers_algorithm.py * Remove sleep() call * updating DIRECTORY.md --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 2 +- other/{dijkstra_bankers_algorithm.py => bankers_algorithm.py} | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) rename other/{dijkstra_bankers_algorithm.py => bankers_algorithm.py} (99%) diff --git a/DIRECTORY.md b/DIRECTORY.md index 5f8eabb6df88..d108acf8dcfb 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -774,8 +774,8 @@ ## Other * [Activity Selection](other/activity_selection.py) * [Alternative List Arrange](other/alternative_list_arrange.py) + * [Bankers Algorithm](other/bankers_algorithm.py) * [Davis Putnam Logemann Loveland](other/davis_putnam_logemann_loveland.py) - * [Dijkstra Bankers Algorithm](other/dijkstra_bankers_algorithm.py) * [Doomsday](other/doomsday.py) * [Fischer Yates Shuffle](other/fischer_yates_shuffle.py) * [Gauss Easter](other/gauss_easter.py) diff --git a/other/dijkstra_bankers_algorithm.py b/other/bankers_algorithm.py similarity index 99% rename from other/dijkstra_bankers_algorithm.py rename to other/bankers_algorithm.py index be7bceba125d..858eb0b2c524 100644 --- a/other/dijkstra_bankers_algorithm.py +++ b/other/bankers_algorithm.py @@ -17,8 +17,6 @@ from __future__ import annotations -import time - import numpy as np test_claim_vector = [8, 5, 9, 7] @@ -216,7 +214,6 @@ def __pretty_data(self): "Initial Available Resources: " + " ".join(str(x) for x in self.__available_resources()) ) - time.sleep(1) if __name__ == "__main__": From 8adbf47c75e6881f8778fc4e9490628c71cc9fa1 Mon Sep 17 00:00:00 2001 From: Kishan Kumar Rai Date: Thu, 26 Oct 2023 18:21:28 +0530 Subject: [PATCH 481/757] Fix Typo & Grammatical Errors (#10980) --- CONTRIBUTING.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index bf3420185c1a..096582e45afa 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,20 +2,20 @@ ## Before contributing -Welcome to [TheAlgorithms/Python](https://github.com/TheAlgorithms/Python)! Before sending your pull requests, make sure that you __read the whole guidelines__. If you have any doubt on the contributing guide, please feel free to [state it clearly in an issue](https://github.com/TheAlgorithms/Python/issues/new) or ask the community in [Gitter](https://gitter.im/TheAlgorithms/community). +Welcome to [TheAlgorithms/Python](https://github.com/TheAlgorithms/Python)! Before submitting your pull requests, please ensure that you __read the whole guidelines__. If you have any doubts about the contributing guide, please feel free to [state it clearly in an issue](https://github.com/TheAlgorithms/Python/issues/new) or ask the community on [Gitter](https://gitter.im/TheAlgorithms/community). ## Contributing ### Contributor -We are very happy that you are considering implementing algorithms and data structures for others! This repository is referenced and used by learners from all over the globe. Being one of our contributors, you agree and confirm that: +We are delighted that you are considering implementing algorithms and data structures for others! This repository is referenced and used by learners from all over the globe. By being one of our contributors, you agree and confirm that: -- You did your work - no plagiarism allowed +- You did your work - no plagiarism allowed. - Any plagiarized work will not be merged. -- Your work will be distributed under [MIT License](LICENSE.md) once your pull request is merged -- Your submitted work fulfils or mostly fulfils our styles and standards +- Your work will be distributed under [MIT License](LICENSE.md) once your pull request is merged. +- Your submitted work fulfills or mostly fulfills our styles and standards. -__New implementation__ is welcome! For example, new solutions for a problem, different representations for a graph data structure or algorithm designs with different complexity but __identical implementation__ of an existing implementation is not allowed. Please check whether the solution is already implemented or not before submitting your pull request. +__New implementation__ is welcome! For example, new solutions for a problem, different representations for a graph data structure or algorithm designs with different complexity, but __identical implementation__ of an existing implementation is not allowed. Please check whether the solution is already implemented or not before submitting your pull request. __Improving comments__ and __writing proper tests__ are also highly welcome. @@ -23,7 +23,7 @@ __Improving comments__ and __writing proper tests__ are also highly welcome. We appreciate any contribution, from fixing a grammar mistake in a comment to implementing complex algorithms. Please read this section if you are contributing your work. -Your contribution will be tested by our [automated testing on GitHub Actions](https://github.com/TheAlgorithms/Python/actions) to save time and mental energy. After you have submitted your pull request, you should see the GitHub Actions tests start to run at the bottom of your submission page. If those tests fail, then click on the ___details___ button try to read through the GitHub Actions output to understand the failure. If you do not understand, please leave a comment on your submission page and a community member will try to help. +Your contribution will be tested by our [automated testing on GitHub Actions](https://github.com/TheAlgorithms/Python/actions) to save time and mental energy. After you have submitted your pull request, you should see the GitHub Actions tests start to run at the bottom of your submission page. If those tests fail, then click on the ___details___ button to read through the GitHub Actions output to understand the failure. If you do not understand, please leave a comment on your submission page and a community member will try to help. #### Issues @@ -58,7 +58,7 @@ Algorithms should: * contain doctests that test both valid and erroneous input values * return all calculation results instead of printing or plotting them -Algorithms in this repo should not be how-to examples for existing Python packages. Instead, they should perform internal calculations or manipulations to convert input values into different output values. Those calculations or manipulations can use data types, classes, or functions of existing Python packages but each algorithm in this repo should add unique value. +Algorithms in this repo should not be how-to examples for existing Python packages. Instead, they should perform internal calculations or manipulations to convert input values into different output values. Those calculations or manipulations can use data types, classes, or functions of existing Python packages but each algorithm in this repo should add unique value. #### Pre-commit plugin Use [pre-commit](https://pre-commit.com/#installation) to automatically format your code to match our coding style: @@ -77,7 +77,7 @@ pre-commit run --all-files --show-diff-on-failure We want your work to be readable by others; therefore, we encourage you to note the following: -- Please write in Python 3.12+. For instance: `print()` is a function in Python 3 so `print "Hello"` will *not* work but `print("Hello")` will. +- Please write in Python 3.12+. For instance: `print()` is a function in Python 3 so `print "Hello"` will *not* work but `print("Hello")` will. - Please focus hard on the naming of functions, classes, and variables. Help your reader by using __descriptive names__ that can help you to remove redundant comments. - Single letter variable names are *old school* so please avoid them unless their life only spans a few lines. - Expand acronyms because `gcd()` is hard to understand but `greatest_common_divisor()` is not. @@ -145,7 +145,7 @@ We want your work to be readable by others; therefore, we encourage you to note python3 -m doctest -v my_submission.py ``` - The use of the Python builtin `input()` function is __not__ encouraged: + The use of the Python built-in `input()` function is __not__ encouraged: ```python input('Enter your input:') From 34b25c0c769b417e82bc32cd4d3a801637ee57ab Mon Sep 17 00:00:00 2001 From: Tiela Rose Black-Law <26930264+tielarose@users.noreply.github.com> Date: Thu, 26 Oct 2023 05:52:47 -0700 Subject: [PATCH 482/757] Add doctest for maths/primelib (#10978) --- maths/primelib.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/maths/primelib.py b/maths/primelib.py index e2d432e1846a..a26b0eaeb328 100644 --- a/maths/primelib.py +++ b/maths/primelib.py @@ -454,6 +454,8 @@ def kg_v(number1, number2): 40 >>> kg_v(824,67) 55208 + >>> kg_v(1, 10) + 10 >>> kg_v(0) Traceback (most recent call last): ... From a8dfd403f6df2275272190a55edb6a739880f6a9 Mon Sep 17 00:00:00 2001 From: Ed Date: Thu, 26 Oct 2023 07:33:42 -0700 Subject: [PATCH 483/757] Add new algorithm index_2d_array_in_1d (#10973) * Add new algorithm index_2d_array_in_1d * Add doctest for iter function * The power of dataclasses * Update index_2d_array_in_1d.py --------- Co-authored-by: Christian Clauss --- .../arrays/index_2d_array_in_1d.py | 105 ++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 data_structures/arrays/index_2d_array_in_1d.py diff --git a/data_structures/arrays/index_2d_array_in_1d.py b/data_structures/arrays/index_2d_array_in_1d.py new file mode 100644 index 000000000000..27a9fa5f9121 --- /dev/null +++ b/data_structures/arrays/index_2d_array_in_1d.py @@ -0,0 +1,105 @@ +""" +Retrieves the value of an 0-indexed 1D index from a 2D array. +There are two ways to retrieve value(s): + +1. Index2DArrayIterator(matrix) -> Iterator[int] +This iterator allows you to iterate through a 2D array by passing in the matrix and +calling next(your_iterator). You can also use the iterator in a loop. +Examples: +list(Index2DArrayIterator(matrix)) +set(Index2DArrayIterator(matrix)) +tuple(Index2DArrayIterator(matrix)) +sum(Index2DArrayIterator(matrix)) +-5 in Index2DArrayIterator(matrix) + +2. index_2d_array_in_1d(array: list[int], index: int) -> int +This function allows you to provide a 2D array and a 0-indexed 1D integer index, +and retrieves the integer value at that index. + +Python doctests can be run using this command: +python3 -m doctest -v index_2d_array_in_1d.py +""" + +from collections.abc import Iterator +from dataclasses import dataclass + + +@dataclass +class Index2DArrayIterator: + matrix: list[list[int]] + + def __iter__(self) -> Iterator[int]: + """ + >>> tuple(Index2DArrayIterator([[5], [-523], [-1], [34], [0]])) + (5, -523, -1, 34, 0) + >>> tuple(Index2DArrayIterator([[5, -523, -1], [34, 0]])) + (5, -523, -1, 34, 0) + >>> tuple(Index2DArrayIterator([[5, -523, -1, 34, 0]])) + (5, -523, -1, 34, 0) + >>> t = Index2DArrayIterator([[5, 2, 25], [23, 14, 5], [324, -1, 0]]) + >>> tuple(t) + (5, 2, 25, 23, 14, 5, 324, -1, 0) + >>> list(t) + [5, 2, 25, 23, 14, 5, 324, -1, 0] + >>> sorted(t) + [-1, 0, 2, 5, 5, 14, 23, 25, 324] + >>> tuple(t)[3] + 23 + >>> sum(t) + 397 + >>> -1 in t + True + >>> t = iter(Index2DArrayIterator([[5], [-523], [-1], [34], [0]])) + >>> next(t) + 5 + >>> next(t) + -523 + """ + for row in self.matrix: + yield from row + + +def index_2d_array_in_1d(array: list[list[int]], index: int) -> int: + """ + Retrieves the value of the one-dimensional index from a two-dimensional array. + + Args: + array: A 2D array of integers where all rows are the same size and all + columns are the same size. + index: A 1D index. + + Returns: + int: The 0-indexed value of the 1D index in the array. + + Examples: + >>> index_2d_array_in_1d([[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11]], 5) + 5 + >>> index_2d_array_in_1d([[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11]], -1) + Traceback (most recent call last): + ... + ValueError: index out of range + >>> index_2d_array_in_1d([[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11]], 12) + Traceback (most recent call last): + ... + ValueError: index out of range + >>> index_2d_array_in_1d([[]], 0) + Traceback (most recent call last): + ... + ValueError: no items in array + """ + rows = len(array) + cols = len(array[0]) + + if rows == 0 or cols == 0: + raise ValueError("no items in array") + + if index < 0 or index >= rows * cols: + raise ValueError("index out of range") + + return array[index // cols][index % cols] + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From fe4aad0ec94a2d2f28470dd8eaad3ff1bf74c5c8 Mon Sep 17 00:00:00 2001 From: Suyash Dongre <109069262+Suyashd999@users.noreply.github.com> Date: Thu, 26 Oct 2023 20:51:45 +0530 Subject: [PATCH 484/757] Added doctest & docstring to quadratic_probing.py (#10996) * Added doctest & docstring to quadratic_probing.py * Update quadratic_probing.py * Update quadratic_probing.py --- data_structures/hashing/quadratic_probing.py | 55 ++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/data_structures/hashing/quadratic_probing.py b/data_structures/hashing/quadratic_probing.py index 0930340a347f..2f3401ec8918 100644 --- a/data_structures/hashing/quadratic_probing.py +++ b/data_structures/hashing/quadratic_probing.py @@ -12,6 +12,55 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) def _collision_resolution(self, key, data=None): + """ + Quadratic probing is an open addressing scheme used for resolving + collisions in hash table. + + It works by taking the original hash index and adding successive + values of an arbitrary quadratic polynomial until open slot is found. + + Hash + 1², Hash + 2², Hash + 3² .... Hash + n² + + reference: + - https://en.wikipedia.org/wiki/Quadratic_probing + e.g: + 1. Create hash table with size 7 + >>> qp = QuadraticProbing(7) + >>> qp.insert_data(90) + >>> qp.insert_data(340) + >>> qp.insert_data(24) + >>> qp.insert_data(45) + >>> qp.insert_data(99) + >>> qp.insert_data(73) + >>> qp.insert_data(7) + >>> qp.keys() + {11: 45, 14: 99, 7: 24, 0: 340, 5: 73, 6: 90, 8: 7} + + 2. Create hash table with size 8 + >>> qp = QuadraticProbing(8) + >>> qp.insert_data(0) + >>> qp.insert_data(999) + >>> qp.insert_data(111) + >>> qp.keys() + {0: 0, 7: 999, 3: 111} + + 3. Try to add three data elements when the size is two + >>> qp = QuadraticProbing(2) + >>> qp.insert_data(0) + >>> qp.insert_data(999) + >>> qp.insert_data(111) + >>> qp.keys() + {0: 0, 4: 999, 1: 111} + + 4. Try to add three data elements when the size is one + >>> qp = QuadraticProbing(1) + >>> qp.insert_data(0) + >>> qp.insert_data(999) + >>> qp.insert_data(111) + >>> qp.keys() + {4: 999, 1: 111} + """ + i = 1 new_key = self.hash_function(key + i * i) @@ -27,3 +76,9 @@ def _collision_resolution(self, key, data=None): break return new_key + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 5987f861926c7560cd46c1e33c3cc2c0506c0ee1 Mon Sep 17 00:00:00 2001 From: Poojan Smart <44301271+PoojanSmart@users.noreply.github.com> Date: Fri, 27 Oct 2023 14:17:24 +0530 Subject: [PATCH 485/757] Add automatic differentiation algorithm (#10977) * Added automatic differentiation algorithm * file name changed * Resolved pre commit errors * updated dependency * added noqa for ignoring check * adding typing_extension for adding Self type in __new__ * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * sorted requirement.text dependency * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * resolved ruff --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- machine_learning/automatic_differentiation.py | 327 ++++++++++++++++++ requirements.txt | 3 +- 2 files changed, 329 insertions(+), 1 deletion(-) create mode 100644 machine_learning/automatic_differentiation.py diff --git a/machine_learning/automatic_differentiation.py b/machine_learning/automatic_differentiation.py new file mode 100644 index 000000000000..cd2e5cdaa782 --- /dev/null +++ b/machine_learning/automatic_differentiation.py @@ -0,0 +1,327 @@ +""" +Demonstration of the Automatic Differentiation (Reverse mode). + +Reference: https://en.wikipedia.org/wiki/Automatic_differentiation + +Author: Poojan Smart +Email: smrtpoojan@gmail.com +""" +from __future__ import annotations + +from collections import defaultdict +from enum import Enum +from types import TracebackType +from typing import Any + +import numpy as np +from typing_extensions import Self # noqa: UP035 + + +class OpType(Enum): + """ + Class represents list of supported operations on Variable for gradient calculation. + """ + + ADD = 0 + SUB = 1 + MUL = 2 + DIV = 3 + MATMUL = 4 + POWER = 5 + NOOP = 6 + + +class Variable: + """ + Class represents n-dimensional object which is used to wrap numpy array on which + operations will be performed and the gradient will be calculated. + + Examples: + >>> Variable(5.0) + Variable(5.0) + >>> Variable([5.0, 2.9]) + Variable([5. 2.9]) + >>> Variable([5.0, 2.9]) + Variable([1.0, 5.5]) + Variable([6. 8.4]) + >>> Variable([[8.0, 10.0]]) + Variable([[ 8. 10.]]) + """ + + def __init__(self, value: Any) -> None: + self.value = np.array(value) + + # pointers to the operations to which the Variable is input + self.param_to: list[Operation] = [] + # pointer to the operation of which the Variable is output of + self.result_of: Operation = Operation(OpType.NOOP) + + def __repr__(self) -> str: + return f"Variable({self.value})" + + def to_ndarray(self) -> np.ndarray: + return self.value + + def __add__(self, other: Variable) -> Variable: + result = Variable(self.value + other.value) + + with GradientTracker() as tracker: + # if tracker is enabled, computation graph will be updated + if tracker.enabled: + tracker.append(OpType.ADD, params=[self, other], output=result) + return result + + def __sub__(self, other: Variable) -> Variable: + result = Variable(self.value - other.value) + + with GradientTracker() as tracker: + # if tracker is enabled, computation graph will be updated + if tracker.enabled: + tracker.append(OpType.SUB, params=[self, other], output=result) + return result + + def __mul__(self, other: Variable) -> Variable: + result = Variable(self.value * other.value) + + with GradientTracker() as tracker: + # if tracker is enabled, computation graph will be updated + if tracker.enabled: + tracker.append(OpType.MUL, params=[self, other], output=result) + return result + + def __truediv__(self, other: Variable) -> Variable: + result = Variable(self.value / other.value) + + with GradientTracker() as tracker: + # if tracker is enabled, computation graph will be updated + if tracker.enabled: + tracker.append(OpType.DIV, params=[self, other], output=result) + return result + + def __matmul__(self, other: Variable) -> Variable: + result = Variable(self.value @ other.value) + + with GradientTracker() as tracker: + # if tracker is enabled, computation graph will be updated + if tracker.enabled: + tracker.append(OpType.MATMUL, params=[self, other], output=result) + return result + + def __pow__(self, power: int) -> Variable: + result = Variable(self.value**power) + + with GradientTracker() as tracker: + # if tracker is enabled, computation graph will be updated + if tracker.enabled: + tracker.append( + OpType.POWER, + params=[self], + output=result, + other_params={"power": power}, + ) + return result + + def add_param_to(self, param_to: Operation) -> None: + self.param_to.append(param_to) + + def add_result_of(self, result_of: Operation) -> None: + self.result_of = result_of + + +class Operation: + """ + Class represents operation between single or two Variable objects. + Operation objects contains type of operation, pointers to input Variable + objects and pointer to resulting Variable from the operation. + """ + + def __init__( + self, + op_type: OpType, + other_params: dict | None = None, + ) -> None: + self.op_type = op_type + self.other_params = {} if other_params is None else other_params + + def add_params(self, params: list[Variable]) -> None: + self.params = params + + def add_output(self, output: Variable) -> None: + self.output = output + + def __eq__(self, value) -> bool: + return self.op_type == value if isinstance(value, OpType) else False + + +class GradientTracker: + """ + Class contains methods to compute partial derivatives of Variable + based on the computation graph. + + Examples: + + >>> with GradientTracker() as tracker: + ... a = Variable([2.0, 5.0]) + ... b = Variable([1.0, 2.0]) + ... m = Variable([1.0, 2.0]) + ... c = a + b + ... d = a * b + ... e = c / d + >>> tracker.gradient(e, a) + array([-0.25, -0.04]) + >>> tracker.gradient(e, b) + array([-1. , -0.25]) + >>> tracker.gradient(e, m) is None + True + + >>> with GradientTracker() as tracker: + ... a = Variable([[2.0, 5.0]]) + ... b = Variable([[1.0], [2.0]]) + ... c = a @ b + >>> tracker.gradient(c, a) + array([[1., 2.]]) + >>> tracker.gradient(c, b) + array([[2.], + [5.]]) + + >>> with GradientTracker() as tracker: + ... a = Variable([[2.0, 5.0]]) + ... b = a ** 3 + >>> tracker.gradient(b, a) + array([[12., 75.]]) + """ + + instance = None + + def __new__(cls) -> Self: + """ + Executes at the creation of class object and returns if + object is already created. This class follows singleton + design pattern. + """ + if cls.instance is None: + cls.instance = super().__new__(cls) + return cls.instance + + def __init__(self) -> None: + self.enabled = False + + def __enter__(self) -> Self: + self.enabled = True + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + traceback: TracebackType | None, + ) -> None: + self.enabled = False + + def append( + self, + op_type: OpType, + params: list[Variable], + output: Variable, + other_params: dict | None = None, + ) -> None: + """ + Adds Operation object to the related Variable objects for + creating computational graph for calculating gradients. + + Args: + op_type: Operation type + params: Input parameters to the operation + output: Output variable of the operation + """ + operation = Operation(op_type, other_params=other_params) + param_nodes = [] + for param in params: + param.add_param_to(operation) + param_nodes.append(param) + output.add_result_of(operation) + + operation.add_params(param_nodes) + operation.add_output(output) + + def gradient(self, target: Variable, source: Variable) -> np.ndarray | None: + """ + Reverse accumulation of partial derivatives to calculate gradients + of target variable with respect to source variable. + + Args: + target: target variable for which gradients are calculated. + source: source variable with respect to which the gradients are + calculated. + + Returns: + Gradient of the source variable with respect to the target variable + """ + + # partial derivatives with respect to target + partial_deriv = defaultdict(lambda: 0) + partial_deriv[target] = np.ones_like(target.to_ndarray()) + + # iterating through each operations in the computation graph + operation_queue = [target.result_of] + while len(operation_queue) > 0: + operation = operation_queue.pop() + for param in operation.params: + # as per the chain rule, multiplying partial derivatives + # of variables with respect to the target + dparam_doutput = self.derivative(param, operation) + dparam_dtarget = dparam_doutput * partial_deriv[operation.output] + partial_deriv[param] += dparam_dtarget + + if param.result_of and param.result_of != OpType.NOOP: + operation_queue.append(param.result_of) + + return partial_deriv.get(source) + + def derivative(self, param: Variable, operation: Operation) -> np.ndarray: + """ + Compute the derivative of given operation/function + + Args: + param: variable to be differentiated + operation: function performed on the input variable + + Returns: + Derivative of input variable with respect to the output of + the operation + """ + params = operation.params + + if operation == OpType.ADD: + return np.ones_like(params[0].to_ndarray(), dtype=np.float64) + if operation == OpType.SUB: + if params[0] == param: + return np.ones_like(params[0].to_ndarray(), dtype=np.float64) + return -np.ones_like(params[1].to_ndarray(), dtype=np.float64) + if operation == OpType.MUL: + return ( + params[1].to_ndarray().T + if params[0] == param + else params[0].to_ndarray().T + ) + if operation == OpType.DIV: + if params[0] == param: + return 1 / params[1].to_ndarray() + return -params[0].to_ndarray() / (params[1].to_ndarray() ** 2) + if operation == OpType.MATMUL: + return ( + params[1].to_ndarray().T + if params[0] == param + else params[0].to_ndarray().T + ) + if operation == OpType.POWER: + power = operation.other_params["power"] + return power * (params[0].to_ndarray() ** (power - 1)) + + err_msg = f"invalid operation type: {operation.op_type}" + raise ValueError(err_msg) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() diff --git a/requirements.txt b/requirements.txt index 05d9f1e8c545..8937f6bb0dae 100644 --- a/requirements.txt +++ b/requirements.txt @@ -19,5 +19,6 @@ statsmodels sympy tensorflow ; python_version < '3.12' tweepy -xgboost # yulewalker # uncomment once audio_filters/equal_loudness_filter.py is fixed +typing_extensions +xgboost From 34eb9c529a74c3f3d1b878a1c7ca2529686b41f8 Mon Sep 17 00:00:00 2001 From: Suyash Dongre <109069262+Suyashd999@users.noreply.github.com> Date: Fri, 27 Oct 2023 18:06:43 +0530 Subject: [PATCH 486/757] Added doctest to hash_table.py (#11023) * Added doctest to hash_table.py * Update hash_table.py * Update hash_table.py * Update hash_table.py * Update hash_table.py * Apply suggestions from code review * Update hash_table.py --------- Co-authored-by: Christian Clauss --- data_structures/hashing/hash_table.py | 113 ++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) diff --git a/data_structures/hashing/hash_table.py b/data_structures/hashing/hash_table.py index 5bf431328da4..7fe57068f6a3 100644 --- a/data_structures/hashing/hash_table.py +++ b/data_structures/hashing/hash_table.py @@ -52,6 +52,30 @@ def balanced_factor(self): ) def hash_function(self, key): + """ + Generates hash for the given key value + + Examples: + + Creating HashTable with size 5 + >>> ht = HashTable(5) + >>> ht.hash_function(10) + 0 + >>> ht.hash_function(20) + 0 + >>> ht.hash_function(4) + 4 + >>> ht.hash_function(18) + 3 + >>> ht.hash_function(-18) + 2 + >>> ht.hash_function(18.5) + 3.5 + >>> ht.hash_function(0) + 0 + >>> ht.hash_function(-0) + 0 + """ return key % self.size_table def _step_by_step(self, step_ord): @@ -105,10 +129,99 @@ def bulk_insert(self, values): i += 1 def _set_value(self, key, data): + """ + _set_value functions allows to update value at a particular hash + + Examples: + 1. _set_value in HashTable of size 5 + >>> ht = HashTable(5) + >>> ht.insert_data(10) + >>> ht.insert_data(20) + >>> ht.insert_data(30) + >>> ht._set_value(0,15) + >>> ht.keys() + {0: 15, 1: 20, 2: 30} + + 2. _set_value in HashTable of size 2 + >>> ht = HashTable(2) + >>> ht.insert_data(17) + >>> ht.insert_data(18) + >>> ht.insert_data(99) + >>> ht._set_value(3,15) + >>> ht.keys() + {3: 15, 2: 17, 4: 99} + + 3. _set_value in HashTable when hash is not present + >>> ht = HashTable(2) + >>> ht.insert_data(17) + >>> ht.insert_data(18) + >>> ht.insert_data(99) + >>> ht._set_value(0,15) + >>> ht.keys() + {3: 18, 2: 17, 4: 99, 0: 15} + + 4. _set_value in HashTable when multiple hash are not present + >>> ht = HashTable(2) + >>> ht.insert_data(17) + >>> ht.insert_data(18) + >>> ht.insert_data(99) + >>> ht._set_value(0,15) + >>> ht._set_value(1,20) + >>> ht.keys() + {3: 18, 2: 17, 4: 99, 0: 15, 1: 20} + """ self.values[key] = data self._keys[key] = data def _collision_resolution(self, key, data=None): + """ + This method is a type of open addressing which is used for handling collision. + + In this implementation the concept of linear probing has been used. + + The hash table is searched sequentially from the original location of the + hash, if the new hash/location we get is already occupied we check for the next + hash/location. + + references: + - https://en.wikipedia.org/wiki/Linear_probing + + Examples: + 1. The collision will be with keys 18 & 99, so new hash will be created for 99 + >>> ht = HashTable(3) + >>> ht.insert_data(17) + >>> ht.insert_data(18) + >>> ht.insert_data(99) + >>> ht.keys() + {2: 17, 0: 18, 1: 99} + + 2. The collision will be with keys 17 & 101, so new hash + will be created for 101 + >>> ht = HashTable(4) + >>> ht.insert_data(17) + >>> ht.insert_data(18) + >>> ht.insert_data(99) + >>> ht.insert_data(101) + >>> ht.keys() + {1: 17, 2: 18, 3: 99, 0: 101} + + 2. The collision will be with all keys, so new hash will be created for all + >>> ht = HashTable(1) + >>> ht.insert_data(17) + >>> ht.insert_data(18) + >>> ht.insert_data(99) + >>> ht.keys() + {2: 17, 3: 18, 4: 99} + + 3. Trying to insert float key in hash + >>> ht = HashTable(1) + >>> ht.insert_data(17) + >>> ht.insert_data(18) + >>> ht.insert_data(99.99) + Traceback (most recent call last): + ... + TypeError: list indices must be integers or slices, not float + """ new_key = self.hash_function(key + 1) while self.values[new_key] is not None and self.values[new_key] != key: From e4eda145833565443be2e5ed4c805fbaaa9d964e Mon Sep 17 00:00:00 2001 From: Poojan Smart <44301271+PoojanSmart@users.noreply.github.com> Date: Fri, 27 Oct 2023 20:14:33 +0530 Subject: [PATCH 487/757] Add perplexity loss algorithm (#11028) --- machine_learning/loss_functions.py | 92 ++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/machine_learning/loss_functions.py b/machine_learning/loss_functions.py index ea1f390e358a..36a760326f3d 100644 --- a/machine_learning/loss_functions.py +++ b/machine_learning/loss_functions.py @@ -379,6 +379,98 @@ def mean_absolute_percentage_error( return np.mean(absolute_percentage_diff) +def perplexity_loss( + y_true: np.ndarray, y_pred: np.ndarray, epsilon: float = 1e-7 +) -> float: + """ + Calculate the perplexity for the y_true and y_pred. + + Compute the Perplexity which useful in predicting language model + accuracy in Natural Language Processing (NLP.) + Perplexity is measure of how certain the model in its predictions. + + Perplexity Loss = exp(-1/N (Σ ln(p(x))) + + Reference: + https://en.wikipedia.org/wiki/Perplexity + + Args: + y_true: Actual label encoded sentences of shape (batch_size, sentence_length) + y_pred: Predicted sentences of shape (batch_size, sentence_length, vocab_size) + epsilon: Small floating point number to avoid getting inf for log(0) + + Returns: + Perplexity loss between y_true and y_pred. + + >>> y_true = np.array([[1, 4], [2, 3]]) + >>> y_pred = np.array( + ... [[[0.28, 0.19, 0.21 , 0.15, 0.15], + ... [0.24, 0.19, 0.09, 0.18, 0.27]], + ... [[0.03, 0.26, 0.21, 0.18, 0.30], + ... [0.28, 0.10, 0.33, 0.15, 0.12]]] + ... ) + >>> perplexity_loss(y_true, y_pred) + 5.0247347775367945 + >>> y_true = np.array([[1, 4], [2, 3]]) + >>> y_pred = np.array( + ... [[[0.28, 0.19, 0.21 , 0.15, 0.15], + ... [0.24, 0.19, 0.09, 0.18, 0.27], + ... [0.30, 0.10, 0.20, 0.15, 0.25]], + ... [[0.03, 0.26, 0.21, 0.18, 0.30], + ... [0.28, 0.10, 0.33, 0.15, 0.12], + ... [0.30, 0.10, 0.20, 0.15, 0.25]],] + ... ) + >>> perplexity_loss(y_true, y_pred) + Traceback (most recent call last): + ... + ValueError: Sentence length of y_true and y_pred must be equal. + >>> y_true = np.array([[1, 4], [2, 11]]) + >>> y_pred = np.array( + ... [[[0.28, 0.19, 0.21 , 0.15, 0.15], + ... [0.24, 0.19, 0.09, 0.18, 0.27]], + ... [[0.03, 0.26, 0.21, 0.18, 0.30], + ... [0.28, 0.10, 0.33, 0.15, 0.12]]] + ... ) + >>> perplexity_loss(y_true, y_pred) + Traceback (most recent call last): + ... + ValueError: Label value must not be greater than vocabulary size. + >>> y_true = np.array([[1, 4]]) + >>> y_pred = np.array( + ... [[[0.28, 0.19, 0.21 , 0.15, 0.15], + ... [0.24, 0.19, 0.09, 0.18, 0.27]], + ... [[0.03, 0.26, 0.21, 0.18, 0.30], + ... [0.28, 0.10, 0.33, 0.15, 0.12]]] + ... ) + >>> perplexity_loss(y_true, y_pred) + Traceback (most recent call last): + ... + ValueError: Batch size of y_true and y_pred must be equal. + """ + + vocab_size = y_pred.shape[2] + + if y_true.shape[0] != y_pred.shape[0]: + raise ValueError("Batch size of y_true and y_pred must be equal.") + if y_true.shape[1] != y_pred.shape[1]: + raise ValueError("Sentence length of y_true and y_pred must be equal.") + if np.max(y_true) > vocab_size: + raise ValueError("Label value must not be greater than vocabulary size.") + + # Matrix to select prediction value only for true class + filter_matrix = np.array( + [[list(np.eye(vocab_size)[word]) for word in sentence] for sentence in y_true] + ) + + # Getting the matrix containing prediction for only true class + true_class_pred = np.sum(y_pred * filter_matrix, axis=2).clip(epsilon, 1) + + # Calculating perplexity for each sentence + perp_losses = np.exp(np.negative(np.mean(np.log(true_class_pred), axis=1))) + + return np.mean(perp_losses) + + if __name__ == "__main__": import doctest From f336cca8f8b2989d612068845f147ce885676148 Mon Sep 17 00:00:00 2001 From: Suyash Dongre <109069262+Suyashd999@users.noreply.github.com> Date: Fri, 27 Oct 2023 22:10:42 +0530 Subject: [PATCH 488/757] Added doctest to double_hash.py (#11020) * Added doctest to double_hash.py * Update double_hash.py --- data_structures/hashing/double_hash.py | 33 ++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/data_structures/hashing/double_hash.py b/data_structures/hashing/double_hash.py index be21e74cadd0..76c6c86814ec 100644 --- a/data_structures/hashing/double_hash.py +++ b/data_structures/hashing/double_hash.py @@ -35,6 +35,33 @@ def __hash_double_function(self, key, data, increment): return (increment * self.__hash_function_2(key, data)) % self.size_table def _collision_resolution(self, key, data=None): + """ + Examples: + + 1. Try to add three data elements when the size is three + >>> dh = DoubleHash(3) + >>> dh.insert_data(10) + >>> dh.insert_data(20) + >>> dh.insert_data(30) + >>> dh.keys() + {1: 10, 2: 20, 0: 30} + + 2. Try to add three data elements when the size is two + >>> dh = DoubleHash(2) + >>> dh.insert_data(10) + >>> dh.insert_data(20) + >>> dh.insert_data(30) + >>> dh.keys() + {10: 10, 9: 20, 8: 30} + + 3. Try to add three data elements when the size is four + >>> dh = DoubleHash(4) + >>> dh.insert_data(10) + >>> dh.insert_data(20) + >>> dh.insert_data(30) + >>> dh.keys() + {9: 20, 10: 10, 8: 30} + """ i = 1 new_key = self.hash_function(data) @@ -50,3 +77,9 @@ def _collision_resolution(self, key, data=None): i += 1 return new_key + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 185a35589ab14bf27f23266a25d8e1bcced646b2 Mon Sep 17 00:00:00 2001 From: Khushi Shukla Date: Fri, 27 Oct 2023 22:12:34 +0530 Subject: [PATCH 489/757] Create monotonic_array.py (#11025) * Create monotonic_array.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update monotonic_array.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- data_structures/arrays/monotonic_array.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 data_structures/arrays/monotonic_array.py diff --git a/data_structures/arrays/monotonic_array.py b/data_structures/arrays/monotonic_array.py new file mode 100644 index 000000000000..c50a21530814 --- /dev/null +++ b/data_structures/arrays/monotonic_array.py @@ -0,0 +1,23 @@ +# https://leetcode.com/problems/monotonic-array/ +def is_monotonic(nums: list[int]) -> bool: + """ + Check if a list is monotonic. + + >>> is_monotonic([1, 2, 2, 3]) + True + >>> is_monotonic([6, 5, 4, 4]) + True + >>> is_monotonic([1, 3, 2]) + False + """ + return all(nums[i] <= nums[i + 1] for i in range(len(nums) - 1)) or all( + nums[i] >= nums[i + 1] for i in range(len(nums) - 1) + ) + + +# Test the function with your examples +if __name__ == "__main__": + # Test the function with your examples + print(is_monotonic([1, 2, 2, 3])) # Output: True + print(is_monotonic([6, 5, 4, 4])) # Output: True + print(is_monotonic([1, 3, 2])) # Output: False From b0837d39859452ed7bd6e5b7adbdf172f70228bf Mon Sep 17 00:00:00 2001 From: Adam Ross <14985050+R055A@users.noreply.github.com> Date: Fri, 27 Oct 2023 22:10:32 +0200 Subject: [PATCH 490/757] Increase code coverage for dijkstra algorithm (#10695) * Increase code coverage for dijkstra algorithm * Add missing code coverage Refactor to pass mypy * Fix missing code coverage * Remove code changes, keep doctest * Remove ALL of the code changes * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update dijkstra_algorithm.py --------- Co-authored-by: Christian Clauss Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- graphs/dijkstra_algorithm.py | 313 +++++++++++++++++++++++++++++++++-- 1 file changed, 299 insertions(+), 14 deletions(-) diff --git a/graphs/dijkstra_algorithm.py b/graphs/dijkstra_algorithm.py index 452138fe904b..2efa2cb634ff 100644 --- a/graphs/dijkstra_algorithm.py +++ b/graphs/dijkstra_algorithm.py @@ -11,35 +11,127 @@ class PriorityQueue: # Based on Min Heap def __init__(self): + """ + Priority queue class constructor method. + + Examples: + >>> priority_queue_test = PriorityQueue() + >>> priority_queue_test.cur_size + 0 + >>> priority_queue_test.array + [] + >>> priority_queue_test.pos + {} + """ self.cur_size = 0 self.array = [] self.pos = {} # To store the pos of node in array def is_empty(self): + """ + Conditional boolean method to determine if the priority queue is empty or not. + + Examples: + >>> priority_queue_test = PriorityQueue() + >>> priority_queue_test.is_empty() + True + >>> priority_queue_test.insert((2, 'A')) + >>> priority_queue_test.is_empty() + False + """ return self.cur_size == 0 def min_heapify(self, idx): + """ + Sorts the queue array so that the minimum element is root. + + Examples: + >>> priority_queue_test = PriorityQueue() + >>> priority_queue_test.cur_size = 3 + >>> priority_queue_test.pos = {'A': 0, 'B': 1, 'C': 2} + + >>> priority_queue_test.array = [(5, 'A'), (10, 'B'), (15, 'C')] + >>> priority_queue_test.min_heapify(0) + Traceback (most recent call last): + ... + TypeError: 'list' object is not callable + >>> priority_queue_test.array + [(5, 'A'), (10, 'B'), (15, 'C')] + + >>> priority_queue_test.array = [(10, 'A'), (5, 'B'), (15, 'C')] + >>> priority_queue_test.min_heapify(0) + Traceback (most recent call last): + ... + TypeError: 'list' object is not callable + >>> priority_queue_test.array + [(10, 'A'), (5, 'B'), (15, 'C')] + + >>> priority_queue_test.array = [(10, 'A'), (15, 'B'), (5, 'C')] + >>> priority_queue_test.min_heapify(0) + Traceback (most recent call last): + ... + TypeError: 'list' object is not callable + >>> priority_queue_test.array + [(10, 'A'), (15, 'B'), (5, 'C')] + + >>> priority_queue_test.array = [(10, 'A'), (5, 'B')] + >>> priority_queue_test.cur_size = len(priority_queue_test.array) + >>> priority_queue_test.pos = {'A': 0, 'B': 1} + >>> priority_queue_test.min_heapify(0) + Traceback (most recent call last): + ... + TypeError: 'list' object is not callable + >>> priority_queue_test.array + [(10, 'A'), (5, 'B')] + """ lc = self.left(idx) rc = self.right(idx) - if lc < self.cur_size and self.array(lc)[0] < self.array(idx)[0]: + if lc < self.cur_size and self.array(lc)[0] < self.array[idx][0]: smallest = lc else: smallest = idx - if rc < self.cur_size and self.array(rc)[0] < self.array(smallest)[0]: + if rc < self.cur_size and self.array(rc)[0] < self.array[smallest][0]: smallest = rc if smallest != idx: self.swap(idx, smallest) self.min_heapify(smallest) def insert(self, tup): - # Inserts a node into the Priority Queue + """ + Inserts a node into the Priority Queue. + + Examples: + >>> priority_queue_test = PriorityQueue() + >>> priority_queue_test.insert((10, 'A')) + >>> priority_queue_test.array + [(10, 'A')] + >>> priority_queue_test.insert((15, 'B')) + >>> priority_queue_test.array + [(10, 'A'), (15, 'B')] + >>> priority_queue_test.insert((5, 'C')) + >>> priority_queue_test.array + [(5, 'C'), (10, 'A'), (15, 'B')] + """ self.pos[tup[1]] = self.cur_size self.cur_size += 1 self.array.append((sys.maxsize, tup[1])) self.decrease_key((sys.maxsize, tup[1]), tup[0]) def extract_min(self): - # Removes and returns the min element at top of priority queue + """ + Removes and returns the min element at top of priority queue. + + Examples: + >>> priority_queue_test = PriorityQueue() + >>> priority_queue_test.array = [(10, 'A'), (15, 'B')] + >>> priority_queue_test.cur_size = len(priority_queue_test.array) + >>> priority_queue_test.pos = {'A': 0, 'B': 1} + >>> priority_queue_test.insert((5, 'C')) + >>> priority_queue_test.extract_min() + 'C' + >>> priority_queue_test.array[0] + (15, 'B') + """ min_node = self.array[0][1] self.array[0] = self.array[self.cur_size - 1] self.cur_size -= 1 @@ -48,20 +140,61 @@ def extract_min(self): return min_node def left(self, i): - # returns the index of left child + """ + Returns the index of left child + + Examples: + >>> priority_queue_test = PriorityQueue() + >>> priority_queue_test.left(0) + 1 + >>> priority_queue_test.left(1) + 3 + """ return 2 * i + 1 def right(self, i): - # returns the index of right child + """ + Returns the index of right child + + Examples: + >>> priority_queue_test = PriorityQueue() + >>> priority_queue_test.right(0) + 2 + >>> priority_queue_test.right(1) + 4 + """ return 2 * i + 2 def par(self, i): - # returns the index of parent + """ + Returns the index of parent + + Examples: + >>> priority_queue_test = PriorityQueue() + >>> priority_queue_test.par(1) + 0 + >>> priority_queue_test.par(2) + 1 + >>> priority_queue_test.par(4) + 2 + """ return math.floor(i / 2) def swap(self, i, j): - # swaps array elements at indices i and j - # update the pos{} + """ + Swaps array elements at indices i and j, update the pos{} + + Examples: + >>> priority_queue_test = PriorityQueue() + >>> priority_queue_test.array = [(10, 'A'), (15, 'B')] + >>> priority_queue_test.cur_size = len(priority_queue_test.array) + >>> priority_queue_test.pos = {'A': 0, 'B': 1} + >>> priority_queue_test.swap(0, 1) + >>> priority_queue_test.array + [(15, 'B'), (10, 'A')] + >>> priority_queue_test.pos + {'A': 1, 'B': 0} + """ self.pos[self.array[i][1]] = j self.pos[self.array[j][1]] = i temp = self.array[i] @@ -69,6 +202,18 @@ def swap(self, i, j): self.array[j] = temp def decrease_key(self, tup, new_d): + """ + Decrease the key value for a given tuple, assuming the new_d is at most old_d. + + Examples: + >>> priority_queue_test = PriorityQueue() + >>> priority_queue_test.array = [(10, 'A'), (15, 'B')] + >>> priority_queue_test.cur_size = len(priority_queue_test.array) + >>> priority_queue_test.pos = {'A': 0, 'B': 1} + >>> priority_queue_test.decrease_key((10, 'A'), 5) + >>> priority_queue_test.array + [(5, 'A'), (15, 'B')] + """ idx = self.pos[tup[1]] # assuming the new_d is atmost old_d self.array[idx] = (new_d, tup[1]) @@ -79,6 +224,20 @@ def decrease_key(self, tup, new_d): class Graph: def __init__(self, num): + """ + Graph class constructor + + Examples: + >>> graph_test = Graph(1) + >>> graph_test.num_nodes + 1 + >>> graph_test.dist + [0] + >>> graph_test.par + [-1] + >>> graph_test.adjList + {} + """ self.adjList = {} # To store graph: u -> (v,w) self.num_nodes = num # Number of nodes in graph # To store the distance from source vertex @@ -86,8 +245,16 @@ def __init__(self, num): self.par = [-1] * self.num_nodes # To store the path def add_edge(self, u, v, w): - # Edge going from node u to v and v to u with weight w - # u (w)-> v, v (w) -> u + """ + Add edge going from node u to v and v to u with weight w: u (w)-> v, v (w) -> u + + Examples: + >>> graph_test = Graph(1) + >>> graph_test.add_edge(1, 2, 1) + >>> graph_test.add_edge(2, 3, 2) + >>> graph_test.adjList + {1: [(2, 1)], 2: [(1, 1), (3, 2)], 3: [(2, 2)]} + """ # Check if u already in graph if u in self.adjList: self.adjList[u].append((v, w)) @@ -101,11 +268,99 @@ def add_edge(self, u, v, w): self.adjList[v] = [(u, w)] def show_graph(self): - # u -> v(w) + """ + Show the graph: u -> v(w) + + Examples: + >>> graph_test = Graph(1) + >>> graph_test.add_edge(1, 2, 1) + >>> graph_test.show_graph() + 1 -> 2(1) + 2 -> 1(1) + >>> graph_test.add_edge(2, 3, 2) + >>> graph_test.show_graph() + 1 -> 2(1) + 2 -> 1(1) -> 3(2) + 3 -> 2(2) + """ for u in self.adjList: print(u, "->", " -> ".join(str(f"{v}({w})") for v, w in self.adjList[u])) def dijkstra(self, src): + """ + Dijkstra algorithm + + Examples: + >>> graph_test = Graph(3) + >>> graph_test.add_edge(0, 1, 2) + >>> graph_test.add_edge(1, 2, 2) + >>> graph_test.dijkstra(0) + Distance from node: 0 + Node 0 has distance: 0 + Node 1 has distance: 2 + Node 2 has distance: 4 + >>> graph_test.dist + [0, 2, 4] + + >>> graph_test = Graph(2) + >>> graph_test.add_edge(0, 1, 2) + >>> graph_test.dijkstra(0) + Distance from node: 0 + Node 0 has distance: 0 + Node 1 has distance: 2 + >>> graph_test.dist + [0, 2] + + >>> graph_test = Graph(3) + >>> graph_test.add_edge(0, 1, 2) + >>> graph_test.dijkstra(0) + Distance from node: 0 + Node 0 has distance: 0 + Node 1 has distance: 2 + Node 2 has distance: 0 + >>> graph_test.dist + [0, 2, 0] + + >>> graph_test = Graph(3) + >>> graph_test.add_edge(0, 1, 2) + >>> graph_test.add_edge(1, 2, 2) + >>> graph_test.add_edge(0, 2, 1) + >>> graph_test.dijkstra(0) + Distance from node: 0 + Node 0 has distance: 0 + Node 1 has distance: 2 + Node 2 has distance: 1 + >>> graph_test.dist + [0, 2, 1] + + >>> graph_test = Graph(4) + >>> graph_test.add_edge(0, 1, 4) + >>> graph_test.add_edge(1, 2, 2) + >>> graph_test.add_edge(2, 3, 1) + >>> graph_test.add_edge(0, 2, 3) + >>> graph_test.dijkstra(0) + Distance from node: 0 + Node 0 has distance: 0 + Node 1 has distance: 4 + Node 2 has distance: 3 + Node 3 has distance: 4 + >>> graph_test.dist + [0, 4, 3, 4] + + >>> graph_test = Graph(4) + >>> graph_test.add_edge(0, 1, 4) + >>> graph_test.add_edge(1, 2, 2) + >>> graph_test.add_edge(2, 3, 1) + >>> graph_test.add_edge(0, 2, 7) + >>> graph_test.dijkstra(0) + Distance from node: 0 + Node 0 has distance: 0 + Node 1 has distance: 4 + Node 2 has distance: 6 + Node 3 has distance: 7 + >>> graph_test.dist + [0, 4, 6, 7] + """ # Flush old junk values in par[] self.par = [-1] * self.num_nodes # src is the source node @@ -135,13 +390,40 @@ def dijkstra(self, src): self.show_distances(src) def show_distances(self, src): + """ + Show the distances from src to all other nodes in a graph + + Examples: + >>> graph_test = Graph(1) + >>> graph_test.show_distances(0) + Distance from node: 0 + Node 0 has distance: 0 + """ print(f"Distance from node: {src}") for u in range(self.num_nodes): print(f"Node {u} has distance: {self.dist[u]}") def show_path(self, src, dest): - # To show the shortest path from src to dest - # WARNING: Use it *after* calling dijkstra + """ + Shows the shortest path from src to dest. + WARNING: Use it *after* calling dijkstra. + + Examples: + >>> graph_test = Graph(4) + >>> graph_test.add_edge(0, 1, 1) + >>> graph_test.add_edge(1, 2, 2) + >>> graph_test.add_edge(2, 3, 3) + >>> graph_test.dijkstra(0) + Distance from node: 0 + Node 0 has distance: 0 + Node 1 has distance: 1 + Node 2 has distance: 3 + Node 3 has distance: 6 + >>> graph_test.show_path(0, 3) # doctest: +NORMALIZE_WHITESPACE + ----Path to reach 3 from 0---- + 0 -> 1 -> 2 -> 3 + Total cost of path: 6 + """ path = [] cost = 0 temp = dest @@ -167,6 +449,9 @@ def show_path(self, src, dest): if __name__ == "__main__": + from doctest import testmod + + testmod() graph = Graph(9) graph.add_edge(0, 1, 4) graph.add_edge(0, 7, 8) From 0eb1825af2114c60792dc5cbd43ca1259ae95a24 Mon Sep 17 00:00:00 2001 From: RaymondDashWu <33266041+RaymondDashWu@users.noreply.github.com> Date: Fri, 27 Oct 2023 13:13:32 -0700 Subject: [PATCH 491/757] Tests for odd_even_transposition_parallel (#10926) * [ADD] tests for odd_even_transposition_parallel * adding another test because build failed 6 hrs * comment out all tests to see if it fails * list(range(10)[::-1]) test uncommented * [a, x, c] test uncommented * [1.9, 42.0, 2.8] test uncommented * [False, True, False] test uncommented * [1, 32.0, 9] test uncommented * [1, 32.0, 9] test uncommented * [-442, -98, -554, 266, -491, 985, -53, -529, 82, -429] test uncommented * test non global lock * [DEL] Testing multiple data types. Couldn't get doctest to work * [ADD] Comment on why non global process lock --- sorts/odd_even_transposition_parallel.py | 25 ++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/sorts/odd_even_transposition_parallel.py b/sorts/odd_even_transposition_parallel.py index 9e0d228bdc5b..b8ab46df1e59 100644 --- a/sorts/odd_even_transposition_parallel.py +++ b/sorts/odd_even_transposition_parallel.py @@ -13,7 +13,8 @@ from multiprocessing import Lock, Pipe, Process # lock used to ensure that two processes do not access a pipe at the same time -process_lock = Lock() +# NOTE This breaks testing on build runner. May work better locally +# process_lock = Lock() """ The function run by the processes that sorts the list @@ -28,7 +29,7 @@ def oe_process(position, value, l_send, r_send, lr_cv, rr_cv, result_pipe): - global process_lock + process_lock = Lock() # we perform n swaps since after n swaps we know we are sorted # we *could* stop early if we are sorted already, but it takes as long to @@ -72,6 +73,26 @@ def oe_process(position, value, l_send, r_send, lr_cv, rr_cv, result_pipe): def odd_even_transposition(arr): + """ + >>> odd_even_transposition(list(range(10)[::-1])) == sorted(list(range(10)[::-1])) + True + >>> odd_even_transposition(["a", "x", "c"]) == sorted(["x", "a", "c"]) + True + >>> odd_even_transposition([1.9, 42.0, 2.8]) == sorted([1.9, 42.0, 2.8]) + True + >>> odd_even_transposition([False, True, False]) == sorted([False, False, True]) + True + >>> odd_even_transposition([1, 32.0, 9]) == sorted([False, False, True]) + False + >>> odd_even_transposition([1, 32.0, 9]) == sorted([1.0, 32, 9.0]) + True + >>> unsorted_list = [-442, -98, -554, 266, -491, 985, -53, -529, 82, -429] + >>> odd_even_transposition(unsorted_list) == sorted(unsorted_list) + True + >>> unsorted_list = [-442, -98, -554, 266, -491, 985, -53, -529, 82, -429] + >>> odd_even_transposition(unsorted_list) == sorted(unsorted_list + [1]) + False + """ process_array_ = [] result_pipe = [] # initialize the list of pipes where the values will be retrieved From 5df16f11eb536f76b74d468de33114f25c2c9ac1 Mon Sep 17 00:00:00 2001 From: Tiela Rose Black-Law <26930264+tielarose@users.noreply.github.com> Date: Fri, 27 Oct 2023 14:13:51 -0700 Subject: [PATCH 492/757] Add doctest to hashes/hamming_code.py (#10961) --- hashes/hamming_code.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hashes/hamming_code.py b/hashes/hamming_code.py index 8498ca920b36..4a6efcf23f63 100644 --- a/hashes/hamming_code.py +++ b/hashes/hamming_code.py @@ -77,6 +77,10 @@ def emitter_converter(size_par, data): >>> emitter_converter(4, "101010111111") ['1', '1', '1', '1', '0', '1', '0', '0', '1', '0', '1', '1', '1', '1', '1', '1'] + >>> emitter_converter(5, "101010111111") + Traceback (most recent call last): + ... + ValueError: size of parity don't match with size of data """ if size_par + len(data) <= 2**size_par - (len(data) - 1): raise ValueError("size of parity don't match with size of data") From a0e80a74c817c8edd35737d2fbf7d38dd71fa43d Mon Sep 17 00:00:00 2001 From: Sanket Nikam <77570082+SannketNikam@users.noreply.github.com> Date: Sat, 28 Oct 2023 02:47:58 +0530 Subject: [PATCH 493/757] Added Gradient Boosting Classifier (#10944) * Added Gradient Boosting Classifier * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update gradient_boosting_classifier.py * Update gradient_boosting_classifier.py * Update gradient_boosting_classifier.py * Update gradient_boosting_classifier.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .../gradient_boosting_classifier.py | 118 ++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 machine_learning/gradient_boosting_classifier.py diff --git a/machine_learning/gradient_boosting_classifier.py b/machine_learning/gradient_boosting_classifier.py new file mode 100644 index 000000000000..2902394d8226 --- /dev/null +++ b/machine_learning/gradient_boosting_classifier.py @@ -0,0 +1,118 @@ +import numpy as np +from sklearn.datasets import load_iris +from sklearn.metrics import accuracy_score +from sklearn.model_selection import train_test_split +from sklearn.tree import DecisionTreeRegressor + + +class GradientBoostingClassifier: + def __init__(self, n_estimators: int = 100, learning_rate: float = 0.1) -> None: + """ + Initialize a GradientBoostingClassifier. + + Parameters: + - n_estimators (int): The number of weak learners to train. + - learning_rate (float): The learning rate for updating the model. + + Attributes: + - n_estimators (int): The number of weak learners. + - learning_rate (float): The learning rate. + - models (list): A list to store the trained weak learners. + """ + self.n_estimators = n_estimators + self.learning_rate = learning_rate + self.models: list[tuple[DecisionTreeRegressor, float]] = [] + + def fit(self, features: np.ndarray, target: np.ndarray) -> None: + """ + Fit the GradientBoostingClassifier to the training data. + + Parameters: + - features (np.ndarray): The training features. + - target (np.ndarray): The target values. + + Returns: + None + + >>> import numpy as np + >>> from sklearn.datasets import load_iris + >>> clf = GradientBoostingClassifier(n_estimators=100, learning_rate=0.1) + >>> iris = load_iris() + >>> X, y = iris.data, iris.target + >>> clf.fit(X, y) + >>> # Check if the model is trained + >>> len(clf.models) == 100 + True + """ + for _ in range(self.n_estimators): + # Calculate the pseudo-residuals + residuals = -self.gradient(target, self.predict(features)) + # Fit a weak learner (e.g., decision tree) to the residuals + model = DecisionTreeRegressor(max_depth=1) + model.fit(features, residuals) + # Update the model by adding the weak learner with a learning rate + self.models.append((model, self.learning_rate)) + + def predict(self, features: np.ndarray) -> np.ndarray: + """ + Make predictions on input data. + + Parameters: + - features (np.ndarray): The input data for making predictions. + + Returns: + - np.ndarray: An array of binary predictions (-1 or 1). + + >>> import numpy as np + >>> from sklearn.datasets import load_iris + >>> clf = GradientBoostingClassifier(n_estimators=100, learning_rate=0.1) + >>> iris = load_iris() + >>> X, y = iris.data, iris.target + >>> clf.fit(X, y) + >>> y_pred = clf.predict(X) + >>> # Check if the predictions have the correct shape + >>> y_pred.shape == y.shape + True + """ + # Initialize predictions with zeros + predictions = np.zeros(features.shape[0]) + for model, learning_rate in self.models: + predictions += learning_rate * model.predict(features) + return np.sign(predictions) # Convert to binary predictions (-1 or 1) + + def gradient(self, target: np.ndarray, y_pred: np.ndarray) -> np.ndarray: + """ + Calculate the negative gradient (pseudo-residuals) for logistic loss. + + Parameters: + - target (np.ndarray): The target values. + - y_pred (np.ndarray): The predicted values. + + Returns: + - np.ndarray: An array of pseudo-residuals. + + >>> import numpy as np + >>> clf = GradientBoostingClassifier(n_estimators=100, learning_rate=0.1) + >>> target = np.array([0, 1, 0, 1]) + >>> y_pred = np.array([0.2, 0.8, 0.3, 0.7]) + >>> residuals = clf.gradient(target, y_pred) + >>> # Check if residuals have the correct shape + >>> residuals.shape == target.shape + True + """ + return -target / (1 + np.exp(target * y_pred)) + + +if __name__ == "__main__": + iris = load_iris() + X, y = iris.data, iris.target + X_train, X_test, y_train, y_test = train_test_split( + X, y, test_size=0.2, random_state=42 + ) + + clf = GradientBoostingClassifier(n_estimators=100, learning_rate=0.1) + clf.fit(X_train, y_train) + + y_pred = clf.predict(X_test) + accuracy = accuracy_score(y_test, y_pred) + print(f"Accuracy: {accuracy:.2f}") From 1e1ee00782d300c22e3d7a425ace5d0c7cefb200 Mon Sep 17 00:00:00 2001 From: Manmita Das <34617961+manmita@users.noreply.github.com> Date: Sat, 28 Oct 2023 03:18:15 +0530 Subject: [PATCH 494/757] Excess 3 code (#11001) * added excess-3 code * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * updated with fixes * updated with fixes * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update excess_3_code.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- bit_manipulation/excess_3_code.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 bit_manipulation/excess_3_code.py diff --git a/bit_manipulation/excess_3_code.py b/bit_manipulation/excess_3_code.py new file mode 100644 index 000000000000..7beaabd90e8a --- /dev/null +++ b/bit_manipulation/excess_3_code.py @@ -0,0 +1,27 @@ +def excess_3_code(number: int) -> str: + """ + Find excess-3 code of integer base 10. + Add 3 to all digits in a decimal number then convert to a binary-coded decimal. + https://en.wikipedia.org/wiki/Excess-3 + + >>> excess_3_code(0) + '0b0011' + >>> excess_3_code(3) + '0b0110' + >>> excess_3_code(2) + '0b0101' + >>> excess_3_code(20) + '0b01010011' + >>> excess_3_code(120) + '0b010001010011' + """ + num = "" + for digit in str(max(0, number)): + num += str(bin(int(digit) + 3))[2:].zfill(4) + return "0b" + num + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From f2436318cef6dba173282f83def4ebf7bd1d2aba Mon Sep 17 00:00:00 2001 From: Shreya123714 <95279016+Shreya123714@users.noreply.github.com> Date: Sun, 29 Oct 2023 00:02:12 +0530 Subject: [PATCH 495/757] Add FuzzySet Class for Triangular Fuzzy Sets (#11036) * Added Opertation for triangular fuzzy sets * Added Sources * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix the bug , for which test were failing * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Add type hints and improve parameter names * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Add Test For fuzzy_operations.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix the bug in fuzzy_operations.py * Add test_fuzzy_logic.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix the bug in fuzzy_operations.py & deleted test * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fixed the typo error * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Again done * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * corrected wrong intendation due to which test fail * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * bug fixed * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Add test_fuzzy_logic * Modified fuzzy_operations.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fixed the bug, made a FuzzySet dataclass * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Replaced assertEqual of unittest to assert python * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * lets see * Changed test * orderd the import statements * Add docstring and dataclass the FuzzySet * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update fuzzy_operations.py * Delete fuzzy_logic/test_fuzzy_logic.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * https://en.wikipedia.org/wiki/Fuzzy_set --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- fuzzy_logic/fuzzy_operations.py | 195 ++++++++++++++++++++++++++++++++ 1 file changed, 195 insertions(+) create mode 100644 fuzzy_logic/fuzzy_operations.py diff --git a/fuzzy_logic/fuzzy_operations.py b/fuzzy_logic/fuzzy_operations.py new file mode 100644 index 000000000000..e41cd2120049 --- /dev/null +++ b/fuzzy_logic/fuzzy_operations.py @@ -0,0 +1,195 @@ +""" +By @Shreya123714 + +https://en.wikipedia.org/wiki/Fuzzy_set +""" + +from __future__ import annotations + +from dataclasses import dataclass + +import matplotlib.pyplot as plt +import numpy as np + + +@dataclass +class FuzzySet: + """ + A class for representing and manipulating triangular fuzzy sets. + Attributes: + name: The name or label of the fuzzy set. + left_boundary: The left boundary of the fuzzy set. + peak: The peak (central) value of the fuzzy set. + right_boundary: The right boundary of the fuzzy set. + Methods: + membership(x): Calculate the membership value of an input 'x' in the fuzzy set. + union(other): Calculate the union of this fuzzy set with another fuzzy set. + intersection(other): Calculate the intersection of this fuzzy set with another. + complement(): Calculate the complement (negation) of this fuzzy set. + plot(): Plot the membership function of the fuzzy set. + + >>> sheru = FuzzySet("Sheru", 0.4, 1, 0.6) + >>> sheru + FuzzySet(name='Sheru', left_boundary=0.4, peak=1, right_boundary=0.6) + >>> str(sheru) + 'Sheru: [0.4, 1, 0.6]' + + >>> siya = FuzzySet("Siya", 0.5, 1, 0.7) + >>> siya + FuzzySet(name='Siya', left_boundary=0.5, peak=1, right_boundary=0.7) + + # Complement Operation + >>> sheru.complement() + FuzzySet(name='¬Sheru', left_boundary=0.4, peak=0.6, right_boundary=0) + >>> siya.complement() # doctest: +NORMALIZE_WHITESPACE + FuzzySet(name='¬Siya', left_boundary=0.30000000000000004, peak=0.5, + right_boundary=0) + + # Intersection Operation + >>> siya.intersection(sheru) + FuzzySet(name='Siya ∩ Sheru', left_boundary=0.5, peak=0.6, right_boundary=1.0) + + # Membership Operation + >>> sheru.membership(0.5) + 0.16666666666666663 + >>> sheru.membership(0.6) + 0.0 + + # Union Operations + >>> siya.union(sheru) + FuzzySet(name='Siya ∪ Sheru', left_boundary=0.4, peak=0.7, right_boundary=1.0) + """ + + name: str + left_boundary: float + peak: float + right_boundary: float + + def __str__(self) -> str: + """ + >>> FuzzySet("fuzzy_set", 0.1, 0.2, 0.3) + FuzzySet(name='fuzzy_set', left_boundary=0.1, peak=0.2, right_boundary=0.3) + """ + return ( + f"{self.name}: [{self.left_boundary}, {self.peak}, {self.right_boundary}]" + ) + + def complement(self) -> FuzzySet: + """ + Calculate the complement (negation) of this fuzzy set. + Returns: + FuzzySet: A new fuzzy set representing the complement. + + >>> FuzzySet("fuzzy_set", 0.1, 0.2, 0.3).complement() + FuzzySet(name='¬fuzzy_set', left_boundary=0.7, peak=0.9, right_boundary=0.8) + """ + return FuzzySet( + f"¬{self.name}", + 1 - self.right_boundary, + 1 - self.left_boundary, + 1 - self.peak, + ) + + def intersection(self, other) -> FuzzySet: + """ + Calculate the intersection of this fuzzy set + with another fuzzy set. + Args: + other: Another fuzzy set to intersect with. + Returns: + A new fuzzy set representing the intersection. + + >>> FuzzySet("a", 0.1, 0.2, 0.3).intersection(FuzzySet("b", 0.4, 0.5, 0.6)) + FuzzySet(name='a ∩ b', left_boundary=0.4, peak=0.3, right_boundary=0.35) + """ + return FuzzySet( + f"{self.name} ∩ {other.name}", + max(self.left_boundary, other.left_boundary), + min(self.right_boundary, other.right_boundary), + (self.peak + other.peak) / 2, + ) + + def membership(self, x: float) -> float: + """ + Calculate the membership value of an input 'x' in the fuzzy set. + Returns: + The membership value of 'x' in the fuzzy set. + + >>> a = FuzzySet("a", 0.1, 0.2, 0.3) + >>> a.membership(0.09) + 0.0 + >>> a.membership(0.1) + 0.0 + >>> a.membership(0.11) + 0.09999999999999995 + >>> a.membership(0.4) + 0.0 + >>> FuzzySet("A", 0, 0.5, 1).membership(0.1) + 0.2 + >>> FuzzySet("B", 0.2, 0.7, 1).membership(0.6) + 0.8 + """ + if x <= self.left_boundary or x >= self.right_boundary: + return 0.0 + elif self.left_boundary < x <= self.peak: + return (x - self.left_boundary) / (self.peak - self.left_boundary) + elif self.peak < x < self.right_boundary: + return (self.right_boundary - x) / (self.right_boundary - self.peak) + msg = f"Invalid value {x} for fuzzy set {self}" + raise ValueError(msg) + + def union(self, other) -> FuzzySet: + """ + Calculate the union of this fuzzy set with another fuzzy set. + Args: + other (FuzzySet): Another fuzzy set to union with. + Returns: + FuzzySet: A new fuzzy set representing the union. + + >>> FuzzySet("a", 0.1, 0.2, 0.3).union(FuzzySet("b", 0.4, 0.5, 0.6)) + FuzzySet(name='a ∪ b', left_boundary=0.1, peak=0.6, right_boundary=0.35) + """ + return FuzzySet( + f"{self.name} ∪ {other.name}", + min(self.left_boundary, other.left_boundary), + max(self.right_boundary, other.right_boundary), + (self.peak + other.peak) / 2, + ) + + def plot(self): + """ + Plot the membership function of the fuzzy set. + """ + x = np.linspace(0, 1, 1000) + y = [self.membership(xi) for xi in x] + + plt.plot(x, y, label=self.name) + + +if __name__ == "__main__": + from doctest import testmod + + testmod() + a = FuzzySet("A", 0, 0.5, 1) + b = FuzzySet("B", 0.2, 0.7, 1) + + a.plot() + b.plot() + + plt.xlabel("x") + plt.ylabel("Membership") + plt.legend() + plt.show() + + union_ab = a.union(b) + intersection_ab = a.intersection(b) + complement_a = a.complement() + + union_ab.plot() + intersection_ab.plot() + complement_a.plot() + + plt.xlabel("x") + plt.ylabel("Membership") + plt.legend() + plt.show() From b51b833e0a0339421c76ee53662521689b1c9d62 Mon Sep 17 00:00:00 2001 From: Suyash Dongre <109069262+Suyashd999@users.noreply.github.com> Date: Sun, 29 Oct 2023 01:13:20 +0530 Subject: [PATCH 496/757] Added doctest to heap.py (#11059) --- data_structures/heap/heap.py | 75 ++++++++++++++++++++++++++++++++++-- 1 file changed, 72 insertions(+), 3 deletions(-) diff --git a/data_structures/heap/heap.py b/data_structures/heap/heap.py index c1004f349479..29bff3af07e3 100644 --- a/data_structures/heap/heap.py +++ b/data_structures/heap/heap.py @@ -81,6 +81,9 @@ def right_child_idx(self, parent_idx: int) -> int | None: def max_heapify(self, index: int) -> None: """ correct a single violation of the heap property in a subtree's root. + + It is the function that is responsible for restoring the property + of Max heap i.e the maximum element is always at top. """ if index < self.heap_size: violation: int = index @@ -99,7 +102,29 @@ def max_heapify(self, index: int) -> None: self.max_heapify(violation) def build_max_heap(self, collection: Iterable[T]) -> None: - """build max heap from an unsorted array""" + """ + build max heap from an unsorted array + + >>> h = Heap() + >>> h.build_max_heap([20,40,50,20,10]) + >>> h + [50, 40, 20, 20, 10] + + >>> h = Heap() + >>> h.build_max_heap([1,2,3,4,5,6,7,8,9,0]) + >>> h + [9, 8, 7, 4, 5, 6, 3, 2, 1, 0] + + >>> h = Heap() + >>> h.build_max_heap([514,5,61,57,8,99,105]) + >>> h + [514, 57, 105, 5, 8, 99, 61] + + >>> h = Heap() + >>> h.build_max_heap([514,5,61.6,57,8,9.9,105]) + >>> h + [514, 57, 105, 5, 8, 9.9, 61.6] + """ self.h = list(collection) self.heap_size = len(self.h) if self.heap_size > 1: @@ -108,7 +133,24 @@ def build_max_heap(self, collection: Iterable[T]) -> None: self.max_heapify(i) def extract_max(self) -> T: - """get and remove max from heap""" + """ + get and remove max from heap + + >>> h = Heap() + >>> h.build_max_heap([20,40,50,20,10]) + >>> h.extract_max() + 50 + + >>> h = Heap() + >>> h.build_max_heap([514,5,61,57,8,99,105]) + >>> h.extract_max() + 514 + + >>> h = Heap() + >>> h.build_max_heap([1,2,3,4,5,6,7,8,9,0]) + >>> h.extract_max() + 9 + """ if self.heap_size >= 2: me = self.h[0] self.h[0] = self.h.pop(-1) @@ -122,7 +164,34 @@ def extract_max(self) -> T: raise Exception("Empty heap") def insert(self, value: T) -> None: - """insert a new value into the max heap""" + """ + insert a new value into the max heap + + >>> h = Heap() + >>> h.insert(10) + >>> h + [10] + + >>> h = Heap() + >>> h.insert(10) + >>> h.insert(10) + >>> h + [10, 10] + + >>> h = Heap() + >>> h.insert(10) + >>> h.insert(10.1) + >>> h + [10.1, 10] + + >>> h = Heap() + >>> h.insert(0.1) + >>> h.insert(0) + >>> h.insert(9) + >>> h.insert(5) + >>> h + [9, 5, 0.1, 0] + """ self.h.append(value) idx = (self.heap_size - 1) // 2 self.heap_size += 1 From d80ee90178d48e530a2df3966fee3b5e06ec3ecc Mon Sep 17 00:00:00 2001 From: Khushi Shukla Date: Sun, 29 Oct 2023 02:43:14 +0530 Subject: [PATCH 497/757] Create crossword_puzzle_solver.py (#11011) * Create crossword_puzzle_solver.py * Update crossword_puzzle_solver.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update crossword_puzzle_solver.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update crossword_puzzle_solver.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update crossword_puzzle_solver.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update crossword_puzzle_solver.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update crossword_puzzle_solver.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update crossword_puzzle_solver.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update crossword_puzzle_solver.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update crossword_puzzle_solver.py * Update backtracking/crossword_puzzle_solver.py * Update crossword_puzzle_solver.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update crossword_puzzle_solver.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update crossword_puzzle_solver.py * Apply suggestions from code review * Update crossword_puzzle_solver.py * Update crossword_puzzle_solver.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- backtracking/crossword_puzzle_solver.py | 132 ++++++++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 backtracking/crossword_puzzle_solver.py diff --git a/backtracking/crossword_puzzle_solver.py b/backtracking/crossword_puzzle_solver.py new file mode 100644 index 000000000000..b9c01c4efea9 --- /dev/null +++ b/backtracking/crossword_puzzle_solver.py @@ -0,0 +1,132 @@ +# https://www.geeksforgeeks.org/solve-crossword-puzzle/ + + +def is_valid( + puzzle: list[list[str]], word: str, row: int, col: int, vertical: bool +) -> bool: + """ + Check if a word can be placed at the given position. + + >>> puzzle = [ + ... ['', '', '', ''], + ... ['', '', '', ''], + ... ['', '', '', ''], + ... ['', '', '', ''] + ... ] + >>> is_valid(puzzle, 'word', 0, 0, True) + True + >>> puzzle = [ + ... ['', '', '', ''], + ... ['', '', '', ''], + ... ['', '', '', ''], + ... ['', '', '', ''] + ... ] + >>> is_valid(puzzle, 'word', 0, 0, False) + True + """ + for i in range(len(word)): + if vertical: + if row + i >= len(puzzle) or puzzle[row + i][col] != "": + return False + else: + if col + i >= len(puzzle[0]) or puzzle[row][col + i] != "": + return False + return True + + +def place_word( + puzzle: list[list[str]], word: str, row: int, col: int, vertical: bool +) -> None: + """ + Place a word at the given position. + + >>> puzzle = [ + ... ['', '', '', ''], + ... ['', '', '', ''], + ... ['', '', '', ''], + ... ['', '', '', ''] + ... ] + >>> place_word(puzzle, 'word', 0, 0, True) + >>> puzzle + [['w', '', '', ''], ['o', '', '', ''], ['r', '', '', ''], ['d', '', '', '']] + """ + for i, char in enumerate(word): + if vertical: + puzzle[row + i][col] = char + else: + puzzle[row][col + i] = char + + +def remove_word( + puzzle: list[list[str]], word: str, row: int, col: int, vertical: bool +) -> None: + """ + Remove a word from the given position. + + >>> puzzle = [ + ... ['w', '', '', ''], + ... ['o', '', '', ''], + ... ['r', '', '', ''], + ... ['d', '', '', ''] + ... ] + >>> remove_word(puzzle, 'word', 0, 0, True) + >>> puzzle + [['', '', '', ''], ['', '', '', ''], ['', '', '', ''], ['', '', '', '']] + """ + for i in range(len(word)): + if vertical: + puzzle[row + i][col] = "" + else: + puzzle[row][col + i] = "" + + +def solve_crossword(puzzle: list[list[str]], words: list[str]) -> bool: + """ + Solve the crossword puzzle using backtracking. + + >>> puzzle = [ + ... ['', '', '', ''], + ... ['', '', '', ''], + ... ['', '', '', ''], + ... ['', '', '', ''] + ... ] + + >>> words = ['word', 'four', 'more', 'last'] + >>> solve_crossword(puzzle, words) + True + >>> puzzle = [ + ... ['', '', '', ''], + ... ['', '', '', ''], + ... ['', '', '', ''], + ... ['', '', '', ''] + ... ] + >>> words = ['word', 'four', 'more', 'paragraphs'] + >>> solve_crossword(puzzle, words) + False + """ + for row in range(len(puzzle)): + for col in range(len(puzzle[0])): + if puzzle[row][col] == "": + for word in words: + for vertical in [True, False]: + if is_valid(puzzle, word, row, col, vertical): + place_word(puzzle, word, row, col, vertical) + words.remove(word) + if solve_crossword(puzzle, words): + return True + words.append(word) + remove_word(puzzle, word, row, col, vertical) + return False + return True + + +if __name__ == "__main__": + PUZZLE = [[""] * 3 for _ in range(3)] + WORDS = ["cat", "dog", "car"] + + if solve_crossword(PUZZLE, WORDS): + print("Solution found:") + for row in PUZZLE: + print(" ".join(row)) + else: + print("No solution found:") From 444dfb0a0f7b1e9b0b2f171b426dca26bcd1937a Mon Sep 17 00:00:00 2001 From: Ravi Kumar <119737193+ravi-ivar-7@users.noreply.github.com> Date: Sun, 29 Oct 2023 03:42:17 +0530 Subject: [PATCH 498/757] Added adams-bashforth method of order 2, 3, 4, 5 (#10969) * added runge kutta gills method * added adams-bashforth method of order 2, 3, 4, 5 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update adams_bashforth.py * Deleted extraneous file, maths/numerical_analysis/runge_kutta_gills.py * Added doctests to each function adams_bashforth.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update adams_bashforth.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- maths/numerical_analysis/adams_bashforth.py | 230 ++++++++++++++++++++ 1 file changed, 230 insertions(+) create mode 100644 maths/numerical_analysis/adams_bashforth.py diff --git a/maths/numerical_analysis/adams_bashforth.py b/maths/numerical_analysis/adams_bashforth.py new file mode 100644 index 000000000000..d61f022a413d --- /dev/null +++ b/maths/numerical_analysis/adams_bashforth.py @@ -0,0 +1,230 @@ +""" +Use the Adams-Bashforth methods to solve Ordinary Differential Equations. + +https://en.wikipedia.org/wiki/Linear_multistep_method +Author : Ravi Kumar +""" +from collections.abc import Callable +from dataclasses import dataclass + +import numpy as np + + +@dataclass +class AdamsBashforth: + """ + args: + func: An ordinary differential equation (ODE) as function of x and y. + x_initials: List containing initial required values of x. + y_initials: List containing initial required values of y. + step_size: The increment value of x. + x_final: The final value of x. + + Returns: Solution of y at each nodal point + + >>> def f(x, y): + ... return x + y + >>> AdamsBashforth(f, [0, 0.2, 0.4], [0, 0.2, 1], 0.2, 1) # doctest: +ELLIPSIS + AdamsBashforth(func=..., x_initials=[0, 0.2, 0.4], y_initials=[0, 0.2, 1], step...) + >>> AdamsBashforth(f, [0, 0.2, 1], [0, 0, 0.04], 0.2, 1).step_2() + Traceback (most recent call last): + ... + ValueError: The final value of x must be greater than the initial values of x. + + >>> AdamsBashforth(f, [0, 0.2, 0.3], [0, 0, 0.04], 0.2, 1).step_3() + Traceback (most recent call last): + ... + ValueError: x-values must be equally spaced according to step size. + + >>> AdamsBashforth(f,[0,0.2,0.4,0.6,0.8],[0,0,0.04,0.128,0.307],-0.2,1).step_5() + Traceback (most recent call last): + ... + ValueError: Step size must be positive. + """ + + func: Callable[[float, float], float] + x_initials: list[float] + y_initials: list[float] + step_size: float + x_final: float + + def __post_init__(self) -> None: + if self.x_initials[-1] >= self.x_final: + raise ValueError( + "The final value of x must be greater than the initial values of x." + ) + + if self.step_size <= 0: + raise ValueError("Step size must be positive.") + + if not all( + round(x1 - x0, 10) == self.step_size + for x0, x1 in zip(self.x_initials, self.x_initials[1:]) + ): + raise ValueError("x-values must be equally spaced according to step size.") + + def step_2(self) -> np.ndarray: + """ + >>> def f(x, y): + ... return x + >>> AdamsBashforth(f, [0, 0.2], [0, 0], 0.2, 1).step_2() + array([0. , 0. , 0.06, 0.16, 0.3 , 0.48]) + + >>> AdamsBashforth(f, [0, 0.2, 0.4], [0, 0, 0.04], 0.2, 1).step_2() + Traceback (most recent call last): + ... + ValueError: Insufficient initial points information. + """ + + if len(self.x_initials) != 2 or len(self.y_initials) != 2: + raise ValueError("Insufficient initial points information.") + + x_0, x_1 = self.x_initials[:2] + y_0, y_1 = self.y_initials[:2] + + n = int((self.x_final - x_1) / self.step_size) + y = np.zeros(n + 2) + y[0] = y_0 + y[1] = y_1 + + for i in range(n): + y[i + 2] = y[i + 1] + (self.step_size / 2) * ( + 3 * self.func(x_1, y[i + 1]) - self.func(x_0, y[i]) + ) + x_0 = x_1 + x_1 += self.step_size + + return y + + def step_3(self) -> np.ndarray: + """ + >>> def f(x, y): + ... return x + y + >>> y = AdamsBashforth(f, [0, 0.2, 0.4], [0, 0, 0.04], 0.2, 1).step_3() + >>> y[3] + 0.15533333333333332 + + >>> AdamsBashforth(f, [0, 0.2], [0, 0], 0.2, 1).step_3() + Traceback (most recent call last): + ... + ValueError: Insufficient initial points information. + """ + if len(self.x_initials) != 3 or len(self.y_initials) != 3: + raise ValueError("Insufficient initial points information.") + + x_0, x_1, x_2 = self.x_initials[:3] + y_0, y_1, y_2 = self.y_initials[:3] + + n = int((self.x_final - x_2) / self.step_size) + y = np.zeros(n + 4) + y[0] = y_0 + y[1] = y_1 + y[2] = y_2 + + for i in range(n + 1): + y[i + 3] = y[i + 2] + (self.step_size / 12) * ( + 23 * self.func(x_2, y[i + 2]) + - 16 * self.func(x_1, y[i + 1]) + + 5 * self.func(x_0, y[i]) + ) + x_0 = x_1 + x_1 = x_2 + x_2 += self.step_size + + return y + + def step_4(self) -> np.ndarray: + """ + >>> def f(x,y): + ... return x + y + >>> y = AdamsBashforth( + ... f, [0, 0.2, 0.4, 0.6], [0, 0, 0.04, 0.128], 0.2, 1).step_4() + >>> y[4] + 0.30699999999999994 + >>> y[5] + 0.5771083333333333 + + >>> AdamsBashforth(f, [0, 0.2, 0.4], [0, 0, 0.04], 0.2, 1).step_4() + Traceback (most recent call last): + ... + ValueError: Insufficient initial points information. + """ + + if len(self.x_initials) != 4 or len(self.y_initials) != 4: + raise ValueError("Insufficient initial points information.") + + x_0, x_1, x_2, x_3 = self.x_initials[:4] + y_0, y_1, y_2, y_3 = self.y_initials[:4] + + n = int((self.x_final - x_3) / self.step_size) + y = np.zeros(n + 4) + y[0] = y_0 + y[1] = y_1 + y[2] = y_2 + y[3] = y_3 + + for i in range(n): + y[i + 4] = y[i + 3] + (self.step_size / 24) * ( + 55 * self.func(x_3, y[i + 3]) + - 59 * self.func(x_2, y[i + 2]) + + 37 * self.func(x_1, y[i + 1]) + - 9 * self.func(x_0, y[i]) + ) + x_0 = x_1 + x_1 = x_2 + x_2 = x_3 + x_3 += self.step_size + + return y + + def step_5(self) -> np.ndarray: + """ + >>> def f(x,y): + ... return x + y + >>> y = AdamsBashforth( + ... f, [0, 0.2, 0.4, 0.6, 0.8], [0, 0.02140, 0.02140, 0.22211, 0.42536], + ... 0.2, 1).step_5() + >>> y[-1] + 0.05436839444444452 + + >>> AdamsBashforth(f, [0, 0.2, 0.4], [0, 0, 0.04], 0.2, 1).step_5() + Traceback (most recent call last): + ... + ValueError: Insufficient initial points information. + """ + + if len(self.x_initials) != 5 or len(self.y_initials) != 5: + raise ValueError("Insufficient initial points information.") + + x_0, x_1, x_2, x_3, x_4 = self.x_initials[:5] + y_0, y_1, y_2, y_3, y_4 = self.y_initials[:5] + + n = int((self.x_final - x_4) / self.step_size) + y = np.zeros(n + 6) + y[0] = y_0 + y[1] = y_1 + y[2] = y_2 + y[3] = y_3 + y[4] = y_4 + + for i in range(n + 1): + y[i + 5] = y[i + 4] + (self.step_size / 720) * ( + 1901 * self.func(x_4, y[i + 4]) + - 2774 * self.func(x_3, y[i + 3]) + - 2616 * self.func(x_2, y[i + 2]) + - 1274 * self.func(x_1, y[i + 1]) + + 251 * self.func(x_0, y[i]) + ) + x_0 = x_1 + x_1 = x_2 + x_2 = x_3 + x_3 = x_4 + x_4 += self.step_size + + return y + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From aa5c97d72c2382ed07c54b17d0b0d74684ca4734 Mon Sep 17 00:00:00 2001 From: Tapas Singhal <98687345+Shocker-lov-t@users.noreply.github.com> Date: Sun, 29 Oct 2023 04:17:46 +0530 Subject: [PATCH 499/757] Create ipv4_conversion.py (#11008) * Create ipconversion.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update conversions/ipconversion.py * Update ipconversion.py * Rename ipconversion.py to ipv4_conversion.py * forward_propagation(32, 450_000) # Was 10_000_000 --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- conversions/ipv4_conversion.py | 85 +++++++++++++++++++++++++ neural_network/simple_neural_network.py | 2 +- 2 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 conversions/ipv4_conversion.py diff --git a/conversions/ipv4_conversion.py b/conversions/ipv4_conversion.py new file mode 100644 index 000000000000..862309b7251e --- /dev/null +++ b/conversions/ipv4_conversion.py @@ -0,0 +1,85 @@ +# https://www.geeksforgeeks.org/convert-ip-address-to-integer-and-vice-versa/ + + +def ipv4_to_decimal(ipv4_address: str) -> int: + """ + Convert an IPv4 address to its decimal representation. + + Args: + ip_address: A string representing an IPv4 address (e.g., "192.168.0.1"). + + Returns: + int: The decimal representation of the IP address. + + >>> ipv4_to_decimal("192.168.0.1") + 3232235521 + >>> ipv4_to_decimal("10.0.0.255") + 167772415 + >>> ipv4_to_decimal("10.0.255") + Traceback (most recent call last): + ... + ValueError: Invalid IPv4 address format + >>> ipv4_to_decimal("10.0.0.256") + Traceback (most recent call last): + ... + ValueError: Invalid IPv4 octet 256 + """ + + octets = [int(octet) for octet in ipv4_address.split(".")] + if len(octets) != 4: + raise ValueError("Invalid IPv4 address format") + + decimal_ipv4 = 0 + for octet in octets: + if not 0 <= octet <= 255: + raise ValueError(f"Invalid IPv4 octet {octet}") # noqa: EM102 + decimal_ipv4 = (decimal_ipv4 << 8) + int(octet) + + return decimal_ipv4 + + +def alt_ipv4_to_decimal(ipv4_address: str) -> int: + """ + >>> alt_ipv4_to_decimal("192.168.0.1") + 3232235521 + >>> alt_ipv4_to_decimal("10.0.0.255") + 167772415 + """ + return int("0x" + "".join(f"{int(i):02x}" for i in ipv4_address.split(".")), 16) + + +def decimal_to_ipv4(decimal_ipv4: int) -> str: + """ + Convert a decimal representation of an IP address to its IPv4 format. + + Args: + decimal_ipv4: An integer representing the decimal IP address. + + Returns: + The IPv4 representation of the decimal IP address. + + >>> decimal_to_ipv4(3232235521) + '192.168.0.1' + >>> decimal_to_ipv4(167772415) + '10.0.0.255' + >>> decimal_to_ipv4(-1) + Traceback (most recent call last): + ... + ValueError: Invalid decimal IPv4 address + """ + + if not (0 <= decimal_ipv4 <= 4294967295): + raise ValueError("Invalid decimal IPv4 address") + + ip_parts = [] + for _ in range(4): + ip_parts.append(str(decimal_ipv4 & 255)) + decimal_ipv4 >>= 8 + + return ".".join(reversed(ip_parts)) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() diff --git a/neural_network/simple_neural_network.py b/neural_network/simple_neural_network.py index f2a3234873b5..8751a38908cf 100644 --- a/neural_network/simple_neural_network.py +++ b/neural_network/simple_neural_network.py @@ -28,7 +28,7 @@ def sigmoid_function(value: float, deriv: bool = False) -> float: def forward_propagation(expected: int, number_propagations: int) -> float: """Return the value found after the forward propagation training. - >>> res = forward_propagation(32, 10000000) + >>> res = forward_propagation(32, 450_000) # Was 10_000_000 >>> res > 31 and res < 33 True From e3eb9daba41512280dd54205c532874ccd2f1b91 Mon Sep 17 00:00:00 2001 From: Ed Date: Sat, 28 Oct 2023 15:48:50 -0700 Subject: [PATCH 500/757] Add bitap_string_match algo (#11060) * Add bitap_string_match algo * Fix types * Fix spelling and add ignore word * Add suggested changes and change return type * Resolve suggestions --- pyproject.toml | 2 +- strings/bitap_string_match.py | 79 +++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 strings/bitap_string_match.py diff --git a/pyproject.toml b/pyproject.toml index 790a328b3564..5d27142d16e2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -135,5 +135,5 @@ omit = [ sort = "Cover" [tool.codespell] -ignore-words-list = "3rt,ans,crate,damon,fo,followings,hist,iff,kwanza,manuel,mater,secant,som,sur,tim,toi,zar" +ignore-words-list = "3rt,ans,bitap,crate,damon,fo,followings,hist,iff,kwanza,manuel,mater,secant,som,sur,tim,toi,zar" skip = "./.*,*.json,ciphers/prehistoric_men.txt,project_euler/problem_022/p022_names.txt,pyproject.toml,strings/dictionary.txt,strings/words.txt" diff --git a/strings/bitap_string_match.py b/strings/bitap_string_match.py new file mode 100644 index 000000000000..bd8a0f0d73ec --- /dev/null +++ b/strings/bitap_string_match.py @@ -0,0 +1,79 @@ +""" +Bitap exact string matching +https://en.wikipedia.org/wiki/Bitap_algorithm + +Searches for a pattern inside text, and returns the index of the first occurrence +of the pattern. Both text and pattern consist of lowercase alphabetical characters only. + +Complexity: O(m*n) + n = length of text + m = length of pattern + +Python doctests can be run using this command: +python3 -m doctest -v bitap_string_match.py +""" + + +def bitap_string_match(text: str, pattern: str) -> int: + """ + Retrieves the index of the first occurrence of pattern in text. + + Args: + text: A string consisting only of lowercase alphabetical characters. + pattern: A string consisting only of lowercase alphabetical characters. + + Returns: + int: The index where pattern first occurs. Return -1 if not found. + + >>> bitap_string_match('abdabababc', 'ababc') + 5 + >>> bitap_string_match('aaaaaaaaaaaaaaaaaa', 'a') + 0 + >>> bitap_string_match('zxywsijdfosdfnso', 'zxywsijdfosdfnso') + 0 + >>> bitap_string_match('abdabababc', '') + 0 + >>> bitap_string_match('abdabababc', 'c') + 9 + >>> bitap_string_match('abdabababc', 'fofosdfo') + -1 + >>> bitap_string_match('abdab', 'fofosdfo') + -1 + """ + if not pattern: + return 0 + m = len(pattern) + if m > len(text): + return -1 + + # Initial state of bit string 1110 + state = ~1 + # Bit = 0 if character appears at index, and 1 otherwise + pattern_mask: list[int] = [~0] * 27 # 1111 + + for i, char in enumerate(pattern): + # For the pattern mask for this character, set the bit to 0 for each i + # the character appears. + pattern_index: int = ord(char) - ord("a") + pattern_mask[pattern_index] &= ~(1 << i) + + for i, char in enumerate(text): + text_index = ord(char) - ord("a") + # If this character does not appear in pattern, it's pattern mask is 1111. + # Performing a bitwise OR between state and 1111 will reset the state to 1111 + # and start searching the start of pattern again. + state |= pattern_mask[text_index] + state <<= 1 + + # If the mth bit (counting right to left) of the state is 0, then we have + # found pattern in text + if (state & (1 << m)) == 0: + return i - m + 1 + + return -1 + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 50195616817983e8c820daf41c252ecbabac0ae2 Mon Sep 17 00:00:00 2001 From: Tapas Singhal <98687345+Shocker-lov-t@users.noreply.github.com> Date: Sun, 29 Oct 2023 13:12:32 +0530 Subject: [PATCH 501/757] Create multiplexer.py (#11064) * Create multiplexer.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Doctests should show how the algorithm fails --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- boolean_algebra/multiplexer.py | 42 ++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 boolean_algebra/multiplexer.py diff --git a/boolean_algebra/multiplexer.py b/boolean_algebra/multiplexer.py new file mode 100644 index 000000000000..7e65c785c829 --- /dev/null +++ b/boolean_algebra/multiplexer.py @@ -0,0 +1,42 @@ +def mux(input0: int, input1: int, select: int) -> int: + """ + Implement a 2-to-1 Multiplexer. + + :param input0: The first input value (0 or 1). + :param input1: The second input value (0 or 1). + :param select: The select signal (0 or 1) to choose between input0 and input1. + :return: The output based on the select signal. input1 if select else input0. + + https://www.electrically4u.com/solved-problems-on-multiplexer + https://en.wikipedia.org/wiki/Multiplexer + + >>> mux(0, 1, 0) + 0 + >>> mux(0, 1, 1) + 1 + >>> mux(1, 0, 0) + 1 + >>> mux(1, 0, 1) + 0 + >>> mux(2, 1, 0) + Traceback (most recent call last): + ... + ValueError: Inputs and select signal must be 0 or 1 + >>> mux(0, -1, 0) + Traceback (most recent call last): + ... + ValueError: Inputs and select signal must be 0 or 1 + >>> mux(0, 1, 1.1) + Traceback (most recent call last): + ... + ValueError: Inputs and select signal must be 0 or 1 + """ + if all(i in (0, 1) for i in (input0, input1, select)): + return input1 if select else input0 + raise ValueError("Inputs and select signal must be 0 or 1") + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From bad39cd15439f4adeab06707c7ceab2de85adb7f Mon Sep 17 00:00:00 2001 From: ojas wani <52542740+ojas-wani@users.noreply.github.com> Date: Sun, 29 Oct 2023 02:37:07 -0700 Subject: [PATCH 502/757] Add more doctest to intro_sort.py #9943 (#11068) * added laplacian_filter file * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * updated laplacian.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * updated laplacian_py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * updated laplacian_filter.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * updated laplacian_filter.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * updated laplacian_filter.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * required changes to laplacian file * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * changed laplacian_filter.py * changed laplacian_filter.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * changed laplacian_filter.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * changed laplacian_filter.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * updated laplacian_filter.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * update laplacian_filter.py * update laplacian_filter.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * update laplacian_filter.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * changed laplacian_filter.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * changed laplacian_filter.py * changed laplacian_filter.py * changed laplacian_filter.py * add matrix_multiplication.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * update matrix_multiplication * update matrix_multiplication * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * make changes * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * update * update * updates * resolve conflict * add doctest * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * make changes * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * update laplacian.py * add doctests * more doctest added * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * try to resolve ruff error * try to reslve ruff error * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * update doctest * attemp - resolve ruff error * resolve build error * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * resolve build issue * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * update build * doctest update * update doctest * update doctest * update doctest * fix ruff error * file location changed * Delete digital_image_processing/filters/laplacian_filter.py * Create laplacian_filter.py * Update matrix_multiplication_recursion.py * Update matrix_multiplication_recursion.py * Update matrix_multiplication_recursion.py * Update matrix_multiplication_recursion.py * Update matrix_multiplication_recursion.py * Add doctest to median_of_3 * add doctest to median_of_3 function * Update intro_sort.py * Update sorts/intro_sort.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- sorts/intro_sort.py | 31 +++++++++++-------------------- 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/sorts/intro_sort.py b/sorts/intro_sort.py index f0e3645adbb7..908d2886533a 100644 --- a/sorts/intro_sort.py +++ b/sorts/intro_sort.py @@ -1,5 +1,5 @@ """ -Introspective Sort is hybrid sort (Quick Sort + Heap Sort + Insertion Sort) +Introspective Sort is a hybrid sort (Quick Sort + Heap Sort + Insertion Sort) if the size of the list is under 16, use insertion sort https://en.wikipedia.org/wiki/Introsort """ @@ -9,7 +9,6 @@ def insertion_sort(array: list, start: int = 0, end: int = 0) -> list: """ >>> array = [4, 2, 6, 8, 1, 7, 8, 22, 14, 56, 27, 79, 23, 45, 14, 12] - >>> insertion_sort(array, 0, len(array)) [1, 2, 4, 6, 7, 8, 8, 12, 14, 14, 22, 23, 27, 45, 56, 79] """ @@ -27,8 +26,7 @@ def insertion_sort(array: list, start: int = 0, end: int = 0) -> list: def heapify(array: list, index: int, heap_size: int) -> None: # Max Heap """ >>> array = [4, 2, 6, 8, 1, 7, 8, 22, 14, 56, 27, 79, 23, 45, 14, 12] - - >>> heapify(array, len(array) // 2 ,len(array)) + >>> heapify(array, len(array) // 2, len(array)) """ largest = index left_index = 2 * index + 1 # Left Node @@ -47,9 +45,7 @@ def heapify(array: list, index: int, heap_size: int) -> None: # Max Heap def heap_sort(array: list) -> list: """ - >>> array = [4, 2, 6, 8, 1, 7, 8, 22, 14, 56, 27, 79, 23, 45, 14, 12] - - >>> heap_sort(array) + >>> heap_sort([4, 2, 6, 8, 1, 7, 8, 22, 14, 56, 27, 79, 23, 45, 14, 12]) [1, 2, 4, 6, 7, 8, 8, 12, 14, 14, 22, 23, 27, 45, 56, 79] """ n = len(array) @@ -69,9 +65,14 @@ def median_of_3( ) -> int: """ >>> array = [4, 2, 6, 8, 1, 7, 8, 22, 14, 56, 27, 79, 23, 45, 14, 12] - - >>> median_of_3(array, 0, 0 + ((len(array) - 0) // 2) + 1, len(array) - 1) + >>> median_of_3(array, 0, ((len(array) - 0) // 2) + 1, len(array) - 1) 12 + >>> array = [13, 2, 6, 8, 1, 7, 8, 22, 14, 56, 27, 79, 23, 45, 14, 12] + >>> median_of_3(array, 0, ((len(array) - 0) // 2) + 1, len(array) - 1) + 13 + >>> array = [4, 2, 6, 8, 1, 7, 8, 22, 15, 14, 27, 79, 23, 45, 14, 16] + >>> median_of_3(array, 0, ((len(array) - 0) // 2) + 1, len(array) - 1) + 14 """ if (array[first_index] > array[middle_index]) != ( array[first_index] > array[last_index] @@ -88,7 +89,6 @@ def median_of_3( def partition(array: list, low: int, high: int, pivot: int) -> int: """ >>> array = [4, 2, 6, 8, 1, 7, 8, 22, 14, 56, 27, 79, 23, 45, 14, 12] - >>> partition(array, 0, len(array), 12) 8 """ @@ -115,22 +115,16 @@ def sort(array: list) -> list: Examples: >>> sort([4, 2, 6, 8, 1, 7, 8, 22, 14, 56, 27, 79, 23, 45, 14, 12]) [1, 2, 4, 6, 7, 8, 8, 12, 14, 14, 22, 23, 27, 45, 56, 79] - >>> sort([-1, -5, -3, -13, -44]) [-44, -13, -5, -3, -1] - >>> sort([]) [] - >>> sort([5]) [5] - >>> sort([-3, 0, -7, 6, 23, -34]) [-34, -7, -3, 0, 6, 23] - >>> sort([1.7, 1.0, 3.3, 2.1, 0.3 ]) [0.3, 1.0, 1.7, 2.1, 3.3] - >>> sort(['d', 'a', 'b', 'e', 'c']) ['a', 'b', 'c', 'd', 'e'] """ @@ -146,9 +140,7 @@ def intro_sort( ) -> list: """ >>> array = [4, 2, 6, 8, 1, 7, 8, 22, 14, 56, 27, 79, 23, 45, 14, 12] - >>> max_depth = 2 * math.ceil(math.log2(len(array))) - >>> intro_sort(array, 0, len(array), 16, max_depth) [1, 2, 4, 6, 7, 8, 8, 12, 14, 14, 22, 23, 27, 45, 56, 79] """ @@ -167,7 +159,6 @@ def intro_sort( import doctest doctest.testmod() - user_input = input("Enter numbers separated by a comma : ").strip() unsorted = [float(item) for item in user_input.split(",")] - print(sort(unsorted)) + print(f"{sort(unsorted) = }") From adb13a106389aa2382a6315e9f008f9f855a89f8 Mon Sep 17 00:00:00 2001 From: SEIKH NABAB UDDIN <93948993+nababuddin@users.noreply.github.com> Date: Sun, 29 Oct 2023 15:22:50 +0530 Subject: [PATCH 503/757] Update instagram_pic.py (#10957) * Update instagram_pic.py * Update instagram_pic.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update instagram_pic.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update instagram_pic.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update instagram_pic.py * Update instagram_pic.py * Update instagram_pic.py * Update instagram_pic.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update instagram_pic.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update instagram_pic.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update instagram_pic.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fast fail instead of nested ifs and PEP8: Keep try/except blocks small * Update instagram_pic.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- web_programming/instagram_pic.py | 51 +++++++++++++++++++++++++------- 1 file changed, 41 insertions(+), 10 deletions(-) diff --git a/web_programming/instagram_pic.py b/web_programming/instagram_pic.py index 8521da674d7d..2630c8659232 100644 --- a/web_programming/instagram_pic.py +++ b/web_programming/instagram_pic.py @@ -3,14 +3,45 @@ import requests from bs4 import BeautifulSoup -if __name__ == "__main__": - url = input("Enter image url: ").strip() - print(f"Downloading image from {url} ...") - soup = BeautifulSoup(requests.get(url).content, "html.parser") - # The image URL is in the content field of the first meta tag with property og:image - image_url = soup.find("meta", {"property": "og:image"})["content"] - image_data = requests.get(image_url).content + +def download_image(url: str) -> str: + """ + Download an image from a given URL by scraping the 'og:image' meta tag. + + Parameters: + url: The URL to scrape. + + Returns: + A message indicating the result of the operation. + """ + try: + response = requests.get(url) + response.raise_for_status() + except requests.exceptions.RequestException as e: + return f"An error occurred during the HTTP request to {url}: {e!r}" + + soup = BeautifulSoup(response.text, "html.parser") + image_meta_tag = soup.find("meta", {"property": "og:image"}) + if not image_meta_tag: + return "No meta tag with property 'og:image' was found." + + image_url = image_meta_tag.get("content") + if not image_url: + return f"Image URL not found in meta tag {image_meta_tag}." + + try: + image_data = requests.get(image_url).content + except requests.exceptions.RequestException as e: + return f"An error occurred during the HTTP request to {image_url}: {e!r}" + if not image_data: + return f"Failed to download the image from {image_url}." + file_name = f"{datetime.now():%Y-%m-%d_%H:%M:%S}.jpg" - with open(file_name, "wb") as fp: - fp.write(image_data) - print(f"Done. Image saved to disk as {file_name}.") + with open(file_name, "wb") as out_file: + out_file.write(image_data) + return f"Image downloaded and saved in the file {file_name}" + + +if __name__ == "__main__": + url = input("Enter image URL: ").strip() or "https://www.instagram.com" + print(f"download_image({url}): {download_image(url)}") From 8217f9bd35e5975e3660217b37b2aac62c1280da Mon Sep 17 00:00:00 2001 From: Tapas Singhal <98687345+Shocker-lov-t@users.noreply.github.com> Date: Sun, 29 Oct 2023 15:55:39 +0530 Subject: [PATCH 504/757] Create find_previous_power_of_two.py (#11004) * Create find_previous_power_of_two.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update find_previous_power_of_two.py This change avoids the unnecessary left shift operation * Update find_previous_power_of_two.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- .../find_previous_power_of_two.py | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 bit_manipulation/find_previous_power_of_two.py diff --git a/bit_manipulation/find_previous_power_of_two.py b/bit_manipulation/find_previous_power_of_two.py new file mode 100644 index 000000000000..8ac74ac98478 --- /dev/null +++ b/bit_manipulation/find_previous_power_of_two.py @@ -0,0 +1,30 @@ +def find_previous_power_of_two(number: int) -> int: + """ + Find the largest power of two that is less than or equal to a given integer. + https://stackoverflow.com/questions/1322510 + + >>> [find_previous_power_of_two(i) for i in range(18)] + [0, 1, 2, 2, 4, 4, 4, 4, 8, 8, 8, 8, 8, 8, 8, 8, 16, 16] + >>> find_previous_power_of_two(-5) + Traceback (most recent call last): + ... + ValueError: Input must be a non-negative integer + >>> find_previous_power_of_two(10.5) + Traceback (most recent call last): + ... + ValueError: Input must be a non-negative integer + """ + if not isinstance(number, int) or number < 0: + raise ValueError("Input must be a non-negative integer") + if number == 0: + return 0 + power = 1 + while power <= number: + power <<= 1 # Equivalent to multiplying by 2 + return power >> 1 if number > 1 else 1 + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 403d3b8a29e754b8f3bbb1000a54fee42a87341b Mon Sep 17 00:00:00 2001 From: Aqib Javid Bhat Date: Sun, 29 Oct 2023 16:28:28 +0530 Subject: [PATCH 505/757] Add Integer Square Root Algorithm (#10949) * Add Integer Square Root Algorithm * Update integer_square_root.py * Update integer_square_root.py --------- Co-authored-by: Christian Clauss --- maths/integer_square_root.py | 73 ++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 maths/integer_square_root.py diff --git a/maths/integer_square_root.py b/maths/integer_square_root.py new file mode 100644 index 000000000000..27e874a43c79 --- /dev/null +++ b/maths/integer_square_root.py @@ -0,0 +1,73 @@ +""" +Integer Square Root Algorithm -- An efficient method to calculate the square root of a +non-negative integer 'num' rounded down to the nearest integer. It uses a binary search +approach to find the integer square root without using any built-in exponent functions +or operators. +* https://en.wikipedia.org/wiki/Integer_square_root +* https://docs.python.org/3/library/math.html#math.isqrt +Note: + - This algorithm is designed for non-negative integers only. + - The result is rounded down to the nearest integer. + - The algorithm has a time complexity of O(log(x)). + - Original algorithm idea based on binary search. +""" + + +def integer_square_root(num: int) -> int: + """ + Returns the integer square root of a non-negative integer num. + Args: + num: A non-negative integer. + Returns: + The integer square root of num. + Raises: + ValueError: If num is not an integer or is negative. + >>> [integer_square_root(i) for i in range(18)] + [0, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 4, 4] + >>> integer_square_root(625) + 25 + >>> integer_square_root(2_147_483_647) + 46340 + >>> from math import isqrt + >>> all(integer_square_root(i) == isqrt(i) for i in range(20)) + True + >>> integer_square_root(-1) + Traceback (most recent call last): + ... + ValueError: num must be non-negative integer + >>> integer_square_root(1.5) + Traceback (most recent call last): + ... + ValueError: num must be non-negative integer + >>> integer_square_root("0") + Traceback (most recent call last): + ... + ValueError: num must be non-negative integer + """ + if not isinstance(num, int) or num < 0: + raise ValueError("num must be non-negative integer") + + if num < 2: + return num + + left_bound = 0 + right_bound = num // 2 + + while left_bound <= right_bound: + mid = left_bound + (right_bound - left_bound) // 2 + mid_squared = mid * mid + if mid_squared == num: + return mid + + if mid_squared < num: + left_bound = mid + 1 + else: + right_bound = mid - 1 + + return right_bound + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From cc22d0b0bac9fec13913ba07bc67d58c06482c83 Mon Sep 17 00:00:00 2001 From: aayushsoni4 <120650736+aayushsoni4@users.noreply.github.com> Date: Sun, 29 Oct 2023 18:25:31 +0530 Subject: [PATCH 506/757] Generate parentheses (#10903) * Add: Matrix Prefix Sum * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Changes made in Matrix Prefix Sum * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Changes made in Matrix Prefix Sum * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Changes made in Matrix Prefix Sum * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Changes made in Matrix Prefix Sum * Changes made in Matrix Prefix Sum * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Changes made in Matrix Prefix Sum * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Changes made in Matrix Prefix Sum * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Changes made in Matrix Prefix Sum * Changes made in Matrix Prefix Sum * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Changes made in Matrix Prefix Sum * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Changes made in Matrix Prefix Sum * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Changes made in Matrix Prefix Sum * Changes made in Matrix Prefix Sum * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Changes made in Matrix Prefix Sum * Changes made in Matrix Prefix Sum * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Changes made in Matrix Prefix Sum * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Changes made in Matrix Prefix Sum * Add: Distinct Subsequences * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Changes made in Distinct Subsequences * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Changes made in Distinct Subsequences * Changes made in Distinct Subsequences * Changes made in Distinct Subsequences * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Removed Distinct Subsequences * Add: Generate Parentheses * Add: Generate Parentheses * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Add: Generate Parentheses * Add: Generate Parentheses * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Add: Generate Parentheses * Add: Generate Parentheses * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update backtracking/generate_parentheses.py * Delete matrix/matrix_prefix_sum.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- backtracking/generate_parentheses.py | 77 ++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 backtracking/generate_parentheses.py diff --git a/backtracking/generate_parentheses.py b/backtracking/generate_parentheses.py new file mode 100644 index 000000000000..18c21e2a9b51 --- /dev/null +++ b/backtracking/generate_parentheses.py @@ -0,0 +1,77 @@ +""" +author: Aayush Soni +Given n pairs of parentheses, write a function to generate all +combinations of well-formed parentheses. +Input: n = 2 +Output: ["(())","()()"] +Leetcode link: https://leetcode.com/problems/generate-parentheses/description/ +""" + + +def backtrack( + partial: str, open_count: int, close_count: int, n: int, result: list[str] +) -> None: + """ + Generate valid combinations of balanced parentheses using recursion. + + :param partial: A string representing the current combination. + :param open_count: An integer representing the count of open parentheses. + :param close_count: An integer representing the count of close parentheses. + :param n: An integer representing the total number of pairs. + :param result: A list to store valid combinations. + :return: None + + This function uses recursion to explore all possible combinations, + ensuring that at each step, the parentheses remain balanced. + + Example: + >>> result = [] + >>> backtrack("", 0, 0, 2, result) + >>> result + ['(())', '()()'] + """ + if len(partial) == 2 * n: + # When the combination is complete, add it to the result. + result.append(partial) + return + + if open_count < n: + # If we can add an open parenthesis, do so, and recurse. + backtrack(partial + "(", open_count + 1, close_count, n, result) + + if close_count < open_count: + # If we can add a close parenthesis (it won't make the combination invalid), + # do so, and recurse. + backtrack(partial + ")", open_count, close_count + 1, n, result) + + +def generate_parenthesis(n: int) -> list[str]: + """ + Generate valid combinations of balanced parentheses for a given n. + + :param n: An integer representing the number of pairs of parentheses. + :return: A list of strings with valid combinations. + + This function uses a recursive approach to generate the combinations. + + Time Complexity: O(2^(2n)) - In the worst case, we have 2^(2n) combinations. + Space Complexity: O(n) - where 'n' is the number of pairs. + + Example 1: + >>> generate_parenthesis(3) + ['((()))', '(()())', '(())()', '()(())', '()()()'] + + Example 2: + >>> generate_parenthesis(1) + ['()'] + """ + + result: list[str] = [] + backtrack("", 0, 0, n, result) + return result + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 7c1dfec08644e4034717844b139e8db948706ccc Mon Sep 17 00:00:00 2001 From: Farzad Hayat Date: Sun, 29 Oct 2023 22:57:04 +1000 Subject: [PATCH 507/757] XOR Cipher: doctests and bug fixes (#10840) * Fixed bug with key modulus wrapping. Should be wrapping on 256, not 255. * Fixed bug with incorrect assertion type in decrypt function. * Added doctests for 4 out of 6 methods --- ciphers/xor_cipher.py | 91 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 82 insertions(+), 9 deletions(-) diff --git a/ciphers/xor_cipher.py b/ciphers/xor_cipher.py index 559036d305c5..e30955d41ff1 100644 --- a/ciphers/xor_cipher.py +++ b/ciphers/xor_cipher.py @@ -35,6 +35,22 @@ def encrypt(self, content: str, key: int) -> list[str]: output: encrypted string 'content' as a list of chars if key not passed the method uses the key by the constructor. otherwise key = 1 + + Empty list + >>> XORCipher().encrypt("", 5) + [] + + One key + >>> XORCipher().encrypt("hallo welt", 1) + ['i', '`', 'm', 'm', 'n', '!', 'v', 'd', 'm', 'u'] + + Normal key + >>> XORCipher().encrypt("HALLO WELT", 32) + ['h', 'a', 'l', 'l', 'o', '\\x00', 'w', 'e', 'l', 't'] + + Key greater than 255 + >>> XORCipher().encrypt("hallo welt", 256) + ['h', 'a', 'l', 'l', 'o', ' ', 'w', 'e', 'l', 't'] """ # precondition @@ -44,7 +60,7 @@ def encrypt(self, content: str, key: int) -> list[str]: key = key or self.__key or 1 # make sure key is an appropriate size - key %= 255 + key %= 256 return [chr(ord(ch) ^ key) for ch in content] @@ -54,16 +70,32 @@ def decrypt(self, content: str, key: int) -> list[str]: output: decrypted string 'content' as a list of chars if key not passed the method uses the key by the constructor. otherwise key = 1 + + Empty list + >>> XORCipher().decrypt("", 5) + [] + + One key + >>> XORCipher().decrypt("hallo welt", 1) + ['i', '`', 'm', 'm', 'n', '!', 'v', 'd', 'm', 'u'] + + Normal key + >>> XORCipher().decrypt("HALLO WELT", 32) + ['h', 'a', 'l', 'l', 'o', '\\x00', 'w', 'e', 'l', 't'] + + Key greater than 255 + >>> XORCipher().decrypt("hallo welt", 256) + ['h', 'a', 'l', 'l', 'o', ' ', 'w', 'e', 'l', 't'] """ # precondition assert isinstance(key, int) - assert isinstance(content, list) + assert isinstance(content, str) key = key or self.__key or 1 # make sure key is an appropriate size - key %= 255 + key %= 256 return [chr(ord(ch) ^ key) for ch in content] @@ -73,6 +105,22 @@ def encrypt_string(self, content: str, key: int = 0) -> str: output: encrypted string 'content' if key not passed the method uses the key by the constructor. otherwise key = 1 + + Empty list + >>> XORCipher().encrypt_string("", 5) + '' + + One key + >>> XORCipher().encrypt_string("hallo welt", 1) + 'i`mmn!vdmu' + + Normal key + >>> XORCipher().encrypt_string("HALLO WELT", 32) + 'hallo\\x00welt' + + Key greater than 255 + >>> XORCipher().encrypt_string("hallo welt", 256) + 'hallo welt' """ # precondition @@ -81,9 +129,8 @@ def encrypt_string(self, content: str, key: int = 0) -> str: key = key or self.__key or 1 - # make sure key can be any size - while key > 255: - key -= 255 + # make sure key is an appropriate size + key %= 256 # This will be returned ans = "" @@ -99,6 +146,22 @@ def decrypt_string(self, content: str, key: int = 0) -> str: output: decrypted string 'content' if key not passed the method uses the key by the constructor. otherwise key = 1 + + Empty list + >>> XORCipher().decrypt_string("", 5) + '' + + One key + >>> XORCipher().decrypt_string("hallo welt", 1) + 'i`mmn!vdmu' + + Normal key + >>> XORCipher().decrypt_string("HALLO WELT", 32) + 'hallo\\x00welt' + + Key greater than 255 + >>> XORCipher().decrypt_string("hallo welt", 256) + 'hallo welt' """ # precondition @@ -107,9 +170,8 @@ def decrypt_string(self, content: str, key: int = 0) -> str: key = key or self.__key or 1 - # make sure key can be any size - while key > 255: - key -= 255 + # make sure key is an appropriate size + key %= 256 # This will be returned ans = "" @@ -132,6 +194,9 @@ def encrypt_file(self, file: str, key: int = 0) -> bool: assert isinstance(file, str) assert isinstance(key, int) + # make sure key is an appropriate size + key %= 256 + try: with open(file) as fin, open("encrypt.out", "w+") as fout: # actual encrypt-process @@ -156,6 +221,9 @@ def decrypt_file(self, file: str, key: int) -> bool: assert isinstance(file, str) assert isinstance(key, int) + # make sure key is an appropriate size + key %= 256 + try: with open(file) as fin, open("decrypt.out", "w+") as fout: # actual encrypt-process @@ -168,6 +236,11 @@ def decrypt_file(self, file: str, key: int) -> bool: return True +if __name__ == "__main__": + from doctest import testmod + + testmod() + # Tests # crypt = XORCipher() # key = 67 From 6b588e4d44085d8f2a60b023f09558442ea7ae91 Mon Sep 17 00:00:00 2001 From: Kento <75509362+nkstonks@users.noreply.github.com> Date: Sun, 29 Oct 2023 23:57:40 +1100 Subject: [PATCH 508/757] Added doctests for fibonacci.py (#10836) * added other possible cases * added test for correct output of truth table * few fibonacci tests added * updating DIRECTORY.md * Update nor_gate.py * updating DIRECTORY.md * Update fibonacci.py removed whitespace * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: = <=> Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: Christian Clauss Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- maths/fibonacci.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/maths/fibonacci.py b/maths/fibonacci.py index e810add69dc7..8cdd6cdb160e 100644 --- a/maths/fibonacci.py +++ b/maths/fibonacci.py @@ -81,6 +81,18 @@ def fib_recursive(n: int) -> list[int]: def fib_recursive_term(i: int) -> int: """ Calculates the i-th (0-indexed) Fibonacci number using recursion + >>> fib_recursive_term(0) + 0 + >>> fib_recursive_term(1) + 1 + >>> fib_recursive_term(5) + 5 + >>> fib_recursive_term(10) + 55 + >>> fib_recursive_term(-1) + Traceback (most recent call last): + ... + Exception: n is negative """ if i < 0: raise Exception("n is negative") @@ -197,6 +209,10 @@ def fib_binet(n: int) -> list[int]: if __name__ == "__main__": + import doctest + + doctest.testmod() + num = 30 time_func(fib_iterative, num) time_func(fib_recursive, num) # Around 3s runtime From d59cf1734fd8216d90fa21ed579e18a41b63755f Mon Sep 17 00:00:00 2001 From: Arshdeep Singh Sachdeva Date: Sun, 29 Oct 2023 07:55:37 -0700 Subject: [PATCH 509/757] Add running key cipher (#10834) * Add running key cipher * update running key cipher add doctests and hints * Add test case * Update return value * range(len()) is almost always a hint to use enumerate() --------- Co-authored-by: Christian Clauss --- ciphers/running_key_cipher.py | 75 +++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 ciphers/running_key_cipher.py diff --git a/ciphers/running_key_cipher.py b/ciphers/running_key_cipher.py new file mode 100644 index 000000000000..6bda417be898 --- /dev/null +++ b/ciphers/running_key_cipher.py @@ -0,0 +1,75 @@ +""" +https://en.wikipedia.org/wiki/Running_key_cipher +""" + + +def running_key_encrypt(key: str, plaintext: str) -> str: + """ + Encrypts the plaintext using the Running Key Cipher. + + :param key: The running key (long piece of text). + :param plaintext: The plaintext to be encrypted. + :return: The ciphertext. + """ + plaintext = plaintext.replace(" ", "").upper() + key = key.replace(" ", "").upper() + key_length = len(key) + ciphertext = [] + ord_a = ord("A") + + for i, char in enumerate(plaintext): + p = ord(char) - ord_a + k = ord(key[i % key_length]) - ord_a + c = (p + k) % 26 + ciphertext.append(chr(c + ord_a)) + + return "".join(ciphertext) + + +def running_key_decrypt(key: str, ciphertext: str) -> str: + """ + Decrypts the ciphertext using the Running Key Cipher. + + :param key: The running key (long piece of text). + :param ciphertext: The ciphertext to be decrypted. + :return: The plaintext. + """ + ciphertext = ciphertext.replace(" ", "").upper() + key = key.replace(" ", "").upper() + key_length = len(key) + plaintext = [] + ord_a = ord("A") + + for i, char in enumerate(ciphertext): + c = ord(char) - ord_a + k = ord(key[i % key_length]) - ord_a + p = (c - k) % 26 + plaintext.append(chr(p + ord_a)) + + return "".join(plaintext) + + +def test_running_key_encrypt() -> None: + """ + >>> key = "How does the duck know that? said Victor" + >>> ciphertext = running_key_encrypt(key, "DEFEND THIS") + >>> running_key_decrypt(key, ciphertext) == "DEFENDTHIS" + True + """ + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + test_running_key_encrypt() + + plaintext = input("Enter the plaintext: ").upper() + print(f"\n{plaintext = }") + + key = "How does the duck know that? said Victor" + encrypted_text = running_key_encrypt(key, plaintext) + print(f"{encrypted_text = }") + + decrypted_text = running_key_decrypt(key, encrypted_text) + print(f"{decrypted_text = }") From 3ad90cea831ee12d9c168735cbd6fab3acac446f Mon Sep 17 00:00:00 2001 From: dragon <51738561+08183080@users.noreply.github.com> Date: Sun, 29 Oct 2023 23:40:01 +0800 Subject: [PATCH 510/757] add a yield method to fibonaci (#10826) * add a yiled method to fibonaci * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fibonaci * Update fibonacci.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update fibonacci.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- maths/fibonacci.py | 79 ++++++++++++++++++++++++++++++---------------- 1 file changed, 51 insertions(+), 28 deletions(-) diff --git a/maths/fibonacci.py b/maths/fibonacci.py index 8cdd6cdb160e..927700b0418e 100644 --- a/maths/fibonacci.py +++ b/maths/fibonacci.py @@ -1,4 +1,3 @@ -# fibonacci.py """ Calculates the Fibonacci sequence using iteration, recursion, memoization, and a simplified form of Binet's formula @@ -9,14 +8,12 @@ NOTE 2: the Binet's formula function is much more limited in the size of inputs that it can handle due to the size limitations of Python floats -RESULTS: (n = 20) -fib_iterative runtime: 0.0055 ms -fib_recursive runtime: 6.5627 ms -fib_memoization runtime: 0.0107 ms -fib_binet runtime: 0.0174 ms +See benchmark numbers in __main__ for performance comparisons/ +https://en.wikipedia.org/wiki/Fibonacci_number for more information """ import functools +from collections.abc import Iterator from math import sqrt from time import time @@ -35,6 +32,31 @@ def time_func(func, *args, **kwargs): return output +def fib_iterative_yield(n: int) -> Iterator[int]: + """ + Calculates the first n (1-indexed) Fibonacci numbers using iteration with yield + >>> list(fib_iterative_yield(0)) + [0] + >>> tuple(fib_iterative_yield(1)) + (0, 1) + >>> tuple(fib_iterative_yield(5)) + (0, 1, 1, 2, 3, 5) + >>> tuple(fib_iterative_yield(10)) + (0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55) + >>> tuple(fib_iterative_yield(-1)) + Traceback (most recent call last): + ... + ValueError: n is negative + """ + if n < 0: + raise ValueError("n is negative") + a, b = 0, 1 + yield a + for _ in range(n): + yield b + a, b = b, a + b + + def fib_iterative(n: int) -> list[int]: """ Calculates the first n (0-indexed) Fibonacci numbers using iteration @@ -49,10 +71,10 @@ def fib_iterative(n: int) -> list[int]: >>> fib_iterative(-1) Traceback (most recent call last): ... - Exception: n is negative + ValueError: n is negative """ if n < 0: - raise Exception("n is negative") + raise ValueError("n is negative") if n == 0: return [0] fib = [0, 1] @@ -75,7 +97,7 @@ def fib_recursive(n: int) -> list[int]: >>> fib_iterative(-1) Traceback (most recent call last): ... - Exception: n is negative + ValueError: n is negative """ def fib_recursive_term(i: int) -> int: @@ -95,13 +117,13 @@ def fib_recursive_term(i: int) -> int: Exception: n is negative """ if i < 0: - raise Exception("n is negative") + raise ValueError("n is negative") if i < 2: return i return fib_recursive_term(i - 1) + fib_recursive_term(i - 2) if n < 0: - raise Exception("n is negative") + raise ValueError("n is negative") return [fib_recursive_term(i) for i in range(n + 1)] @@ -119,7 +141,7 @@ def fib_recursive_cached(n: int) -> list[int]: >>> fib_iterative(-1) Traceback (most recent call last): ... - Exception: n is negative + ValueError: n is negative """ @functools.cache @@ -128,13 +150,13 @@ def fib_recursive_term(i: int) -> int: Calculates the i-th (0-indexed) Fibonacci number using recursion """ if i < 0: - raise Exception("n is negative") + raise ValueError("n is negative") if i < 2: return i return fib_recursive_term(i - 1) + fib_recursive_term(i - 2) if n < 0: - raise Exception("n is negative") + raise ValueError("n is negative") return [fib_recursive_term(i) for i in range(n + 1)] @@ -152,10 +174,10 @@ def fib_memoization(n: int) -> list[int]: >>> fib_iterative(-1) Traceback (most recent call last): ... - Exception: n is negative + ValueError: n is negative """ if n < 0: - raise Exception("n is negative") + raise ValueError("n is negative") # Cache must be outside recursuive function # other it will reset every time it calls itself. cache: dict[int, int] = {0: 0, 1: 1, 2: 1} # Prefilled cache @@ -193,29 +215,30 @@ def fib_binet(n: int) -> list[int]: >>> fib_binet(-1) Traceback (most recent call last): ... - Exception: n is negative + ValueError: n is negative >>> fib_binet(1475) Traceback (most recent call last): ... - Exception: n is too large + ValueError: n is too large """ if n < 0: - raise Exception("n is negative") + raise ValueError("n is negative") if n >= 1475: - raise Exception("n is too large") + raise ValueError("n is too large") sqrt_5 = sqrt(5) phi = (1 + sqrt_5) / 2 return [round(phi**i / sqrt_5) for i in range(n + 1)] if __name__ == "__main__": - import doctest - - doctest.testmod() + from doctest import testmod + testmod() + # Time on an M1 MacBook Pro -- Fastest to slowest num = 30 - time_func(fib_iterative, num) - time_func(fib_recursive, num) # Around 3s runtime - time_func(fib_recursive_cached, num) # Around 0ms runtime - time_func(fib_memoization, num) - time_func(fib_binet, num) + time_func(fib_iterative_yield, num) # 0.0012 ms + time_func(fib_iterative, num) # 0.0031 ms + time_func(fib_binet, num) # 0.0062 ms + time_func(fib_memoization, num) # 0.0100 ms + time_func(fib_recursive_cached, num) # 0.0153 ms + time_func(fib_recursive, num) # 257.0910 ms From 67c85ee289b66f9c8ac02c6732240965eec879a2 Mon Sep 17 00:00:00 2001 From: Suyash Dongre <109069262+Suyashd999@users.noreply.github.com> Date: Sun, 29 Oct 2023 21:31:54 +0530 Subject: [PATCH 511/757] Added doctest to hash_map.py (#11082) * Added doctest to heap.py * Added doctest to hash_map.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update hash_map.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- data_structures/hashing/hash_map.py | 111 +++++++++++++++++++++++++++- 1 file changed, 110 insertions(+), 1 deletion(-) diff --git a/data_structures/hashing/hash_map.py b/data_structures/hashing/hash_map.py index 1dfcc8bbf906..1689e07afd9f 100644 --- a/data_structures/hashing/hash_map.py +++ b/data_structures/hashing/hash_map.py @@ -54,6 +54,14 @@ def _get_next_ind(self, ind: int) -> int: Get next index. Implements linear open addressing. + >>> HashMap(5)._get_next_ind(3) + 4 + >>> HashMap(5)._get_next_ind(5) + 1 + >>> HashMap(5)._get_next_ind(6) + 2 + >>> HashMap(5)._get_next_ind(9) + 0 """ return (ind + 1) % len(self._buckets) @@ -82,6 +90,14 @@ def _is_full(self) -> bool: Return true if we have reached safe capacity. So we need to increase the number of buckets to avoid collisions. + + >>> hm = HashMap(2) + >>> hm._add_item(1, 10) + >>> hm._add_item(2, 20) + >>> hm._is_full() + True + >>> HashMap(2)._is_full() + False """ limit = len(self._buckets) * self._capacity_factor return len(self) >= int(limit) @@ -114,17 +130,104 @@ def _iterate_buckets(self, key: KEY) -> Iterator[int]: ind = self._get_next_ind(ind) def _add_item(self, key: KEY, val: VAL) -> None: + """ + Try to add 3 elements when the size is 5 + >>> hm = HashMap(5) + >>> hm._add_item(1, 10) + >>> hm._add_item(2, 20) + >>> hm._add_item(3, 30) + >>> hm + HashMap(1: 10, 2: 20, 3: 30) + + Try to add 3 elements when the size is 5 + >>> hm = HashMap(5) + >>> hm._add_item(-5, 10) + >>> hm._add_item(6, 30) + >>> hm._add_item(-7, 20) + >>> hm + HashMap(-5: 10, 6: 30, -7: 20) + + Try to add 3 elements when size is 1 + >>> hm = HashMap(1) + >>> hm._add_item(10, 13.2) + >>> hm._add_item(6, 5.26) + >>> hm._add_item(7, 5.155) + >>> hm + HashMap(10: 13.2) + + Trying to add an element with a key that is a floating point value + >>> hm = HashMap(5) + >>> hm._add_item(1.5, 10) + >>> hm + HashMap(1.5: 10) + + 5. Trying to add an item with the same key + >>> hm = HashMap(5) + >>> hm._add_item(1, 10) + >>> hm._add_item(1, 20) + >>> hm + HashMap(1: 20) + """ for ind in self._iterate_buckets(key): if self._try_set(ind, key, val): break def __setitem__(self, key: KEY, val: VAL) -> None: + """ + 1. Changing value of item whose key is present + >>> hm = HashMap(5) + >>> hm._add_item(1, 10) + >>> hm.__setitem__(1, 20) + >>> hm + HashMap(1: 20) + + 2. Changing value of item whose key is not present + >>> hm = HashMap(5) + >>> hm._add_item(1, 10) + >>> hm.__setitem__(0, 20) + >>> hm + HashMap(0: 20, 1: 10) + + 3. Changing the value of the same item multiple times + >>> hm = HashMap(5) + >>> hm._add_item(1, 10) + >>> hm.__setitem__(1, 20) + >>> hm.__setitem__(1, 30) + >>> hm + HashMap(1: 30) + """ if self._is_full(): self._size_up() self._add_item(key, val) def __delitem__(self, key: KEY) -> None: + """ + >>> hm = HashMap(5) + >>> hm._add_item(1, 10) + >>> hm._add_item(2, 20) + >>> hm._add_item(3, 30) + >>> hm.__delitem__(3) + >>> hm + HashMap(1: 10, 2: 20) + >>> hm = HashMap(5) + >>> hm._add_item(-5, 10) + >>> hm._add_item(6, 30) + >>> hm._add_item(-7, 20) + >>> hm.__delitem__(-5) + >>> hm + HashMap(6: 30, -7: 20) + + # Trying to remove a non-existing item + >>> hm = HashMap(5) + >>> hm._add_item(1, 10) + >>> hm._add_item(2, 20) + >>> hm._add_item(3, 30) + >>> hm.__delitem__(4) + Traceback (most recent call last): + ... + KeyError: 4 + """ for ind in self._iterate_buckets(key): item = self._buckets[ind] if item is None: @@ -156,7 +259,13 @@ def __iter__(self) -> Iterator[KEY]: yield from (item.key for item in self._buckets if item) def __repr__(self) -> str: - val_string = " ,".join( + val_string = ", ".join( f"{item.key}: {item.val}" for item in self._buckets if item ) return f"HashMap({val_string})" + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From be60f42a5fe29c0e83a049a803f17992bf66be47 Mon Sep 17 00:00:00 2001 From: Aqib Javid Bhat Date: Sun, 29 Oct 2023 22:12:41 +0530 Subject: [PATCH 512/757] Add Josephus Problem (#10928) * Add Josephus Problem * Add iterative implementation of Josephus Problem * Add descriptive variable names * Update maths/josephus_problem.py * Update josephus_problem.py --------- Co-authored-by: Christian Clauss --- maths/josephus_problem.py | 130 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 maths/josephus_problem.py diff --git a/maths/josephus_problem.py b/maths/josephus_problem.py new file mode 100644 index 000000000000..271292ba1d9f --- /dev/null +++ b/maths/josephus_problem.py @@ -0,0 +1,130 @@ +""" +The Josephus problem is a famous theoretical problem related to a certain +counting-out game. This module provides functions to solve the Josephus problem +for num_people and a step_size. + +The Josephus problem is defined as follows: +- num_people are standing in a circle. +- Starting with a specified person, you count around the circle, + skipping a fixed number of people (step_size). +- The person at which you stop counting is eliminated from the circle. +- The counting continues until only one person remains. + +For more information about the Josephus problem, refer to: +https://en.wikipedia.org/wiki/Josephus_problem +""" + + +def josephus_recursive(num_people: int, step_size: int) -> int: + """ + Solve the Josephus problem for num_people and a step_size recursively. + + Args: + num_people: A positive integer representing the number of people. + step_size: A positive integer representing the step size for elimination. + + Returns: + The position of the last person remaining. + + Raises: + ValueError: If num_people or step_size is not a positive integer. + + Examples: + >>> josephus_recursive(7, 3) + 3 + >>> josephus_recursive(10, 2) + 4 + >>> josephus_recursive(0, 2) + Traceback (most recent call last): + ... + ValueError: num_people or step_size is not a positive integer. + >>> josephus_recursive(1.9, 2) + Traceback (most recent call last): + ... + ValueError: num_people or step_size is not a positive integer. + >>> josephus_recursive(-2, 2) + Traceback (most recent call last): + ... + ValueError: num_people or step_size is not a positive integer. + >>> josephus_recursive(7, 0) + Traceback (most recent call last): + ... + ValueError: num_people or step_size is not a positive integer. + >>> josephus_recursive(7, -2) + Traceback (most recent call last): + ... + ValueError: num_people or step_size is not a positive integer. + >>> josephus_recursive(1_000, 0.01) + Traceback (most recent call last): + ... + ValueError: num_people or step_size is not a positive integer. + >>> josephus_recursive("cat", "dog") + Traceback (most recent call last): + ... + ValueError: num_people or step_size is not a positive integer. + """ + if ( + not isinstance(num_people, int) + or not isinstance(step_size, int) + or num_people <= 0 + or step_size <= 0 + ): + raise ValueError("num_people or step_size is not a positive integer.") + + if num_people == 1: + return 0 + + return (josephus_recursive(num_people - 1, step_size) + step_size) % num_people + + +def find_winner(num_people: int, step_size: int) -> int: + """ + Find the winner of the Josephus problem for num_people and a step_size. + + Args: + num_people (int): Number of people. + step_size (int): Step size for elimination. + + Returns: + int: The position of the last person remaining (1-based index). + + Examples: + >>> find_winner(7, 3) + 4 + >>> find_winner(10, 2) + 5 + """ + return josephus_recursive(num_people, step_size) + 1 + + +def josephus_iterative(num_people: int, step_size: int) -> int: + """ + Solve the Josephus problem for num_people and a step_size iteratively. + + Args: + num_people (int): The number of people in the circle. + step_size (int): The number of steps to take before eliminating someone. + + Returns: + int: The position of the last person standing. + + Examples: + >>> josephus_iterative(5, 2) + 3 + >>> josephus_iterative(7, 3) + 4 + """ + circle = list(range(1, num_people + 1)) + current = 0 + + while len(circle) > 1: + current = (current + step_size - 1) % len(circle) + circle.pop(current) + + return circle[0] + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From eafdb8b86697eb9dbdc03916679719dff2f6425a Mon Sep 17 00:00:00 2001 From: dahhou ilyas <110790236+dahhou-ilyas@users.noreply.github.com> Date: Sun, 29 Oct 2023 18:24:37 +0100 Subject: [PATCH 513/757] Dahhou ilyas (#10058) * add new programme in dynamique programming wildcard_matching * add new programme in dynamique programming wildcard_matching * fix bug * fix * fix * fix * fix * fix * fix error recrusion * fix error recrusion * bug fix * add doctest * The power of enumerate() --------- Co-authored-by: Christian Clauss --- dynamic_programming/wildcard_matching.py | 92 +++++++++++++----------- 1 file changed, 49 insertions(+), 43 deletions(-) diff --git a/dynamic_programming/wildcard_matching.py b/dynamic_programming/wildcard_matching.py index 4ffc4b5d46aa..d9a1392720bd 100644 --- a/dynamic_programming/wildcard_matching.py +++ b/dynamic_programming/wildcard_matching.py @@ -1,62 +1,68 @@ """ -Given two strings, an input string and a pattern, -this program checks if the input string matches the pattern. +Author : ilyas dahhou +Date : Oct 7, 2023 -Example : -input_string = "baaabab" -pattern = "*****ba*****ab" -Output: True +Task: +Given an input string and a pattern, implement wildcard pattern matching with support +for '?' and '*' where: +'?' matches any single character. +'*' matches any sequence of characters (including the empty sequence). +The matching should cover the entire input string (not partial). -This problem can be solved using the concept of "DYNAMIC PROGRAMMING". - -We create a 2D boolean matrix, where each entry match_matrix[i][j] is True -if the first i characters in input_string match the first j characters -of pattern. We initialize the first row and first column based on specific -rules, then fill up the rest of the matrix using a bottom-up dynamic -programming approach. - -The amount of match that will be determined is equal to match_matrix[n][m] -where n and m are lengths of the input_string and pattern respectively. +Runtime complexity: O(m * n) +The implementation was tested on the +leetcode: https://leetcode.com/problems/wildcard-matching/ """ -def is_pattern_match(input_string: str, pattern: str) -> bool: +def is_match(string: str, pattern: str) -> bool: """ - >>> is_pattern_match('baaabab','*****ba*****ba') + >>> is_match("", "") + True + >>> is_match("aa", "a") False - >>> is_pattern_match('baaabab','*****ba*****ab') + >>> is_match("abc", "abc") + True + >>> is_match("abc", "*c") + True + >>> is_match("abc", "a*") True - >>> is_pattern_match('aa','*') + >>> is_match("abc", "*a*") + True + >>> is_match("abc", "?b?") + True + >>> is_match("abc", "*?") + True + >>> is_match("abc", "a*d") + False + >>> is_match("abc", "a*c?") + False + >>> is_match('baaabab','*****ba*****ba') + False + >>> is_match('baaabab','*****ba*****ab') + True + >>> is_match('aa','*') True """ - - input_length = len(input_string) - pattern_length = len(pattern) - - match_matrix = [[False] * (pattern_length + 1) for _ in range(input_length + 1)] - - match_matrix[0][0] = True - - for j in range(1, pattern_length + 1): - if pattern[j - 1] == "*": - match_matrix[0][j] = match_matrix[0][j - 1] - - for i in range(1, input_length + 1): - for j in range(1, pattern_length + 1): - if pattern[j - 1] in ("?", input_string[i - 1]): - match_matrix[i][j] = match_matrix[i - 1][j - 1] + dp = [[False] * (len(pattern) + 1) for _ in string + "1"] + dp[0][0] = True + # Fill in the first row + for j, char in enumerate(pattern, 1): + if char == "*": + dp[0][j] = dp[0][j - 1] + # Fill in the rest of the DP table + for i, s_char in enumerate(string, 1): + for j, p_char in enumerate(pattern, 1): + if p_char in (s_char, "?"): + dp[i][j] = dp[i - 1][j - 1] elif pattern[j - 1] == "*": - match_matrix[i][j] = match_matrix[i - 1][j] or match_matrix[i][j - 1] - else: - match_matrix[i][j] = False - - return match_matrix[input_length][pattern_length] + dp[i][j] = dp[i - 1][j] or dp[i][j - 1] + return dp[len(string)][len(pattern)] if __name__ == "__main__": import doctest doctest.testmod() - - print(f"{is_pattern_match('baaabab','*****ba*****ab')}") + print(f"{is_match('baaabab','*****ba*****ab') = }") From 760d9bedc1a7ff06a75fafaeb519a5b1979a2885 Mon Sep 17 00:00:00 2001 From: Aryansh B Date: Mon, 30 Oct 2023 02:27:37 +0530 Subject: [PATCH 514/757] Added Fast Inverse Square Root (#11054) * Feat: Added Fast inverse square root * Fix: Added typehint * Fix: Added doctests that break the code, changed var name * updating DIRECTORY.md * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix: fixed length of docstring * Update fast_inverse_sqrt.py --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- DIRECTORY.md | 10 +++++++ maths/fast_inverse_sqrt.py | 54 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 maths/fast_inverse_sqrt.py diff --git a/DIRECTORY.md b/DIRECTORY.md index d108acf8dcfb..9b2c8ce735c3 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -34,6 +34,7 @@ * [Bitwise Addition Recursive](bit_manipulation/bitwise_addition_recursive.py) * [Count 1S Brian Kernighan Method](bit_manipulation/count_1s_brian_kernighan_method.py) * [Count Number Of One Bits](bit_manipulation/count_number_of_one_bits.py) + * [Excess 3 Code](bit_manipulation/excess_3_code.py) * [Gray Code Sequence](bit_manipulation/gray_code_sequence.py) * [Highest Set Bit](bit_manipulation/highest_set_bit.py) * [Index Of Rightmost Set Bit](bit_manipulation/index_of_rightmost_set_bit.py) @@ -170,7 +171,10 @@ * Arrays * [Equilibrium Index In Array](data_structures/arrays/equilibrium_index_in_array.py) * [Find Triplets With 0 Sum](data_structures/arrays/find_triplets_with_0_sum.py) + * [Index 2D Array In 1D](data_structures/arrays/index_2d_array_in_1d.py) + * [Kth Largest Element](data_structures/arrays/kth_largest_element.py) * [Median Two Array](data_structures/arrays/median_two_array.py) + * [Monotonic Array](data_structures/arrays/monotonic_array.py) * [Pairs With Given Sum](data_structures/arrays/pairs_with_given_sum.py) * [Permutations](data_structures/arrays/permutations.py) * [Prefix Sum](data_structures/arrays/prefix_sum.py) @@ -368,6 +372,7 @@ ## Electronics * [Apparent Power](electronics/apparent_power.py) * [Builtin Voltage](electronics/builtin_voltage.py) + * [Capacitor Equivalence](electronics/capacitor_equivalence.py) * [Carrier Concentration](electronics/carrier_concentration.py) * [Charging Capacitor](electronics/charging_capacitor.py) * [Charging Inductor](electronics/charging_inductor.py) @@ -531,12 +536,14 @@ ## Machine Learning * [Apriori Algorithm](machine_learning/apriori_algorithm.py) * [Astar](machine_learning/astar.py) + * [Automatic Differentiation](machine_learning/automatic_differentiation.py) * [Data Transformations](machine_learning/data_transformations.py) * [Decision Tree](machine_learning/decision_tree.py) * [Dimensionality Reduction](machine_learning/dimensionality_reduction.py) * Forecasting * [Run](machine_learning/forecasting/run.py) * [Frequent Pattern Growth](machine_learning/frequent_pattern_growth.py) + * [Gradient Boosting Classifier](machine_learning/gradient_boosting_classifier.py) * [Gradient Descent](machine_learning/gradient_descent.py) * [K Means Clust](machine_learning/k_means_clust.py) * [K Nearest Neighbours](machine_learning/k_nearest_neighbours.py) @@ -598,6 +605,7 @@ * [Extended Euclidean Algorithm](maths/extended_euclidean_algorithm.py) * [Factorial](maths/factorial.py) * [Factors](maths/factors.py) + * [Fast Inverse Sqrt](maths/fast_inverse_sqrt.py) * [Fermat Little Theorem](maths/fermat_little_theorem.py) * [Fibonacci](maths/fibonacci.py) * [Find Max](maths/find_max.py) @@ -648,6 +656,7 @@ * [Numerical Integration](maths/numerical_analysis/numerical_integration.py) * [Runge Kutta](maths/numerical_analysis/runge_kutta.py) * [Runge Kutta Fehlberg 45](maths/numerical_analysis/runge_kutta_fehlberg_45.py) + * [Runge Kutta Gills](maths/numerical_analysis/runge_kutta_gills.py) * [Secant Method](maths/numerical_analysis/secant_method.py) * [Simpson Rule](maths/numerical_analysis/simpson_rule.py) * [Square Root](maths/numerical_analysis/square_root.py) @@ -814,6 +823,7 @@ * [Ideal Gas Law](physics/ideal_gas_law.py) * [In Static Equilibrium](physics/in_static_equilibrium.py) * [Kinetic Energy](physics/kinetic_energy.py) + * [Lens Formulae](physics/lens_formulae.py) * [Lorentz Transformation Four Vector](physics/lorentz_transformation_four_vector.py) * [Malus Law](physics/malus_law.py) * [Mass Energy Equivalence](physics/mass_energy_equivalence.py) diff --git a/maths/fast_inverse_sqrt.py b/maths/fast_inverse_sqrt.py new file mode 100644 index 000000000000..79385bb84877 --- /dev/null +++ b/maths/fast_inverse_sqrt.py @@ -0,0 +1,54 @@ +""" +Fast inverse square root (1/sqrt(x)) using the Quake III algorithm. +Reference: https://en.wikipedia.org/wiki/Fast_inverse_square_root +Accuracy: https://en.wikipedia.org/wiki/Fast_inverse_square_root#Accuracy +""" + +import struct + + +def fast_inverse_sqrt(number: float) -> float: + """ + Compute the fast inverse square root of a floating-point number using the famous + Quake III algorithm. + + :param float number: Input number for which to calculate the inverse square root. + :return float: The fast inverse square root of the input number. + + Example: + >>> fast_inverse_sqrt(10) + 0.3156857923527257 + >>> fast_inverse_sqrt(4) + 0.49915357479239103 + >>> fast_inverse_sqrt(4.1) + 0.4932849504615651 + >>> fast_inverse_sqrt(0) + Traceback (most recent call last): + ... + ValueError: Input must be a positive number. + >>> fast_inverse_sqrt(-1) + Traceback (most recent call last): + ... + ValueError: Input must be a positive number. + >>> from math import isclose, sqrt + >>> all(isclose(fast_inverse_sqrt(i), 1 / sqrt(i), rel_tol=0.00132) + ... for i in range(50, 60)) + True + """ + if number <= 0: + raise ValueError("Input must be a positive number.") + i = struct.unpack(">i", struct.pack(">f", number))[0] + i = 0x5F3759DF - (i >> 1) + y = struct.unpack(">f", struct.pack(">i", i))[0] + return y * (1.5 - 0.5 * number * y * y) + + +if __name__ == "__main__": + from doctest import testmod + + testmod() + # https://en.wikipedia.org/wiki/Fast_inverse_square_root#Accuracy + from math import sqrt + + for i in range(5, 101, 5): + print(f"{i:>3}: {(1 / sqrt(i)) - fast_inverse_sqrt(i):.5f}") From c7a1331b34d6644f546f049058c1d9738fbc9b4c Mon Sep 17 00:00:00 2001 From: Khushi Shukla Date: Mon, 30 Oct 2023 02:50:57 +0530 Subject: [PATCH 515/757] Create karnaugh_map_simplification.py (#11056) * Create karnaugh_map_simplification.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update karnaugh_map_simplification.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update karnaugh_map_simplification.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update karnaugh_map_simplification.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update karnaugh_map_simplification.py * Update boolean_algebra/karnaugh_map_simplification.py Co-authored-by: Christian Clauss * Update karnaugh_map_simplification.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update karnaugh_map_simplification.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update karnaugh_map_simplification.py * Update karnaugh_map_simplification.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- .../karnaugh_map_simplification.py | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 boolean_algebra/karnaugh_map_simplification.py diff --git a/boolean_algebra/karnaugh_map_simplification.py b/boolean_algebra/karnaugh_map_simplification.py new file mode 100644 index 000000000000..c7f2d4c6b897 --- /dev/null +++ b/boolean_algebra/karnaugh_map_simplification.py @@ -0,0 +1,55 @@ +""" +https://en.wikipedia.org/wiki/Karnaugh_map +https://www.allaboutcircuits.com/technical-articles/karnaugh-map-boolean-algebraic-simplification-technique +""" + + +def simplify_kmap(kmap: list[list[int]]) -> str: + """ + Simplify the Karnaugh map. + >>> simplify_kmap(kmap=[[0, 1], [1, 1]]) + "A'B + AB' + AB" + >>> simplify_kmap(kmap=[[0, 0], [0, 0]]) + '' + >>> simplify_kmap(kmap=[[0, 1], [1, -1]]) + "A'B + AB' + AB" + >>> simplify_kmap(kmap=[[0, 1], [1, 2]]) + "A'B + AB' + AB" + >>> simplify_kmap(kmap=[[0, 1], [1, 1.1]]) + "A'B + AB' + AB" + >>> simplify_kmap(kmap=[[0, 1], [1, 'a']]) + "A'B + AB' + AB" + """ + simplified_f = [] + for a, row in enumerate(kmap): + for b, item in enumerate(row): + if item: + term = ("A" if a else "A'") + ("B" if b else "B'") + simplified_f.append(term) + return " + ".join(simplified_f) + + +def main() -> None: + """ + Main function to create and simplify a K-Map. + + >>> main() + [0, 1] + [1, 1] + Simplified Expression: + A'B + AB' + AB + """ + kmap = [[0, 1], [1, 1]] + + # Manually generate the product of [0, 1] and [0, 1] + + for row in kmap: + print(row) + + print("Simplified Expression:") + print(simplify_kmap(kmap)) + + +if __name__ == "__main__": + main() + print(f"{simplify_kmap(kmap=[[0, 1], [1, 1]]) = }") From 13e66c18d2738dd7a223c12ebbfc989faa4bcfce Mon Sep 17 00:00:00 2001 From: chien liu Date: Sun, 29 Oct 2023 22:22:19 +0100 Subject: [PATCH 516/757] Fix typo power_using_recursion.py (#11083) --- maths/power_using_recursion.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maths/power_using_recursion.py b/maths/power_using_recursion.py index 462fc45bff64..29283ca0f67c 100644 --- a/maths/power_using_recursion.py +++ b/maths/power_using_recursion.py @@ -43,7 +43,7 @@ def power(base: int, exponent: int) -> float: if __name__ == "__main__": - from doctests import testmod + from doctest import testmod testmod() print("Raise base to the power of exponent using recursion...") From 2531f8e221f04014821e16eb5eb1d3c52e5f174c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ab=C3=ADlio=20Azevedo?= Date: Sun, 29 Oct 2023 18:43:32 -0300 Subject: [PATCH 517/757] test: adding more tests to missing number algorithm (#10394) * test: adding more tests to missing number algorithm * Update missing_number.py --------- Co-authored-by: Christian Clauss --- bit_manipulation/missing_number.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/bit_manipulation/missing_number.py b/bit_manipulation/missing_number.py index 32b949daa717..554887b17562 100644 --- a/bit_manipulation/missing_number.py +++ b/bit_manipulation/missing_number.py @@ -11,6 +11,12 @@ def find_missing_number(nums: list[int]) -> int: Example: >>> find_missing_number([0, 1, 3, 4]) 2 + >>> find_missing_number([4, 3, 1, 0]) + 2 + >>> find_missing_number([-4, -3, -1, 0]) + -2 + >>> find_missing_number([-2, 2, 1, 3, 0]) + -1 >>> find_missing_number([1, 3, 4, 5, 6]) 2 >>> find_missing_number([6, 5, 4, 2, 1]) @@ -26,3 +32,9 @@ def find_missing_number(nums: list[int]) -> int: missing_number ^= i ^ nums[i - low] return missing_number + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From c38b222212de921295440b2b1236376136f37136 Mon Sep 17 00:00:00 2001 From: dekomori_sanae09 Date: Mon, 30 Oct 2023 04:37:21 +0530 Subject: [PATCH 518/757] serialize deserialize binary tree (#9625) * added serialize and desrialize bin tree * format files * added type hints * added type hints * Use dataclass .__eq__(), .__iter__(), and .__repr__() --------- Co-authored-by: Christian Clauss --- .../serialize_deserialize_binary_tree.py | 140 ++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 data_structures/binary_tree/serialize_deserialize_binary_tree.py diff --git a/data_structures/binary_tree/serialize_deserialize_binary_tree.py b/data_structures/binary_tree/serialize_deserialize_binary_tree.py new file mode 100644 index 000000000000..7d3e0c61f96d --- /dev/null +++ b/data_structures/binary_tree/serialize_deserialize_binary_tree.py @@ -0,0 +1,140 @@ +from __future__ import annotations + +from collections.abc import Iterator +from dataclasses import dataclass + + +@dataclass +class TreeNode: + """ + A binary tree node has a value, left child, and right child. + + Props: + value: The value of the node. + left: The left child of the node. + right: The right child of the node. + """ + + value: int = 0 + left: TreeNode | None = None + right: TreeNode | None = None + + def __post_init__(self): + if not isinstance(self.value, int): + raise TypeError("Value must be an integer.") + + def __iter__(self) -> Iterator[TreeNode]: + """ + Iterate through the tree in preorder. + + Returns: + An iterator of the tree nodes. + + >>> list(TreeNode(1)) + [1,null,null] + >>> tuple(TreeNode(1, TreeNode(2), TreeNode(3))) + (1,2,null,null,3,null,null, 2,null,null, 3,null,null) + """ + yield self + yield from self.left or () + yield from self.right or () + + def __len__(self) -> int: + """ + Count the number of nodes in the tree. + + Returns: + The number of nodes in the tree. + + >>> len(TreeNode(1)) + 1 + >>> len(TreeNode(1, TreeNode(2), TreeNode(3))) + 3 + """ + return sum(1 for _ in self) + + def __repr__(self) -> str: + """ + Represent the tree as a string. + + Returns: + A string representation of the tree. + + >>> repr(TreeNode(1)) + '1,null,null' + >>> repr(TreeNode(1, TreeNode(2), TreeNode(3))) + '1,2,null,null,3,null,null' + >>> repr(TreeNode(1, TreeNode(2), TreeNode(3, TreeNode(4), TreeNode(5)))) + '1,2,null,null,3,4,null,null,5,null,null' + """ + return f"{self.value},{self.left!r},{self.right!r}".replace("None", "null") + + @classmethod + def five_tree(cls) -> TreeNode: + """ + >>> repr(TreeNode.five_tree()) + '1,2,null,null,3,4,null,null,5,null,null' + """ + root = TreeNode(1) + root.left = TreeNode(2) + root.right = TreeNode(3) + root.right.left = TreeNode(4) + root.right.right = TreeNode(5) + return root + + +def deserialize(data: str) -> TreeNode | None: + """ + Deserialize a string to a binary tree. + + Args: + data(str): The serialized string. + + Returns: + The root of the binary tree. + + >>> root = TreeNode.five_tree() + >>> serialzed_data = repr(root) + >>> deserialized = deserialize(serialzed_data) + >>> root == deserialized + True + >>> root is deserialized # two separate trees + False + >>> root.right.right.value = 6 + >>> root == deserialized + False + >>> serialzed_data = repr(root) + >>> deserialized = deserialize(serialzed_data) + >>> root == deserialized + True + >>> deserialize("") + Traceback (most recent call last): + ... + ValueError: Data cannot be empty. + """ + + if not data: + raise ValueError("Data cannot be empty.") + + # Split the serialized string by a comma to get node values + nodes = data.split(",") + + def build_tree() -> TreeNode | None: + # Get the next value from the list + value = nodes.pop(0) + + if value == "null": + return None + + node = TreeNode(int(value)) + node.left = build_tree() # Recursively build left subtree + node.right = build_tree() # Recursively build right subtree + return node + + return build_tree() + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From ad9948d5d4d65d1457b58d278e780a1b9470a715 Mon Sep 17 00:00:00 2001 From: Mohammad Esfandiyar Date: Mon, 30 Oct 2023 16:50:47 +0330 Subject: [PATCH 519/757] implementation of Gaussian Elimination pivoting as a numerical linear algebra algorithm (#10457) * Adding new implementation Adding my python implementation of Gaussian Elimination pivoting as a numerical linear algebra algorithm * Delete linear_algebra/src/GaussianEliminationpivoting.py * Adding new implementation Adding my python implementation of Gaussian Elimination pivoting as a numerical linear algebra algorithm * Delete linear_algebra/src/gaussianeliminationpivoting.py * Adding new implementation Adding my python implementation of Gaussian Elimination pivoting as a numerical linear algebra algorithm for the third time because the last two times had conflict with the rules in PR * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Delete linear_algebra/src/gaussianeliminationpivoting.py * Adding gaussianeliminationpivoting.py Adding my python implementation of Gaussian Elimination pivoting as a numerical linear algebra algorithm for the fourth time because the last three times had conflict with the rules in PR and bots * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update gaussianeliminationpivoting.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update gaussianeliminationpivoting.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update gaussianeliminationpivoting.py I changed a to matrix and coeff_matrix for better clarity * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update gaussianeliminationpivoting.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update gaussianeliminationpivoting.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update gaussianeliminationpivoting.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update and rename gaussianeliminationpivoting.py to gaussian_elimination_pivoting.py renamed the file * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update gaussian_elimination_pivoting.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update gaussian_elimination_pivoting.py * Update gaussian_elimination_pivoting.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update gaussian_elimination_pivoting.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update gaussian_elimination_pivoting.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update gaussian_elimination_pivoting.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update gaussian_elimination_pivoting.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update gaussian_elimination_pivoting.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update gaussian_elimination_pivoting.py * Update gaussian_elimination_pivoting.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update gaussian_elimination_pivoting.py * Update gaussian_elimination_pivoting.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update gaussian_elimination_pivoting.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update gaussian_elimination_pivoting.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update gaussian_elimination_pivoting.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update gaussian_elimination_pivoting.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update gaussian_elimination_pivoting.py * Update gaussian_elimination_pivoting.py * Update gaussian_elimination_pivoting.py * Update gaussian_elimination_pivoting.py * Delete linear_algebra/src/gaussian_elimination_pivoting.py * Add files via upload * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update gaussian_elimination_pivoting.py * Update gaussian_elimination_pivoting.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Delete linear_algebra/src/gaussian_elimination_pivoting/text.py * Add files via upload * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update linear_algebra/src/gaussian_elimination_pivoting/gaussian_elimination_pivoting.py Co-authored-by: Christian Clauss * Update gaussian_elimination_pivoting.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update gaussian_elimination_pivoting.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update gaussian_elimination_pivoting.py * Update gaussian_elimination_pivoting.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update gaussian_elimination_pivoting.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update gaussian_elimination_pivoting.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update gaussian_elimination_pivoting.py * Update gaussian_elimination_pivoting.py * Update gaussian_elimination_pivoting.py * Update gaussian_elimination_pivoting.py * Update gaussian_elimination_pivoting.py * Update gaussian_elimination_pivoting.py * Update gaussian_elimination_pivoting.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update gaussian_elimination_pivoting.py * Update gaussian_elimination_pivoting.py * Update gaussian_elimination_pivoting.py * Update gaussian_elimination_pivoting.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- .../gaussian_elimination_pivoting.py | 101 ++++++++++++++++++ .../gaussian_elimination_pivoting/matrix.txt | 4 + 2 files changed, 105 insertions(+) create mode 100644 linear_algebra/src/gaussian_elimination_pivoting/gaussian_elimination_pivoting.py create mode 100644 linear_algebra/src/gaussian_elimination_pivoting/matrix.txt diff --git a/linear_algebra/src/gaussian_elimination_pivoting/gaussian_elimination_pivoting.py b/linear_algebra/src/gaussian_elimination_pivoting/gaussian_elimination_pivoting.py new file mode 100644 index 000000000000..2a86350e9fc6 --- /dev/null +++ b/linear_algebra/src/gaussian_elimination_pivoting/gaussian_elimination_pivoting.py @@ -0,0 +1,101 @@ +import numpy as np + +matrix = np.array( + [ + [5.0, -5.0, -3.0, 4.0, -11.0], + [1.0, -4.0, 6.0, -4.0, -10.0], + [-2.0, -5.0, 4.0, -5.0, -12.0], + [-3.0, -3.0, 5.0, -5.0, 8.0], + ], + dtype=float, +) + + +def solve_linear_system(matrix: np.ndarray) -> np.ndarray: + """ + Solve a linear system of equations using Gaussian elimination with partial pivoting + + Args: + - matrix: Coefficient matrix with the last column representing the constants. + + Returns: + - Solution vector. + + Raises: + - ValueError: If the matrix is not correct (i.e., singular). + + https://courses.engr.illinois.edu/cs357/su2013/lect.htm Lecture 7 + + Example: + >>> A = np.array([[2, 1, -1], [-3, -1, 2], [-2, 1, 2]], dtype=float) + >>> B = np.array([8, -11, -3], dtype=float) + >>> solution = solve_linear_system(np.column_stack((A, B))) + >>> np.allclose(solution, np.array([2., 3., -1.])) + True + >>> solve_linear_system(np.array([[0, 0], [0, 0]], dtype=float)) + array([nan, nan]) + """ + ab = np.copy(matrix) + num_of_rows = ab.shape[0] + num_of_columns = ab.shape[1] - 1 + x_lst: list[float] = [] + + # Lead element search + for column_num in range(num_of_rows): + for i in range(column_num, num_of_columns): + if abs(ab[i][column_num]) > abs(ab[column_num][column_num]): + ab[[column_num, i]] = ab[[i, column_num]] + if ab[column_num, column_num] == 0.0: + raise ValueError("Matrix is not correct") + else: + pass + if column_num != 0: + for i in range(column_num, num_of_rows): + ab[i, :] -= ( + ab[i, column_num - 1] + / ab[column_num - 1, column_num - 1] + * ab[column_num - 1, :] + ) + + # Upper triangular matrix + for column_num in range(num_of_rows): + for i in range(column_num, num_of_columns): + if abs(ab[i][column_num]) > abs(ab[column_num][column_num]): + ab[[column_num, i]] = ab[[i, column_num]] + if ab[column_num, column_num] == 0.0: + raise ValueError("Matrix is not correct") + else: + pass + if column_num != 0: + for i in range(column_num, num_of_rows): + ab[i, :] -= ( + ab[i, column_num - 1] + / ab[column_num - 1, column_num - 1] + * ab[column_num - 1, :] + ) + + # Find x vector (Back Substitution) + for column_num in range(num_of_rows - 1, -1, -1): + x = ab[column_num, -1] / ab[column_num, column_num] + x_lst.insert(0, x) + for i in range(column_num - 1, -1, -1): + ab[i, -1] -= ab[i, column_num] * x + + # Return the solution vector + return np.asarray(x_lst) + + +if __name__ == "__main__": + from doctest import testmod + from pathlib import Path + + testmod() + file_path = Path(__file__).parent / "matrix.txt" + try: + matrix = np.loadtxt(file_path) + except FileNotFoundError: + print(f"Error: {file_path} not found. Using default matrix instead.") + + # Example usage: + print(f"Matrix:\n{matrix}") + print(f"{solve_linear_system(matrix) = }") diff --git a/linear_algebra/src/gaussian_elimination_pivoting/matrix.txt b/linear_algebra/src/gaussian_elimination_pivoting/matrix.txt new file mode 100644 index 000000000000..dd895ad856ee --- /dev/null +++ b/linear_algebra/src/gaussian_elimination_pivoting/matrix.txt @@ -0,0 +1,4 @@ +5.0 -5.0 -3.0 4.0 -11.0 +1.0 -4.0 6.0 -4.0 -10.0 +-2.0 -5.0 4.0 -5.0 -12.0 +-3.0 -3.0 5.0 -5.0 8.0 \ No newline at end of file From ddd4023fe66cd4a0605d4f7de5ae85680ac94167 Mon Sep 17 00:00:00 2001 From: Devashri Deulkar <95555641+Devadeut@users.noreply.github.com> Date: Mon, 30 Oct 2023 23:45:49 +0530 Subject: [PATCH 520/757] Happy number (new algorithm) (#10864) * Happy number (new algorithm) adding new algorithm * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update maths/special_numbers/happy_number.py Co-authored-by: Christian Clauss * Update happy_number.py added new changes * Update happy_number.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update happy_number.py * Update happy_number.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update happy_number.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update happy_number.py added ValueError part in code * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update happy_number.py modified and added raise Error code * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update happy_number.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- maths/special_numbers/happy_number.py | 48 +++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 maths/special_numbers/happy_number.py diff --git a/maths/special_numbers/happy_number.py b/maths/special_numbers/happy_number.py new file mode 100644 index 000000000000..eac3167e304b --- /dev/null +++ b/maths/special_numbers/happy_number.py @@ -0,0 +1,48 @@ +def is_happy_number(number: int) -> bool: + """ + A happy number is a number which eventually reaches 1 when replaced by the sum of + the square of each digit. + + :param number: The number to check for happiness. + :return: True if the number is a happy number, False otherwise. + + >>> is_happy_number(19) + True + >>> is_happy_number(2) + False + >>> is_happy_number(23) + True + >>> is_happy_number(1) + True + >>> is_happy_number(0) + Traceback (most recent call last): + ... + ValueError: number=0 must be a positive integer + >>> is_happy_number(-19) + Traceback (most recent call last): + ... + ValueError: number=-19 must be a positive integer + >>> is_happy_number(19.1) + Traceback (most recent call last): + ... + ValueError: number=19.1 must be a positive integer + >>> is_happy_number("happy") + Traceback (most recent call last): + ... + ValueError: number='happy' must be a positive integer + """ + if not isinstance(number, int) or number <= 0: + msg = f"{number=} must be a positive integer" + raise ValueError(msg) + + seen = set() + while number != 1 and number not in seen: + seen.add(number) + number = sum(int(digit) ** 2 for digit in str(number)) + return number == 1 + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 79a327fc07388a093e132d9df94723f24c162315 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 30 Oct 2023 19:17:00 +0100 Subject: [PATCH 521/757] [pre-commit.ci] pre-commit autoupdate (#11106) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/astral-sh/ruff-pre-commit: v0.1.1 → v0.1.3](https://github.com/astral-sh/ruff-pre-commit/compare/v0.1.1...v0.1.3) - [github.com/psf/black: 23.10.0 → 23.10.1](https://github.com/psf/black/compare/23.10.0...23.10.1) - [github.com/tox-dev/pyproject-fmt: 1.2.0 → 1.3.0](https://github.com/tox-dev/pyproject-fmt/compare/1.2.0...1.3.0) * updating DIRECTORY.md --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- .pre-commit-config.yaml | 6 +++--- DIRECTORY.md | 17 +++++++++++++++++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e0b9922fae7e..784993e6b00c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,12 +16,12 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.1.1 + rev: v0.1.3 hooks: - id: ruff - repo: https://github.com/psf/black - rev: 23.10.0 + rev: 23.10.1 hooks: - id: black @@ -33,7 +33,7 @@ repos: - tomli - repo: https://github.com/tox-dev/pyproject-fmt - rev: "1.2.0" + rev: "1.3.0" hooks: - id: pyproject-fmt diff --git a/DIRECTORY.md b/DIRECTORY.md index 9b2c8ce735c3..ee4a521f708b 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -10,6 +10,8 @@ * [All Subsequences](backtracking/all_subsequences.py) * [Coloring](backtracking/coloring.py) * [Combination Sum](backtracking/combination_sum.py) + * [Crossword Puzzle Solver](backtracking/crossword_puzzle_solver.py) + * [Generate Parentheses](backtracking/generate_parentheses.py) * [Hamiltonian Cycle](backtracking/hamiltonian_cycle.py) * [Knight Tour](backtracking/knight_tour.py) * [Match Word Pattern](backtracking/match_word_pattern.py) @@ -35,6 +37,7 @@ * [Count 1S Brian Kernighan Method](bit_manipulation/count_1s_brian_kernighan_method.py) * [Count Number Of One Bits](bit_manipulation/count_number_of_one_bits.py) * [Excess 3 Code](bit_manipulation/excess_3_code.py) + * [Find Previous Power Of Two](bit_manipulation/find_previous_power_of_two.py) * [Gray Code Sequence](bit_manipulation/gray_code_sequence.py) * [Highest Set Bit](bit_manipulation/highest_set_bit.py) * [Index Of Rightmost Set Bit](bit_manipulation/index_of_rightmost_set_bit.py) @@ -54,6 +57,8 @@ ## Boolean Algebra * [And Gate](boolean_algebra/and_gate.py) * [Imply Gate](boolean_algebra/imply_gate.py) + * [Karnaugh Map Simplification](boolean_algebra/karnaugh_map_simplification.py) + * [Multiplexer](boolean_algebra/multiplexer.py) * [Nand Gate](boolean_algebra/nand_gate.py) * [Nimply Gate](boolean_algebra/nimply_gate.py) * [Nor Gate](boolean_algebra/nor_gate.py) @@ -108,6 +113,7 @@ * [Rsa Cipher](ciphers/rsa_cipher.py) * [Rsa Factorization](ciphers/rsa_factorization.py) * [Rsa Key Generator](ciphers/rsa_key_generator.py) + * [Running Key Cipher](ciphers/running_key_cipher.py) * [Shuffled Shift Cipher](ciphers/shuffled_shift_cipher.py) * [Simple Keyword Cypher](ciphers/simple_keyword_cypher.py) * [Simple Substitution Cipher](ciphers/simple_substitution_cipher.py) @@ -150,6 +156,7 @@ * [Excel Title To Column](conversions/excel_title_to_column.py) * [Hex To Bin](conversions/hex_to_bin.py) * [Hexadecimal To Decimal](conversions/hexadecimal_to_decimal.py) + * [Ipv4 Conversion](conversions/ipv4_conversion.py) * [Length Conversion](conversions/length_conversion.py) * [Molecular Chemistry](conversions/molecular_chemistry.py) * [Octal To Binary](conversions/octal_to_binary.py) @@ -209,6 +216,7 @@ * [Red Black Tree](data_structures/binary_tree/red_black_tree.py) * [Segment Tree](data_structures/binary_tree/segment_tree.py) * [Segment Tree Other](data_structures/binary_tree/segment_tree_other.py) + * [Serialize Deserialize Binary Tree](data_structures/binary_tree/serialize_deserialize_binary_tree.py) * [Symmetric Tree](data_structures/binary_tree/symmetric_tree.py) * [Treap](data_structures/binary_tree/treap.py) * [Wavelet Tree](data_structures/binary_tree/wavelet_tree.py) @@ -410,6 +418,9 @@ * [Mandelbrot](fractals/mandelbrot.py) * [Sierpinski Triangle](fractals/sierpinski_triangle.py) +## Fuzzy Logic + * [Fuzzy Operations](fuzzy_logic/fuzzy_operations.py) + ## Genetic Algorithm * [Basic String](genetic_algorithm/basic_string.py) @@ -521,6 +532,8 @@ * [Lu Decomposition](linear_algebra/lu_decomposition.py) * Src * [Conjugate Gradient](linear_algebra/src/conjugate_gradient.py) + * Gaussian Elimination Pivoting + * [Gaussian Elimination Pivoting](linear_algebra/src/gaussian_elimination_pivoting/gaussian_elimination_pivoting.py) * [Lib](linear_algebra/src/lib.py) * [Polynom For Points](linear_algebra/src/polynom_for_points.py) * [Power Iteration](linear_algebra/src/power_iteration.py) @@ -618,12 +631,14 @@ * [Germain Primes](maths/germain_primes.py) * [Greatest Common Divisor](maths/greatest_common_divisor.py) * [Hardy Ramanujanalgo](maths/hardy_ramanujanalgo.py) + * [Integer Square Root](maths/integer_square_root.py) * [Interquartile Range](maths/interquartile_range.py) * [Is Int Palindrome](maths/is_int_palindrome.py) * [Is Ip V4 Address Valid](maths/is_ip_v4_address_valid.py) * [Is Square Free](maths/is_square_free.py) * [Jaccard Similarity](maths/jaccard_similarity.py) * [Joint Probability Distribution](maths/joint_probability_distribution.py) + * [Josephus Problem](maths/josephus_problem.py) * [Juggler Sequence](maths/juggler_sequence.py) * [Karatsuba](maths/karatsuba.py) * [Kth Lexicographic Permutation](maths/kth_lexicographic_permutation.py) @@ -646,6 +661,7 @@ * [Monte Carlo Dice](maths/monte_carlo_dice.py) * [Number Of Digits](maths/number_of_digits.py) * Numerical Analysis + * [Adams Bashforth](maths/numerical_analysis/adams_bashforth.py) * [Bisection](maths/numerical_analysis/bisection.py) * [Bisection 2](maths/numerical_analysis/bisection_2.py) * [Integration By Simpson Approx](maths/numerical_analysis/integration_by_simpson_approx.py) @@ -1223,6 +1239,7 @@ * [Anagrams](strings/anagrams.py) * [Autocomplete Using Trie](strings/autocomplete_using_trie.py) * [Barcode Validator](strings/barcode_validator.py) + * [Bitap String Match](strings/bitap_string_match.py) * [Boyer Moore Search](strings/boyer_moore_search.py) * [Camel Case To Snake Case](strings/camel_case_to_snake_case.py) * [Can String Be Rearranged As Palindrome](strings/can_string_be_rearranged_as_palindrome.py) From b072ba657f045a899ad133006d54ce5c9035c7f4 Mon Sep 17 00:00:00 2001 From: Akshar Goyal Date: Mon, 30 Oct 2023 20:00:48 -0400 Subject: [PATCH 522/757] Added tests for validate_sudoku_board.py (#11108) --- matrix/validate_sudoku_board.py | 60 +++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/matrix/validate_sudoku_board.py b/matrix/validate_sudoku_board.py index 0ee7b3df0b83..a7e08d169059 100644 --- a/matrix/validate_sudoku_board.py +++ b/matrix/validate_sudoku_board.py @@ -54,6 +54,66 @@ def is_valid_sudoku_board(sudoku_board: list[list[str]]) -> bool: ... ,[".",".",".",".","8",".",".","7","9"] ... ]) False + >>> is_valid_sudoku_board([ + ... ["1","2","3","4","5","6","7","8","9"] + ... ,["4","5","6","7","8","9","1","2","3"] + ... ,["7","8","9","1","2","3","4","5","6"] + ... ,[".",".",".",".",".",".",".",".","."] + ... ,[".",".",".",".",".",".",".",".","."] + ... ,[".",".",".",".",".",".",".",".","."] + ... ,[".",".",".",".",".",".",".",".","."] + ... ,[".",".",".",".",".",".",".",".","."] + ... ,[".",".",".",".",".",".",".",".","."] + ... ]) + True + >>> is_valid_sudoku_board([ + ... ["1","2","3",".",".",".",".",".","."] + ... ,["4","5","6",".",".",".",".",".","."] + ... ,["7","8","9",".",".",".",".",".","."] + ... ,[".",".",".","4","5","6",".",".","."] + ... ,[".",".",".","7","8","9",".",".","."] + ... ,[".",".",".","1","2","3",".",".","."] + ... ,[".",".",".",".",".",".","7","8","9"] + ... ,[".",".",".",".",".",".","1","2","3"] + ... ,[".",".",".",".",".",".","4","5","6"] + ... ]) + True + >>> is_valid_sudoku_board([ + ... ["1","2","3",".",".",".","5","6","4"] + ... ,["4","5","6",".",".",".","8","9","7"] + ... ,["7","8","9",".",".",".","2","3","1"] + ... ,[".",".",".","4","5","6",".",".","."] + ... ,[".",".",".","7","8","9",".",".","."] + ... ,[".",".",".","1","2","3",".",".","."] + ... ,["3","1","2",".",".",".","7","8","9"] + ... ,["6","4","5",".",".",".","1","2","3"] + ... ,["9","7","8",".",".",".","4","5","6"] + ... ]) + True + >>> is_valid_sudoku_board([ + ... ["1","2","3","4","5","6","7","8","9"] + ... ,["2",".",".",".",".",".",".",".","8"] + ... ,["3",".",".",".",".",".",".",".","7"] + ... ,["4",".",".",".",".",".",".",".","6"] + ... ,["5",".",".",".",".",".",".",".","5"] + ... ,["6",".",".",".",".",".",".",".","4"] + ... ,["7",".",".",".",".",".",".",".","3"] + ... ,["8",".",".",".",".",".",".",".","2"] + ... ,["9","8","7","6","5","4","3","2","1"] + ... ]) + False + >>> is_valid_sudoku_board([ + ... ["1","2","3","8","9","7","5","6","4"] + ... ,["4","5","6","2","3","1","8","9","7"] + ... ,["7","8","9","5","6","4","2","3","1"] + ... ,["2","3","1","4","5","6","9","7","8"] + ... ,["5","6","4","7","8","9","3","1","2"] + ... ,["8","9","7","1","2","3","6","4","5"] + ... ,["3","1","2","6","4","5","7","8","9"] + ... ,["6","4","5","9","7","8","1","2","3"] + ... ,["9","7","8","3","1","2","4","5","6"] + ... ]) + True >>> is_valid_sudoku_board([["1", "2", "3", "4", "5", "6", "7", "8", "9"]]) Traceback (most recent call last): ... From 99f3a0e4c9b1a6d9ff5bba2adf65d90d55f2250a Mon Sep 17 00:00:00 2001 From: Arya Hariharan <84255987+Arya-Hari@users.noreply.github.com> Date: Tue, 31 Oct 2023 12:23:38 +0530 Subject: [PATCH 523/757] adding-docstrings (#11114) * adding-docstrings * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update intro_sort.py * Update intro_sort.py * Remove blank lines --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- sorts/intro_sort.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/sorts/intro_sort.py b/sorts/intro_sort.py index 908d2886533a..5a5741dc8375 100644 --- a/sorts/intro_sort.py +++ b/sorts/intro_sort.py @@ -11,6 +11,18 @@ def insertion_sort(array: list, start: int = 0, end: int = 0) -> list: >>> array = [4, 2, 6, 8, 1, 7, 8, 22, 14, 56, 27, 79, 23, 45, 14, 12] >>> insertion_sort(array, 0, len(array)) [1, 2, 4, 6, 7, 8, 8, 12, 14, 14, 22, 23, 27, 45, 56, 79] + >>> array = [21, 15, 11, 45, -2, -11, 46] + >>> insertion_sort(array, 0, len(array)) + [-11, -2, 11, 15, 21, 45, 46] + >>> array = [-2, 0, 89, 11, 48, 79, 12] + >>> insertion_sort(array, 0, len(array)) + [-2, 0, 11, 12, 48, 79, 89] + >>> array = ['a', 'z', 'd', 'p', 'v', 'l', 'o', 'o'] + >>> insertion_sort(array, 0, len(array)) + ['a', 'd', 'l', 'o', 'o', 'p', 'v', 'z'] + >>> array = [73.568, 73.56, -45.03, 1.7, 0, 89.45] + >>> insertion_sort(array, 0, len(array)) + [-45.03, 0, 1.7, 73.56, 73.568, 89.45] """ end = end or len(array) for i in range(start, end): @@ -47,6 +59,12 @@ def heap_sort(array: list) -> list: """ >>> heap_sort([4, 2, 6, 8, 1, 7, 8, 22, 14, 56, 27, 79, 23, 45, 14, 12]) [1, 2, 4, 6, 7, 8, 8, 12, 14, 14, 22, 23, 27, 45, 56, 79] + >>> heap_sort([-2, -11, 0, 0, 0, 87, 45, -69, 78, 12, 10, 103, 89, 52]) + [-69, -11, -2, 0, 0, 0, 10, 12, 45, 52, 78, 87, 89, 103] + >>> heap_sort(['b', 'd', 'e', 'f', 'g', 'p', 'x', 'z', 'b', 's', 'e', 'u', 'v']) + ['b', 'b', 'd', 'e', 'e', 'f', 'g', 'p', 's', 'u', 'v', 'x', 'z'] + >>> heap_sort([6.2, -45.54, 8465.20, 758.56, -457.0, 0, 1, 2.879, 1.7, 11.7]) + [-457.0, -45.54, 0, 1, 1.7, 2.879, 6.2, 11.7, 758.56, 8465.2] """ n = len(array) @@ -91,6 +109,15 @@ def partition(array: list, low: int, high: int, pivot: int) -> int: >>> array = [4, 2, 6, 8, 1, 7, 8, 22, 14, 56, 27, 79, 23, 45, 14, 12] >>> partition(array, 0, len(array), 12) 8 + >>> array = [21, 15, 11, 45, -2, -11, 46] + >>> partition(array, 0, len(array), 15) + 3 + >>> array = ['a', 'z', 'd', 'p', 'v', 'l', 'o', 'o'] + >>> partition(array, 0, len(array), 'p') + 5 + >>> array = [6.2, -45.54, 8465.20, 758.56, -457.0, 0, 1, 2.879, 1.7, 11.7] + >>> partition(array, 0, len(array), 2.879) + 6 """ i = low j = high From ebfdb127e76e76c122d3110155abf644474b9fa9 Mon Sep 17 00:00:00 2001 From: Suyash Dongre <109069262+Suyashd999@users.noreply.github.com> Date: Sat, 4 Nov 2023 17:34:57 +0530 Subject: [PATCH 524/757] Added doctest to hash_map.py (#11105) * Added doctest to heap.py * Added doctest to hash_map.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update hash_map.py * Added doctest to hash_map.py * Added doctest to hash_map.py * Added doctest to detecting_english_programmatically.py * Update detecting_english_programmatically.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- data_structures/hashing/hash_map.py | 33 +++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/data_structures/hashing/hash_map.py b/data_structures/hashing/hash_map.py index 1689e07afd9f..6a6f8e54d5e9 100644 --- a/data_structures/hashing/hash_map.py +++ b/data_structures/hashing/hash_map.py @@ -242,6 +242,25 @@ def __delitem__(self, key: KEY) -> None: self._size_down() def __getitem__(self, key: KEY) -> VAL: + """ + Returns the item at the given key + + >>> hm = HashMap(5) + >>> hm._add_item(1, 10) + >>> hm.__getitem__(1) + 10 + + >>> hm = HashMap(5) + >>> hm._add_item(10, -10) + >>> hm._add_item(20, -20) + >>> hm.__getitem__(20) + -20 + + >>> hm = HashMap(5) + >>> hm._add_item(-1, 10) + >>> hm.__getitem__(-1) + 10 + """ for ind in self._iterate_buckets(key): item = self._buckets[ind] if item is None: @@ -253,6 +272,20 @@ def __getitem__(self, key: KEY) -> VAL: raise KeyError(key) def __len__(self) -> int: + """ + Returns the number of items present in hashmap + + >>> hm = HashMap(5) + >>> hm._add_item(1, 10) + >>> hm._add_item(2, 20) + >>> hm._add_item(3, 30) + >>> hm.__len__() + 3 + + >>> hm = HashMap(5) + >>> hm.__len__() + 0 + """ return self._len def __iter__(self) -> Iterator[KEY]: From 257cfbdf6e2a55d48727f533ef15295065e0057b Mon Sep 17 00:00:00 2001 From: Suyash Dongre <109069262+Suyashd999@users.noreply.github.com> Date: Sun, 5 Nov 2023 13:46:00 +0530 Subject: [PATCH 525/757] Added doctest to decision_tree.py (#11143) * Added doctest to decision_tree.py * Update decision_tree.py * Update machine_learning/decision_tree.py * Update machine_learning/decision_tree.py * raise ValueError() * Update decision_tree.py --------- Co-authored-by: Christian Clauss --- machine_learning/decision_tree.py | 49 ++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 14 deletions(-) diff --git a/machine_learning/decision_tree.py b/machine_learning/decision_tree.py index 7cd1b02c4181..c67e09c7f114 100644 --- a/machine_learning/decision_tree.py +++ b/machine_learning/decision_tree.py @@ -18,7 +18,7 @@ def __init__(self, depth=5, min_leaf_size=5): def mean_squared_error(self, labels, prediction): """ mean_squared_error: - @param labels: a one dimensional numpy array + @param labels: a one-dimensional numpy array @param prediction: a floating point value return value: mean_squared_error calculates the error if prediction is used to estimate the labels @@ -44,26 +44,47 @@ def mean_squared_error(self, labels, prediction): def train(self, x, y): """ train: - @param x: a one dimensional numpy array - @param y: a one dimensional numpy array. + @param x: a one-dimensional numpy array + @param y: a one-dimensional numpy array. The contents of y are the labels for the corresponding X values - train does not have a return value - """ - - """ - this section is to check that the inputs conform to our dimensionality + train() does not have a return value + + Examples: + 1. Try to train when x & y are of same length & 1 dimensions (No errors) + >>> dt = DecisionTree() + >>> dt.train(np.array([10,20,30,40,50]),np.array([0,0,0,1,1])) + + 2. Try to train when x is 2 dimensions + >>> dt = DecisionTree() + >>> dt.train(np.array([[1,2,3,4,5],[1,2,3,4,5]]),np.array([0,0,0,1,1])) + Traceback (most recent call last): + ... + ValueError: Input data set must be one-dimensional + + 3. Try to train when x and y are not of the same length + >>> dt = DecisionTree() + >>> dt.train(np.array([1,2,3,4,5]),np.array([[0,0,0,1,1],[0,0,0,1,1]])) + Traceback (most recent call last): + ... + ValueError: x and y have different lengths + + 4. Try to train when x & y are of the same length but different dimensions + >>> dt = DecisionTree() + >>> dt.train(np.array([1,2,3,4,5]),np.array([[1],[2],[3],[4],[5]])) + Traceback (most recent call last): + ... + ValueError: Data set labels must be one-dimensional + + This section is to check that the inputs conform to our dimensionality constraints """ if x.ndim != 1: - print("Error: Input data set must be one dimensional") - return + raise ValueError("Input data set must be one-dimensional") if len(x) != len(y): - print("Error: X and y have different lengths") - return + raise ValueError("x and y have different lengths") if y.ndim != 1: - print("Error: Data set labels must be one dimensional") - return + raise ValueError("Data set labels must be one-dimensional") if len(x) < 2 * self.min_leaf_size: self.prediction = np.mean(y) From 1e50cf366022a5c44abfa5adf5e01bef62524cc3 Mon Sep 17 00:00:00 2001 From: Suyash Dongre <109069262+Suyashd999@users.noreply.github.com> Date: Sun, 5 Nov 2023 14:08:39 +0530 Subject: [PATCH 526/757] Added doctest to binary_search_tree.py (#11141) * Added doctest to binary_search_tree.py * Update binary_search_tree.py * Update binary_search_tree.py --------- Co-authored-by: Christian Clauss --- .../binary_tree/binary_search_tree.py | 32 +++++++++++++++---- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/data_structures/binary_tree/binary_search_tree.py b/data_structures/binary_tree/binary_search_tree.py index 38691c4755c9..f08f278a8e47 100644 --- a/data_structures/binary_tree/binary_search_tree.py +++ b/data_structures/binary_tree/binary_search_tree.py @@ -10,8 +10,7 @@ / \ / 4 7 13 ->>> t = BinarySearchTree() ->>> t.insert(8, 3, 6, 1, 10, 14, 13, 4, 7) +>>> t = BinarySearchTree().insert(8, 3, 6, 1, 10, 14, 13, 4, 7) >>> print(" ".join(repr(i.value) for i in t.traversal_tree())) 8 3 1 6 4 7 10 14 13 @@ -40,7 +39,16 @@ >>> testlist = (8, 3, 6, 1, 10, 14, 13, 4, 7) >>> t = BinarySearchTree() >>> for i in testlist: -... t.insert(i) +... t.insert(i) # doctest: +ELLIPSIS +BinarySearchTree(root=8) +BinarySearchTree(root={'8': (3, None)}) +BinarySearchTree(root={'8': ({'3': (None, 6)}, None)}) +BinarySearchTree(root={'8': ({'3': (1, 6)}, None)}) +BinarySearchTree(root={'8': ({'3': (1, 6)}, 10)}) +BinarySearchTree(root={'8': ({'3': (1, 6)}, {'10': (None, 14)})}) +BinarySearchTree(root={'8': ({'3': (1, 6)}, {'10': (None, {'14': (13, None)})})}) +BinarySearchTree(root={'8': ({'3': (1, {'6': (4, None)})}, {'10': (None, {'14': ... +BinarySearchTree(root={'8': ({'3': (1, {'6': (4, 7)})}, {'10': (None, {'14': (13, ... Prints all the elements of the list in order traversal >>> print(t) @@ -84,7 +92,7 @@ from collections.abc import Iterable, Iterator from dataclasses import dataclass -from typing import Any +from typing import Any, Self @dataclass @@ -145,7 +153,18 @@ def __reassign_nodes(self, node: Node, new_children: Node | None) -> None: self.root = new_children def empty(self) -> bool: - return self.root is None + """ + Returns True if the tree does not have any element(s). + False if the tree has element(s). + + >>> BinarySearchTree().empty() + True + >>> BinarySearchTree().insert(1).empty() + False + >>> BinarySearchTree().insert(8, 3, 6, 1, 10, 14, 13, 4, 7).empty() + False + """ + return not self.root def __insert(self, value) -> None: """ @@ -173,9 +192,10 @@ def __insert(self, value) -> None: parent_node = parent_node.right new_node.parent = parent_node - def insert(self, *values) -> None: + def insert(self, *values) -> Self: for value in values: self.__insert(value) + return self def search(self, value) -> Node | None: if self.empty(): From e48ea7d39643f3c15f830ccf63a363378858a001 Mon Sep 17 00:00:00 2001 From: SEIKH NABAB UDDIN <93948993+nababuddin@users.noreply.github.com> Date: Sun, 5 Nov 2023 14:13:52 +0530 Subject: [PATCH 527/757] Create get_ip_geolocation.py (#10902) * Create get_ip_geolocation.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update get_ip_geolocation.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update get_ip_geolocation.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- web_programming/get_ip_geolocation.py | 40 +++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 web_programming/get_ip_geolocation.py diff --git a/web_programming/get_ip_geolocation.py b/web_programming/get_ip_geolocation.py new file mode 100644 index 000000000000..62eaeafceb7e --- /dev/null +++ b/web_programming/get_ip_geolocation.py @@ -0,0 +1,40 @@ +import requests + + +# Function to get geolocation data for an IP address +def get_ip_geolocation(ip_address: str) -> str: + try: + # Construct the URL for the IP geolocation API + url = f"https://ipinfo.io/{ip_address}/json" + + # Send a GET request to the API + response = requests.get(url) + + # Check if the HTTP request was successful + response.raise_for_status() + + # Parse the response as JSON + data = response.json() + + # Check if city, region, and country information is available + if "city" in data and "region" in data and "country" in data: + location = f"Location: {data['city']}, {data['region']}, {data['country']}" + else: + location = "Location data not found." + + return location + except requests.exceptions.RequestException as e: + # Handle network-related exceptions + return f"Request error: {e}" + except ValueError as e: + # Handle JSON parsing errors + return f"JSON parsing error: {e}" + + +if __name__ == "__main__": + # Prompt the user to enter an IP address + ip_address = input("Enter an IP address: ") + + # Get the geolocation data and print it + location = get_ip_geolocation(ip_address) + print(location) From eb989c08cdbf82e1a4db6481371f3e9ccb3bcf99 Mon Sep 17 00:00:00 2001 From: Sunny Kumar <37464973+Skyad@users.noreply.github.com> Date: Mon, 6 Nov 2023 17:40:50 +0530 Subject: [PATCH 528/757] Data structures/arrays/triplet sum (#11134) * updated code for find triplets with 0 sum Signed-off-by: Skyad <777.sunnykumar@gmail.com> * extra line added at the end of file Signed-off-by: Sunny Kumar * extra line added at the end of file Signed-off-by: Skyad <777.sunnykumar@gmail.com> * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * file updated with comments Signed-off-by: Skyad <777.sunnykumar@gmail.com> * updated the comments as suggested by community Signed-off-by: Sunny Kumar * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * file updated according to community comments Signed-off-by: Skyad <777.sunnykumar@gmail.com> * Update find_triplets_with_0_sum.py --------- Signed-off-by: Skyad <777.sunnykumar@gmail.com> Signed-off-by: Sunny Kumar Co-authored-by: Sunny Kumar Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- .../arrays/find_triplets_with_0_sum.py | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/data_structures/arrays/find_triplets_with_0_sum.py b/data_structures/arrays/find_triplets_with_0_sum.py index 8217ff857e3d..52e521906873 100644 --- a/data_structures/arrays/find_triplets_with_0_sum.py +++ b/data_structures/arrays/find_triplets_with_0_sum.py @@ -22,3 +22,66 @@ def find_triplets_with_0_sum(nums: list[int]) -> list[list[int]]: list(x) for x in sorted({abc for abc in combinations(sorted(nums), 3) if not sum(abc)}) ] + + +def find_triplets_with_0_sum_hashing(arr: list[int]) -> list[list[int]]: + """ + Function for finding the triplets with a given sum in the array using hashing. + + Given a list of integers, return elements a, b, c such that a + b + c = 0. + + Args: + nums: list of integers + Returns: + list of lists of integers where sum(each_list) == 0 + Examples: + >>> find_triplets_with_0_sum_hashing([-1, 0, 1, 2, -1, -4]) + [[-1, 0, 1], [-1, -1, 2]] + >>> find_triplets_with_0_sum_hashing([]) + [] + >>> find_triplets_with_0_sum_hashing([0, 0, 0]) + [[0, 0, 0]] + >>> find_triplets_with_0_sum_hashing([1, 2, 3, 0, -1, -2, -3]) + [[-1, 0, 1], [-3, 1, 2], [-2, 0, 2], [-2, -1, 3], [-3, 0, 3]] + + Time complexity: O(N^2) + Auxiliary Space: O(N) + + """ + target_sum = 0 + + # Initialize the final output array with blank. + output_arr = [] + + # Set the initial element as arr[i]. + for index, item in enumerate(arr[:-2]): + # to store second elements that can complement the final sum. + set_initialize = set() + + # current sum needed for reaching the target sum + current_sum = target_sum - item + + # Traverse the subarray arr[i+1:]. + for other_item in arr[index + 1 :]: + # required value for the second element + required_value = current_sum - other_item + + # Verify if the desired value exists in the set. + if required_value in set_initialize: + # finding triplet elements combination. + combination_array = sorted([item, other_item, required_value]) + if combination_array not in output_arr: + output_arr.append(combination_array) + + # Include the current element in the set + # for subsequent complement verification. + set_initialize.add(other_item) + + # Return all the triplet combinations. + return output_arr + + +if __name__ == "__main__": + from doctest import testmod + + testmod() From fa508d7b8bf9696805e97deac71e657256500ab7 Mon Sep 17 00:00:00 2001 From: Suyash Dongre <109069262+Suyashd999@users.noreply.github.com> Date: Mon, 6 Nov 2023 17:44:39 +0530 Subject: [PATCH 529/757] Added doctest to detecting_english_programmatically.py (#11135) --- strings/detecting_english_programmatically.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/strings/detecting_english_programmatically.py b/strings/detecting_english_programmatically.py index b9000101beb4..e30e2ea8dd8b 100644 --- a/strings/detecting_english_programmatically.py +++ b/strings/detecting_english_programmatically.py @@ -25,6 +25,18 @@ def get_english_count(message: str) -> float: def remove_non_letters(message: str) -> str: + """ + >>> remove_non_letters("Hi! how are you?") + 'Hi how are you' + >>> remove_non_letters("P^y%t)h@o*n") + 'Python' + >>> remove_non_letters("1+1=2") + '' + >>> remove_non_letters("www.google.com/") + 'wwwgooglecom' + >>> remove_non_letters("") + '' + """ return "".join(symbol for symbol in message if symbol in LETTERS_AND_SPACE) From 12e401650c8afd4b6cf69ddab09a882d1eb6ff5c Mon Sep 17 00:00:00 2001 From: Suyash Dongre <109069262+Suyashd999@users.noreply.github.com> Date: Mon, 6 Nov 2023 17:48:41 +0530 Subject: [PATCH 530/757] Added doctest to string_switch_case.py (#11136) * Added doctest to string_switch_case.py * Update string_switch_case.py --- strings/string_switch_case.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/strings/string_switch_case.py b/strings/string_switch_case.py index 9a07472dfd71..c16d9fa552f9 100644 --- a/strings/string_switch_case.py +++ b/strings/string_switch_case.py @@ -28,6 +28,12 @@ def to_simple_case(str_: str) -> str: """ >>> to_simple_case("one two 31235three4four") 'OneTwo31235three4four' + >>> to_simple_case("This should be combined") + 'ThisShouldBeCombined' + >>> to_simple_case("The first letters are capitalized, then string is merged") + 'TheFirstLettersAreCapitalizedThenStringIsMerged' + >>> to_simple_case("special characters :, ', %, ^, $, are ignored") + 'SpecialCharactersAreIgnored' """ string_split = split_input(str_) return "".join( @@ -37,6 +43,14 @@ def to_simple_case(str_: str) -> str: def to_complex_case(text: str, upper: bool, separator: str) -> str: """ + Returns the string concatenated with the delimiter we provide. + + Parameters: + @text: The string on which we want to perform operation + @upper: Boolean value to determine whether we want capitalized result or not + @separator: The delimiter with which we want to concatenate words + + Examples: >>> to_complex_case("one two 31235three4four", True, "_") 'ONE_TWO_31235THREE4FOUR' >>> to_complex_case("one two 31235three4four", False, "-") From a13e9c21374caf40652ee75cc3620f3ac0c72ff3 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 7 Nov 2023 06:49:09 +0600 Subject: [PATCH 531/757] [pre-commit.ci] pre-commit autoupdate (#11146) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/astral-sh/ruff-pre-commit: v0.1.3 → v0.1.4](https://github.com/astral-sh/ruff-pre-commit/compare/v0.1.3...v0.1.4) - [github.com/tox-dev/pyproject-fmt: 1.3.0 → 1.4.1](https://github.com/tox-dev/pyproject-fmt/compare/1.3.0...1.4.1) * updating DIRECTORY.md * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- .pre-commit-config.yaml | 4 ++-- DIRECTORY.md | 2 ++ pyproject.toml | 8 ++++---- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 784993e6b00c..1bb3de782275 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.1.3 + rev: v0.1.4 hooks: - id: ruff @@ -33,7 +33,7 @@ repos: - tomli - repo: https://github.com/tox-dev/pyproject-fmt - rev: "1.3.0" + rev: "1.4.1" hooks: - id: pyproject-fmt diff --git a/DIRECTORY.md b/DIRECTORY.md index ee4a521f708b..cb4b00b045b5 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -725,6 +725,7 @@ * [Carmichael Number](maths/special_numbers/carmichael_number.py) * [Catalan Number](maths/special_numbers/catalan_number.py) * [Hamming Numbers](maths/special_numbers/hamming_numbers.py) + * [Happy Number](maths/special_numbers/happy_number.py) * [Harshad Numbers](maths/special_numbers/harshad_numbers.py) * [Hexagonal Number](maths/special_numbers/hexagonal_number.py) * [Krishnamurthy Number](maths/special_numbers/krishnamurthy_number.py) @@ -1310,6 +1311,7 @@ * [Get Amazon Product Data](web_programming/get_amazon_product_data.py) * [Get Imdb Top 250 Movies Csv](web_programming/get_imdb_top_250_movies_csv.py) * [Get Imdbtop](web_programming/get_imdbtop.py) + * [Get Ip Geolocation](web_programming/get_ip_geolocation.py) * [Get Top Billionaires](web_programming/get_top_billionaires.py) * [Get Top Hn Posts](web_programming/get_top_hn_posts.py) * [Get User Tweets](web_programming/get_user_tweets.py) diff --git a/pyproject.toml b/pyproject.toml index 5d27142d16e2..c7163dc78371 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -117,6 +117,10 @@ max-branches = 20 # default: 12 max-returns = 8 # default: 6 max-statements = 88 # default: 50 +[tool.codespell] +ignore-words-list = "3rt,ans,bitap,crate,damon,fo,followings,hist,iff,kwanza,manuel,mater,secant,som,sur,tim,toi,zar" +skip = "./.*,*.json,ciphers/prehistoric_men.txt,project_euler/problem_022/p022_names.txt,pyproject.toml,strings/dictionary.txt,strings/words.txt" + [tool.pytest.ini_options] markers = [ "mat_ops: mark a test as utilizing matrix operations.", @@ -133,7 +137,3 @@ omit = [ "project_euler/*" ] sort = "Cover" - -[tool.codespell] -ignore-words-list = "3rt,ans,bitap,crate,damon,fo,followings,hist,iff,kwanza,manuel,mater,secant,som,sur,tim,toi,zar" -skip = "./.*,*.json,ciphers/prehistoric_men.txt,project_euler/problem_022/p022_names.txt,pyproject.toml,strings/dictionary.txt,strings/words.txt" From 8b7352626e54b619113b771a7e9586aabe603fa7 Mon Sep 17 00:00:00 2001 From: Suyash Dongre <109069262+Suyashd999@users.noreply.github.com> Date: Sun, 12 Nov 2023 07:43:04 +0530 Subject: [PATCH 532/757] Added doctest to randomized_heap.py (#11151) --- data_structures/heap/randomized_heap.py | 30 +++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/data_structures/heap/randomized_heap.py b/data_structures/heap/randomized_heap.py index c0f9888f80c7..12888c1f4089 100644 --- a/data_structures/heap/randomized_heap.py +++ b/data_structures/heap/randomized_heap.py @@ -22,14 +22,40 @@ def __init__(self, value: T) -> None: @property def value(self) -> T: - """Return the value of the node.""" + """ + Return the value of the node. + + >>> rhn = RandomizedHeapNode(10) + >>> rhn.value + 10 + >>> rhn = RandomizedHeapNode(-10) + >>> rhn.value + -10 + """ return self._value @staticmethod def merge( root1: RandomizedHeapNode[T] | None, root2: RandomizedHeapNode[T] | None ) -> RandomizedHeapNode[T] | None: - """Merge 2 nodes together.""" + """ + Merge 2 nodes together. + + >>> rhn1 = RandomizedHeapNode(10) + >>> rhn2 = RandomizedHeapNode(20) + >>> RandomizedHeapNode.merge(rhn1, rhn2).value + 10 + + >>> rhn1 = RandomizedHeapNode(20) + >>> rhn2 = RandomizedHeapNode(10) + >>> RandomizedHeapNode.merge(rhn1, rhn2).value + 10 + + >>> rhn1 = RandomizedHeapNode(5) + >>> rhn2 = RandomizedHeapNode(0) + >>> RandomizedHeapNode.merge(rhn1, rhn2).value + 0 + """ if not root1: return root2 From fb17eeab7d1fbc608a538b6d154d2c08781e087d Mon Sep 17 00:00:00 2001 From: Suyash Dongre <109069262+Suyashd999@users.noreply.github.com> Date: Sun, 12 Nov 2023 07:46:43 +0530 Subject: [PATCH 533/757] Added doctest to stack.py (#11149) --- data_structures/stacks/stack.py | 92 +++++++++++++++++++++++++++++++-- 1 file changed, 87 insertions(+), 5 deletions(-) diff --git a/data_structures/stacks/stack.py b/data_structures/stacks/stack.py index a14f4648a399..93698f5aa116 100644 --- a/data_structures/stacks/stack.py +++ b/data_structures/stacks/stack.py @@ -33,7 +33,23 @@ def __str__(self) -> str: return str(self.stack) def push(self, data: T) -> None: - """Push an element to the top of the stack.""" + """ + Push an element to the top of the stack. + + >>> S = Stack(2) # stack size = 2 + >>> S.push(10) + >>> S.push(20) + >>> print(S) + [10, 20] + + >>> S = Stack(1) # stack size = 1 + >>> S.push(10) + >>> S.push(20) + Traceback (most recent call last): + ... + data_structures.stacks.stack.StackOverflowError + + """ if len(self.stack) >= self.limit: raise StackOverflowError self.stack.append(data) @@ -42,6 +58,12 @@ def pop(self) -> T: """ Pop an element off of the top of the stack. + >>> S = Stack() + >>> S.push(-5) + >>> S.push(10) + >>> S.pop() + 10 + >>> Stack().pop() Traceback (most recent call last): ... @@ -55,7 +77,13 @@ def peek(self) -> T: """ Peek at the top-most element of the stack. - >>> Stack().pop() + >>> S = Stack() + >>> S.push(-5) + >>> S.push(10) + >>> S.peek() + 10 + + >>> Stack().peek() Traceback (most recent call last): ... data_structures.stacks.stack.StackUnderflowError @@ -65,18 +93,68 @@ def peek(self) -> T: return self.stack[-1] def is_empty(self) -> bool: - """Check if a stack is empty.""" + """ + Check if a stack is empty. + + >>> S = Stack() + >>> S.is_empty() + True + + >>> S = Stack() + >>> S.push(10) + >>> S.is_empty() + False + """ return not bool(self.stack) def is_full(self) -> bool: + """ + >>> S = Stack() + >>> S.is_full() + False + + >>> S = Stack(1) + >>> S.push(10) + >>> S.is_full() + True + """ return self.size() == self.limit def size(self) -> int: - """Return the size of the stack.""" + """ + Return the size of the stack. + + >>> S = Stack(3) + >>> S.size() + 0 + + >>> S = Stack(3) + >>> S.push(10) + >>> S.size() + 1 + + >>> S = Stack(3) + >>> S.push(10) + >>> S.push(20) + >>> S.size() + 2 + """ return len(self.stack) def __contains__(self, item: T) -> bool: - """Check if item is in stack""" + """ + Check if item is in stack + + >>> S = Stack(3) + >>> S.push(10) + >>> 10 in S + True + + >>> S = Stack(3) + >>> S.push(10) + >>> 20 in S + False + """ return item in self.stack @@ -131,3 +209,7 @@ def test_stack() -> None: if __name__ == "__main__": test_stack() + + import doctest + + doctest.testmod() From 0e2e6abd6f24d0d816212ff0480a18abecd3028b Mon Sep 17 00:00:00 2001 From: Suyash Dongre <109069262+Suyashd999@users.noreply.github.com> Date: Mon, 13 Nov 2023 16:35:22 +0530 Subject: [PATCH 534/757] Added doctest to heap.py (#11129) * Added doctest to heap.py * Update heap.py --- data_structures/heap/heap.py | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/data_structures/heap/heap.py b/data_structures/heap/heap.py index 29bff3af07e3..7b15e69f13ca 100644 --- a/data_structures/heap/heap.py +++ b/data_structures/heap/heap.py @@ -53,7 +53,37 @@ def __repr__(self) -> str: return str(self.h) def parent_index(self, child_idx: int) -> int | None: - """return the parent index of given child""" + """ + returns the parent index based on the given child index + + >>> h = Heap() + >>> h.build_max_heap([103, 9, 1, 7, 11, 15, 25, 201, 209, 107, 5]) + >>> h + [209, 201, 25, 103, 107, 15, 1, 9, 7, 11, 5] + + >>> h.parent_index(-1) # returns none if index is <=0 + + >>> h.parent_index(0) # returns none if index is <=0 + + >>> h.parent_index(1) + 0 + >>> h.parent_index(2) + 0 + >>> h.parent_index(3) + 1 + >>> h.parent_index(4) + 1 + >>> h.parent_index(5) + 2 + >>> h.parent_index(10.5) + 4.0 + >>> h.parent_index(209.0) + 104.0 + >>> h.parent_index("Test") + Traceback (most recent call last): + ... + TypeError: '>' not supported between instances of 'str' and 'int' + """ if child_idx > 0: return (child_idx - 1) // 2 return None From 5f61af4fbbab33704b4aebd6523c64f8e6360869 Mon Sep 17 00:00:00 2001 From: MC <129918860+FishyGitHubUser@users.noreply.github.com> Date: Thu, 16 Nov 2023 19:00:48 +0800 Subject: [PATCH 535/757] Fix ignore venv in build_directory_md.py (#11156) Co-authored-by: MICHAEL CASTLE --- scripts/build_directory_md.py | 6 +++++- web_programming/{get_imdbtop.py => get_imdbtop.py.DISABLED} | 0 2 files changed, 5 insertions(+), 1 deletion(-) rename web_programming/{get_imdbtop.py => get_imdbtop.py.DISABLED} (100%) diff --git a/scripts/build_directory_md.py b/scripts/build_directory_md.py index 24bc00cd036f..aa95b95db4b5 100755 --- a/scripts/build_directory_md.py +++ b/scripts/build_directory_md.py @@ -6,7 +6,11 @@ def good_file_paths(top_dir: str = ".") -> Iterator[str]: for dir_path, dir_names, filenames in os.walk(top_dir): - dir_names[:] = [d for d in dir_names if d != "scripts" and d[0] not in "._"] + dir_names[:] = [ + d + for d in dir_names + if d != "scripts" and d[0] not in "._" and "venv" not in d + ] for filename in filenames: if filename == "__init__.py": continue diff --git a/web_programming/get_imdbtop.py b/web_programming/get_imdbtop.py.DISABLED similarity index 100% rename from web_programming/get_imdbtop.py rename to web_programming/get_imdbtop.py.DISABLED From 3999abfea392209fcb67c2218774a229878cf4cb Mon Sep 17 00:00:00 2001 From: Margaret <62753112+meg-1@users.noreply.github.com> Date: Fri, 24 Nov 2023 20:00:21 +0200 Subject: [PATCH 536/757] adding a geometry module (#11138) * adding a geometry module * fixing errors and adding type hints * Create code_review_feedback.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * implementing suggestions * fixing ruff errors * Update geometry/code_review_feedback.py * Update geometry/code_review_feedback.py * Update geometry/geometry.py * Apply suggestions from code review * Delete geometry/code_review_feedback.py * Update geometry/geometry.py * Update geometry/geometry.py --------- Co-authored-by: Christian Clauss Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- geometry/geometry.py | 259 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 259 insertions(+) create mode 100644 geometry/geometry.py diff --git a/geometry/geometry.py b/geometry/geometry.py new file mode 100644 index 000000000000..9e353dee17a7 --- /dev/null +++ b/geometry/geometry.py @@ -0,0 +1,259 @@ +from __future__ import annotations + +import math +from dataclasses import dataclass, field +from types import NoneType +from typing import Self + +# Building block classes + + +@dataclass +class Angle: + """ + An Angle in degrees (unit of measurement) + + >>> Angle() + Angle(degrees=90) + >>> Angle(45.5) + Angle(degrees=45.5) + >>> Angle(-1) + Traceback (most recent call last): + ... + TypeError: degrees must be a numeric value between 0 and 360. + >>> Angle(361) + Traceback (most recent call last): + ... + TypeError: degrees must be a numeric value between 0 and 360. + """ + + degrees: float = 90 + + def __post_init__(self) -> None: + if not isinstance(self.degrees, (int, float)) or not 0 <= self.degrees <= 360: + raise TypeError("degrees must be a numeric value between 0 and 360.") + + +@dataclass +class Side: + """ + A side of a two dimensional Shape such as Polygon, etc. + adjacent_sides: a list of sides which are adjacent to the current side + angle: the angle in degrees between each adjacent side + length: the length of the current side in meters + + >>> Side(5) + Side(length=5, angle=Angle(degrees=90), next_side=None) + >>> Side(5, Angle(45.6)) + Side(length=5, angle=Angle(degrees=45.6), next_side=None) + >>> Side(5, Angle(45.6), Side(1, Angle(2))) # doctest: +ELLIPSIS + Side(length=5, angle=Angle(degrees=45.6), next_side=Side(length=1, angle=Angle(d... + """ + + length: float + angle: Angle = field(default_factory=Angle) + next_side: Side | None = None + + def __post_init__(self) -> None: + if not isinstance(self.length, (int, float)) or self.length <= 0: + raise TypeError("length must be a positive numeric value.") + if not isinstance(self.angle, Angle): + raise TypeError("angle must be an Angle object.") + if not isinstance(self.next_side, (Side, NoneType)): + raise TypeError("next_side must be a Side or None.") + + +@dataclass +class Ellipse: + """ + A geometric Ellipse on a 2D surface + + >>> Ellipse(5, 10) + Ellipse(major_radius=5, minor_radius=10) + >>> Ellipse(5, 10) is Ellipse(5, 10) + False + >>> Ellipse(5, 10) == Ellipse(5, 10) + True + """ + + major_radius: float + minor_radius: float + + @property + def area(self) -> float: + """ + >>> Ellipse(5, 10).area + 157.07963267948966 + """ + return math.pi * self.major_radius * self.minor_radius + + @property + def perimeter(self) -> float: + """ + >>> Ellipse(5, 10).perimeter + 47.12388980384689 + """ + return math.pi * (self.major_radius + self.minor_radius) + + +class Circle(Ellipse): + """ + A geometric Circle on a 2D surface + + >>> Circle(5) + Circle(radius=5) + >>> Circle(5) is Circle(5) + False + >>> Circle(5) == Circle(5) + True + >>> Circle(5).area + 78.53981633974483 + >>> Circle(5).perimeter + 31.41592653589793 + """ + + def __init__(self, radius: float) -> None: + super().__init__(radius, radius) + self.radius = radius + + def __repr__(self) -> str: + return f"Circle(radius={self.radius})" + + @property + def diameter(self) -> float: + """ + >>> Circle(5).diameter + 10 + """ + return self.radius * 2 + + def max_parts(self, num_cuts: float) -> float: + """ + Return the maximum number of parts that circle can be divided into if cut + 'num_cuts' times. + + >>> circle = Circle(5) + >>> circle.max_parts(0) + 1.0 + >>> circle.max_parts(7) + 29.0 + >>> circle.max_parts(54) + 1486.0 + >>> circle.max_parts(22.5) + 265.375 + >>> circle.max_parts(-222) + Traceback (most recent call last): + ... + TypeError: num_cuts must be a positive numeric value. + >>> circle.max_parts("-222") + Traceback (most recent call last): + ... + TypeError: num_cuts must be a positive numeric value. + """ + if not isinstance(num_cuts, (int, float)) or num_cuts < 0: + raise TypeError("num_cuts must be a positive numeric value.") + return (num_cuts + 2 + num_cuts**2) * 0.5 + + +@dataclass +class Polygon: + """ + An abstract class which represents Polygon on a 2D surface. + + >>> Polygon() + Polygon(sides=[]) + """ + + sides: list[Side] = field(default_factory=list) + + def add_side(self, side: Side) -> Self: + """ + >>> Polygon().add_side(Side(5)) + Polygon(sides=[Side(length=5, angle=Angle(degrees=90), next_side=None)]) + """ + self.sides.append(side) + return self + + def get_side(self, index: int) -> Side: + """ + >>> Polygon().get_side(0) + Traceback (most recent call last): + ... + IndexError: list index out of range + >>> Polygon().add_side(Side(5)).get_side(-1) + Side(length=5, angle=Angle(degrees=90), next_side=None) + """ + return self.sides[index] + + def set_side(self, index: int, side: Side) -> Self: + """ + >>> Polygon().set_side(0, Side(5)) + Traceback (most recent call last): + ... + IndexError: list assignment index out of range + >>> Polygon().add_side(Side(5)).set_side(0, Side(10)) + Polygon(sides=[Side(length=10, angle=Angle(degrees=90), next_side=None)]) + """ + self.sides[index] = side + return self + + +class Rectangle(Polygon): + """ + A geometric rectangle on a 2D surface. + + >>> rectangle_one = Rectangle(5, 10) + >>> rectangle_one.perimeter() + 30 + >>> rectangle_one.area() + 50 + """ + + def __init__(self, short_side_length: float, long_side_length: float) -> None: + super().__init__() + self.short_side_length = short_side_length + self.long_side_length = long_side_length + self.post_init() + + def post_init(self) -> None: + """ + >>> Rectangle(5, 10) # doctest: +NORMALIZE_WHITESPACE + Rectangle(sides=[Side(length=5, angle=Angle(degrees=90), next_side=None), + Side(length=10, angle=Angle(degrees=90), next_side=None)]) + """ + self.short_side = Side(self.short_side_length) + self.long_side = Side(self.long_side_length) + super().add_side(self.short_side) + super().add_side(self.long_side) + + def perimeter(self) -> float: + return (self.short_side.length + self.long_side.length) * 2 + + def area(self) -> float: + return self.short_side.length * self.long_side.length + + +@dataclass +class Square(Rectangle): + """ + a structure which represents a + geometrical square on a 2D surface + >>> square_one = Square(5) + >>> square_one.perimeter() + 20 + >>> square_one.area() + 25 + """ + + def __init__(self, side_length: float) -> None: + super().__init__(side_length, side_length) + + def perimeter(self) -> float: + return super().perimeter() + + def area(self) -> float: + return super().area() + + +if __name__ == "__main__": + __import__("doctest").testmod() From b8e7a4c76c4a4929ac2c7e784b0c151be47c1e6e Mon Sep 17 00:00:00 2001 From: MC <129918860+FishyGitHubUser@users.noreply.github.com> Date: Sat, 25 Nov 2023 19:17:59 +0800 Subject: [PATCH 537/757] Fix typo in knight_tour.py (#11173) --- backtracking/knight_tour.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backtracking/knight_tour.py b/backtracking/knight_tour.py index cc88307b7fe8..5f7dee8d97bf 100644 --- a/backtracking/knight_tour.py +++ b/backtracking/knight_tour.py @@ -79,7 +79,7 @@ def open_knight_tour(n: int) -> list[list[int]]: >>> open_knight_tour(2) Traceback (most recent call last): ... - ValueError: Open Kight Tour cannot be performed on a board of size 2 + ValueError: Open Knight Tour cannot be performed on a board of size 2 """ board = [[0 for i in range(n)] for j in range(n)] @@ -91,7 +91,7 @@ def open_knight_tour(n: int) -> list[list[int]]: return board board[i][j] = 0 - msg = f"Open Kight Tour cannot be performed on a board of size {n}" + msg = f"Open Knight Tour cannot be performed on a board of size {n}" raise ValueError(msg) From 5898b9603bbe9b449cf5a2e331cf0c7d3245a788 Mon Sep 17 00:00:00 2001 From: Rahid Zeynalov <44039543+rahidzeynal@users.noreply.github.com> Date: Sat, 25 Nov 2023 15:25:46 +0400 Subject: [PATCH 538/757] Typo deicmal -> decimal (#11169) --- bit_manipulation/is_even.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bit_manipulation/is_even.py b/bit_manipulation/is_even.py index ba036f35aa1e..6f95a1160797 100644 --- a/bit_manipulation/is_even.py +++ b/bit_manipulation/is_even.py @@ -1,7 +1,7 @@ def is_even(number: int) -> bool: """ return true if the input integer is even - Explanation: Lets take a look at the following deicmal to binary conversions + Explanation: Lets take a look at the following decimal to binary conversions 2 => 10 14 => 1110 100 => 1100100 From 4151a13b57fbd881d3fce3bb61101fe58ad541ae Mon Sep 17 00:00:00 2001 From: Clark <1009013283@qq.com> Date: Sat, 25 Nov 2023 20:26:03 +0800 Subject: [PATCH 539/757] add graphs/ant_colony_optimization_algorithms.py (#11163) * add ant_colonyant_colony_optimization_algorithms.py * Modify details * Modify type annotation * Add tests for KeyError, IndexError, StopIteration, etc. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: Christian Clauss Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- graphs/ant_colony_optimization_algorithms.py | 226 +++++++++++++++++++ 1 file changed, 226 insertions(+) create mode 100644 graphs/ant_colony_optimization_algorithms.py diff --git a/graphs/ant_colony_optimization_algorithms.py b/graphs/ant_colony_optimization_algorithms.py new file mode 100644 index 000000000000..652ad6144297 --- /dev/null +++ b/graphs/ant_colony_optimization_algorithms.py @@ -0,0 +1,226 @@ +""" +Use an ant colony optimization algorithm to solve the travelling salesman problem (TSP) +which asks the following question: +"Given a list of cities and the distances between each pair of cities, what is the + shortest possible route that visits each city exactly once and returns to the origin + city?" + +https://en.wikipedia.org/wiki/Ant_colony_optimization_algorithms +https://en.wikipedia.org/wiki/Travelling_salesman_problem + +Author: Clark +""" + +import copy +import random + +cities = { + 0: [0, 0], + 1: [0, 5], + 2: [3, 8], + 3: [8, 10], + 4: [12, 8], + 5: [12, 4], + 6: [8, 0], + 7: [6, 2], +} + + +def main( + cities: dict[int, list[int]], + ants_num: int, + iterations_num: int, + pheromone_evaporation: float, + alpha: float, + beta: float, + q: float, # Pheromone system parameters Q,which is a constant +) -> tuple[list[int], float]: + """ + Ant colony algorithm main function + >>> main(cities=cities, ants_num=10, iterations_num=20, + ... pheromone_evaporation=0.7, alpha=1.0, beta=5.0, q=10) + ([0, 1, 2, 3, 4, 5, 6, 7, 0], 37.909778143828696) + >>> main(cities={0: [0, 0], 1: [2, 2]}, ants_num=5, iterations_num=5, + ... pheromone_evaporation=0.7, alpha=1.0, beta=5.0, q=10) + ([0, 1, 0], 5.656854249492381) + >>> main(cities={0: [0, 0], 1: [2, 2], 4: [4, 4]}, ants_num=5, iterations_num=5, + ... pheromone_evaporation=0.7, alpha=1.0, beta=5.0, q=10) + Traceback (most recent call last): + ... + IndexError: list index out of range + >>> main(cities={}, ants_num=5, iterations_num=5, + ... pheromone_evaporation=0.7, alpha=1.0, beta=5.0, q=10) + Traceback (most recent call last): + ... + StopIteration + >>> main(cities={0: [0, 0], 1: [2, 2]}, ants_num=0, iterations_num=5, + ... pheromone_evaporation=0.7, alpha=1.0, beta=5.0, q=10) + ([], inf) + >>> main(cities={0: [0, 0], 1: [2, 2]}, ants_num=5, iterations_num=0, + ... pheromone_evaporation=0.7, alpha=1.0, beta=5.0, q=10) + ([], inf) + >>> main(cities={0: [0, 0], 1: [2, 2]}, ants_num=5, iterations_num=5, + ... pheromone_evaporation=1, alpha=1.0, beta=5.0, q=10) + ([0, 1, 0], 5.656854249492381) + >>> main(cities={0: [0, 0], 1: [2, 2]}, ants_num=5, iterations_num=5, + ... pheromone_evaporation=0, alpha=1.0, beta=5.0, q=10) + ([0, 1, 0], 5.656854249492381) + """ + # Initialize the pheromone matrix + cities_num = len(cities) + pheromone = [[1.0] * cities_num] * cities_num + + best_path: list[int] = [] + best_distance = float("inf") + for _ in range(iterations_num): + ants_route = [] + for _ in range(ants_num): + unvisited_cities = copy.deepcopy(cities) + current_city = {next(iter(cities.keys())): next(iter(cities.values()))} + del unvisited_cities[next(iter(current_city.keys()))] + ant_route = [next(iter(current_city.keys()))] + while unvisited_cities: + current_city, unvisited_cities = city_select( + pheromone, current_city, unvisited_cities, alpha, beta + ) + ant_route.append(next(iter(current_city.keys()))) + ant_route.append(0) + ants_route.append(ant_route) + + pheromone, best_path, best_distance = pheromone_update( + pheromone, + cities, + pheromone_evaporation, + ants_route, + q, + best_path, + best_distance, + ) + return best_path, best_distance + + +def distance(city1: list[int], city2: list[int]) -> float: + """ + Calculate the distance between two coordinate points + >>> distance([0, 0], [3, 4] ) + 5.0 + >>> distance([0, 0], [-3, 4] ) + 5.0 + >>> distance([0, 0], [-3, -4] ) + 5.0 + """ + return (((city1[0] - city2[0]) ** 2) + ((city1[1] - city2[1]) ** 2)) ** 0.5 + + +def pheromone_update( + pheromone: list[list[float]], + cities: dict[int, list[int]], + pheromone_evaporation: float, + ants_route: list[list[int]], + q: float, # Pheromone system parameters Q,which is a constant + best_path: list[int], + best_distance: float, +) -> tuple[list[list[float]], list[int], float]: + """ + Update pheromones on the route and update the best route + >>> + >>> pheromone_update(pheromone=[[1.0, 1.0], [1.0, 1.0]], + ... cities={0: [0,0], 1: [2,2]}, pheromone_evaporation=0.7, + ... ants_route=[[0, 1, 0]], q=10, best_path=[], + ... best_distance=float("inf")) + ([[0.7, 4.235533905932737], [4.235533905932737, 0.7]], [0, 1, 0], 5.656854249492381) + >>> pheromone_update(pheromone=[], + ... cities={0: [0,0], 1: [2,2]}, pheromone_evaporation=0.7, + ... ants_route=[[0, 1, 0]], q=10, best_path=[], + ... best_distance=float("inf")) + Traceback (most recent call last): + ... + IndexError: list index out of range + >>> pheromone_update(pheromone=[[1.0, 1.0], [1.0, 1.0]], + ... cities={}, pheromone_evaporation=0.7, + ... ants_route=[[0, 1, 0]], q=10, best_path=[], + ... best_distance=float("inf")) + Traceback (most recent call last): + ... + KeyError: 0 + """ + for a in range(len(cities)): # Update the volatilization of pheromone on all routes + for b in range(len(cities)): + pheromone[a][b] *= pheromone_evaporation + for ant_route in ants_route: + total_distance = 0.0 + for i in range(len(ant_route) - 1): # Calculate total distance + total_distance += distance(cities[ant_route[i]], cities[ant_route[i + 1]]) + delta_pheromone = q / total_distance + for i in range(len(ant_route) - 1): # Update pheromones + pheromone[ant_route[i]][ant_route[i + 1]] += delta_pheromone + pheromone[ant_route[i + 1]][ant_route[i]] = pheromone[ant_route[i]][ + ant_route[i + 1] + ] + + if total_distance < best_distance: + best_path = ant_route + best_distance = total_distance + + return pheromone, best_path, best_distance + + +def city_select( + pheromone: list[list[float]], + current_city: dict[int, list[int]], + unvisited_cities: dict[int, list[int]], + alpha: float, + beta: float, +) -> tuple[dict[int, list[int]], dict[int, list[int]]]: + """ + Choose the next city for ants + >>> city_select(pheromone=[[1.0, 1.0], [1.0, 1.0]], current_city={0: [0, 0]}, + ... unvisited_cities={1: [2, 2]}, alpha=1.0, beta=5.0) + ({1: [2, 2]}, {}) + >>> city_select(pheromone=[], current_city={0: [0,0]}, + ... unvisited_cities={1: [2, 2]}, alpha=1.0, beta=5.0) + Traceback (most recent call last): + ... + IndexError: list index out of range + >>> city_select(pheromone=[[1.0, 1.0], [1.0, 1.0]], current_city={}, + ... unvisited_cities={1: [2, 2]}, alpha=1.0, beta=5.0) + Traceback (most recent call last): + ... + StopIteration + >>> city_select(pheromone=[[1.0, 1.0], [1.0, 1.0]], current_city={0: [0, 0]}, + ... unvisited_cities={}, alpha=1.0, beta=5.0) + Traceback (most recent call last): + ... + IndexError: list index out of range + """ + probabilities = [] + for city in unvisited_cities: + city_distance = distance( + unvisited_cities[city], next(iter(current_city.values())) + ) + probability = (pheromone[city][next(iter(current_city.keys()))] ** alpha) * ( + (1 / city_distance) ** beta + ) + probabilities.append(probability) + + chosen_city_i = random.choices( + list(unvisited_cities.keys()), weights=probabilities + )[0] + chosen_city = {chosen_city_i: unvisited_cities[chosen_city_i]} + del unvisited_cities[next(iter(chosen_city.keys()))] + return chosen_city, unvisited_cities + + +if __name__ == "__main__": + best_path, best_distance = main( + cities=cities, + ants_num=10, + iterations_num=20, + pheromone_evaporation=0.7, + alpha=1.0, + beta=5.0, + q=10, + ) + + print(f"{best_path = }") + print(f"{best_distance = }") From 050b2a6e2cf0e474b75cf48abe4aa134b97643e4 Mon Sep 17 00:00:00 2001 From: moaldeen <132774635+moaldeen@users.noreply.github.com> Date: Sat, 25 Nov 2023 08:31:17 -0500 Subject: [PATCH 540/757] Bug fix combinations (#11158) * Update all_combinations.py The original implementation had limitations in handling edge cases and certain input parameters, leading to potential RecursionError. * Update all_combinations.py Added checks to handle cases where n or k are negative or where k is greater than n. In such scenarios, the function now returns an empty list, avoiding invalid recursive calls. * Update error handling Added checks to handle cases where `n` or `k` are negative or where `k` is greater than `n`. In such scenarios, the function now returns an empty list, avoiding invalid recursive calls. * Update backtracking/all_combinations.py * Update all_combinations.py --------- Co-authored-by: Christian Clauss --- backtracking/all_combinations.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/backtracking/all_combinations.py b/backtracking/all_combinations.py index ecbcc5882ec1..407304948c39 100644 --- a/backtracking/all_combinations.py +++ b/backtracking/all_combinations.py @@ -26,9 +26,11 @@ def generate_all_combinations(n: int, k: int) -> list[list[int]]: >>> generate_all_combinations(n=10, k=-1) Traceback (most recent call last): ... - RecursionError: maximum recursion depth exceeded + ValueError: k must not be negative >>> generate_all_combinations(n=-1, k=10) - [] + Traceback (most recent call last): + ... + ValueError: n must not be negative >>> generate_all_combinations(n=5, k=4) [[1, 2, 3, 4], [1, 2, 3, 5], [1, 2, 4, 5], [1, 3, 4, 5], [2, 3, 4, 5]] >>> from itertools import combinations @@ -36,6 +38,10 @@ def generate_all_combinations(n: int, k: int) -> list[list[int]]: ... for n in range(1, 6) for k in range(1, 6)) True """ + if k < 0: + raise ValueError("k must not be negative") + if n < 0: + raise ValueError("n must not be negative") result: list[list[int]] = [] create_all_state(1, n, k, [], result) From 8b39a0fb54d0f63489952606d2036d1a63f981e3 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 25 Nov 2023 14:53:18 +0100 Subject: [PATCH 541/757] [pre-commit.ci] pre-commit autoupdate (#11154) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/astral-sh/ruff-pre-commit: v0.1.4 → v0.1.6](https://github.com/astral-sh/ruff-pre-commit/compare/v0.1.4...v0.1.6) - [github.com/psf/black: 23.10.1 → 23.11.0](https://github.com/psf/black/compare/23.10.1...23.11.0) - [github.com/tox-dev/pyproject-fmt: 1.4.1 → 1.5.1](https://github.com/tox-dev/pyproject-fmt/compare/1.4.1...1.5.1) - [github.com/pre-commit/mirrors-mypy: v1.6.1 → v1.7.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.6.1...v1.7.0) * updating DIRECTORY.md * Update spiral_print.py * Update matrix/spiral_print.py * Update matrix/spiral_print.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: Christian Clauss --- .pre-commit-config.yaml | 8 ++++---- DIRECTORY.md | 1 - matrix/spiral_print.py | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1bb3de782275..9a0f78fdde5a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,12 +16,12 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.1.4 + rev: v0.1.6 hooks: - id: ruff - repo: https://github.com/psf/black - rev: 23.10.1 + rev: 23.11.0 hooks: - id: black @@ -33,7 +33,7 @@ repos: - tomli - repo: https://github.com/tox-dev/pyproject-fmt - rev: "1.4.1" + rev: "1.5.1" hooks: - id: pyproject-fmt @@ -51,7 +51,7 @@ repos: - id: validate-pyproject - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.6.1 + rev: v1.7.0 hooks: - id: mypy args: diff --git a/DIRECTORY.md b/DIRECTORY.md index cb4b00b045b5..438950325380 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -1310,7 +1310,6 @@ * [Fetch Well Rx Price](web_programming/fetch_well_rx_price.py) * [Get Amazon Product Data](web_programming/get_amazon_product_data.py) * [Get Imdb Top 250 Movies Csv](web_programming/get_imdb_top_250_movies_csv.py) - * [Get Imdbtop](web_programming/get_imdbtop.py) * [Get Ip Geolocation](web_programming/get_ip_geolocation.py) * [Get Top Billionaires](web_programming/get_top_billionaires.py) * [Get Top Hn Posts](web_programming/get_top_hn_posts.py) diff --git a/matrix/spiral_print.py b/matrix/spiral_print.py index 5eef263f7aef..7ba0a275157b 100644 --- a/matrix/spiral_print.py +++ b/matrix/spiral_print.py @@ -116,7 +116,7 @@ def spiral_traversal(matrix: list[list]) -> list[int]: [1, 2, 3, 4, 8, 12, 11, 10, 9, 5, 6, 7] + spiral_traversal([]) """ if matrix: - return list(matrix.pop(0)) + spiral_traversal(list(zip(*matrix))[::-1]) + return list(matrix.pop(0)) + spiral_traversal(list(zip(*matrix))[::-1]) # type: ignore else: return [] From 86ae30d29e4813c2ef071d7d27f1302b6be6cc0c Mon Sep 17 00:00:00 2001 From: Harsh Kumar <61012869+cyrixninja@users.noreply.github.com> Date: Sat, 25 Nov 2023 19:50:42 +0530 Subject: [PATCH 542/757] Create Spearman's rank correlation coefficient (#11155) * Create spearman_rank_correlation_coefficient.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fixed Issues * Added More Description * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fixed Issues * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Tried Fixing Issues * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Tried Fixing Issues * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fixed Issues * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fixed Issues * Apply suggestions from code review * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update maths/spearman_rank_correlation_coefficient.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- .../spearman_rank_correlation_coefficient.py | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 maths/spearman_rank_correlation_coefficient.py diff --git a/maths/spearman_rank_correlation_coefficient.py b/maths/spearman_rank_correlation_coefficient.py new file mode 100644 index 000000000000..32ff6b9e3d71 --- /dev/null +++ b/maths/spearman_rank_correlation_coefficient.py @@ -0,0 +1,82 @@ +from collections.abc import Sequence + + +def assign_ranks(data: Sequence[float]) -> list[int]: + """ + Assigns ranks to elements in the array. + + :param data: List of floats. + :return: List of ints representing the ranks. + + Example: + >>> assign_ranks([3.2, 1.5, 4.0, 2.7, 5.1]) + [3, 1, 4, 2, 5] + + >>> assign_ranks([10.5, 8.1, 12.4, 9.3, 11.0]) + [3, 1, 5, 2, 4] + """ + ranked_data = sorted((value, index) for index, value in enumerate(data)) + ranks = [0] * len(data) + + for position, (_, index) in enumerate(ranked_data): + ranks[index] = position + 1 + + return ranks + + +def calculate_spearman_rank_correlation( + variable_1: Sequence[float], variable_2: Sequence[float] +) -> float: + """ + Calculates Spearman's rank correlation coefficient. + + :param variable_1: List of floats representing the first variable. + :param variable_2: List of floats representing the second variable. + :return: Spearman's rank correlation coefficient. + + Example Usage: + + >>> x = [1, 2, 3, 4, 5] + >>> y = [5, 4, 3, 2, 1] + >>> calculate_spearman_rank_correlation(x, y) + -1.0 + + >>> x = [1, 2, 3, 4, 5] + >>> y = [2, 4, 6, 8, 10] + >>> calculate_spearman_rank_correlation(x, y) + 1.0 + + >>> x = [1, 2, 3, 4, 5] + >>> y = [5, 1, 2, 9, 5] + >>> calculate_spearman_rank_correlation(x, y) + 0.6 + """ + n = len(variable_1) + rank_var1 = assign_ranks(variable_1) + rank_var2 = assign_ranks(variable_2) + + # Calculate differences of ranks + d = [rx - ry for rx, ry in zip(rank_var1, rank_var2)] + + # Calculate the sum of squared differences + d_squared = sum(di**2 for di in d) + + # Calculate the Spearman's rank correlation coefficient + rho = 1 - (6 * d_squared) / (n * (n**2 - 1)) + + return rho + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + + # Example usage: + print( + f"{calculate_spearman_rank_correlation([1, 2, 3, 4, 5], [2, 4, 6, 8, 10]) = }" + ) + + print(f"{calculate_spearman_rank_correlation([1, 2, 3, 4, 5], [5, 4, 3, 2, 1]) = }") + + print(f"{calculate_spearman_rank_correlation([1, 2, 3, 4, 5], [5, 1, 2, 9, 5]) = }") From 84a1533fd5d262dae767a9298de1c1d7fcb2bec9 Mon Sep 17 00:00:00 2001 From: Suyash Dongre <109069262+Suyashd999@users.noreply.github.com> Date: Sat, 25 Nov 2023 19:59:44 +0530 Subject: [PATCH 543/757] Added doctest to binary_search_tree.py (#11145) * Added doctest to binary_search_tree.py * Apply suggestions from code review --------- Co-authored-by: Christian Clauss --- .../binary_tree/binary_search_tree.py | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/data_structures/binary_tree/binary_search_tree.py b/data_structures/binary_tree/binary_search_tree.py index f08f278a8e47..9071f03dcc8c 100644 --- a/data_structures/binary_tree/binary_search_tree.py +++ b/data_structures/binary_tree/binary_search_tree.py @@ -198,6 +198,30 @@ def insert(self, *values) -> Self: return self def search(self, value) -> Node | None: + """ + >>> tree = BinarySearchTree().insert(10, 20, 30, 40, 50) + >>> tree.search(10) + {'10': (None, {'20': (None, {'30': (None, {'40': (None, 50)})})})} + >>> tree.search(20) + {'20': (None, {'30': (None, {'40': (None, 50)})})} + >>> tree.search(30) + {'30': (None, {'40': (None, 50)})} + >>> tree.search(40) + {'40': (None, 50)} + >>> tree.search(50) + 50 + >>> tree.search(5) is None # element not present + True + >>> tree.search(0) is None # element not present + True + >>> tree.search(-5) is None # element not present + True + >>> BinarySearchTree().search(10) + Traceback (most recent call last): + ... + IndexError: Warning: Tree is empty! please use another. + """ + if self.empty(): raise IndexError("Warning: Tree is empty! please use another.") else: @@ -210,6 +234,15 @@ def search(self, value) -> Node | None: def get_max(self, node: Node | None = None) -> Node | None: """ We go deep on the right branch + + >>> BinarySearchTree().insert(10, 20, 30, 40, 50).get_max() + 50 + >>> BinarySearchTree().insert(-5, -1, 0.1, -0.3, -4.5).get_max() + {'0.1': (-0.3, None)} + >>> BinarySearchTree().insert(1, 78.3, 30, 74.0, 1).get_max() + {'78.3': ({'30': (1, 74.0)}, None)} + >>> BinarySearchTree().insert(1, 783, 30, 740, 1).get_max() + {'783': ({'30': (1, 740)}, None)} """ if node is None: if self.root is None: @@ -224,6 +257,15 @@ def get_max(self, node: Node | None = None) -> Node | None: def get_min(self, node: Node | None = None) -> Node | None: """ We go deep on the left branch + + >>> BinarySearchTree().insert(10, 20, 30, 40, 50).get_min() + {'10': (None, {'20': (None, {'30': (None, {'40': (None, 50)})})})} + >>> BinarySearchTree().insert(-5, -1, 0, -0.3, -4.5).get_min() + {'-5': (None, {'-1': (-4.5, {'0': (-0.3, None)})})} + >>> BinarySearchTree().insert(1, 78.3, 30, 74.0, 1).get_min() + {'1': (None, {'78.3': ({'30': (1, 74.0)}, None)})} + >>> BinarySearchTree().insert(1, 783, 30, 740, 1).get_min() + {'1': (None, {'783': ({'30': (1, 740)}, None)})} """ if node is None: node = self.root From 154e5e8681b7ae9711fbef0b89f0ce365a8bf5bf Mon Sep 17 00:00:00 2001 From: Pedram_Mohajer <48964282+pedram-mohajer@users.noreply.github.com> Date: Sun, 26 Nov 2023 17:46:54 -0500 Subject: [PATCH 544/757] Update levenshtein_distance.py (#11171) * Update levenshtein_distance.py * Update levenshtein_distance.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update levenshtein_distance.py * Update levenshtein_distance.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update levenshtein_distance.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update levenshtein_distance.py * Update levenshtein_distance.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- strings/levenshtein_distance.py | 93 ++++++++++++++++++++++++++------- 1 file changed, 74 insertions(+), 19 deletions(-) diff --git a/strings/levenshtein_distance.py b/strings/levenshtein_distance.py index 7be4074dc39b..3af6608723a5 100644 --- a/strings/levenshtein_distance.py +++ b/strings/levenshtein_distance.py @@ -1,20 +1,9 @@ -""" -This is a Python implementation of the levenshtein distance. -Levenshtein distance is a string metric for measuring the -difference between two sequences. - -For doctests run following command: -python -m doctest -v levenshtein-distance.py -or -python3 -m doctest -v levenshtein-distance.py - -For manual testing run: -python levenshtein-distance.py -""" +from collections.abc import Callable def levenshtein_distance(first_word: str, second_word: str) -> int: - """Implementation of the levenshtein distance in Python. + """ + Implementation of the Levenshtein distance in Python. :param first_word: the first word to measure the difference. :param second_word: the second word to measure the difference. :return: the levenshtein distance between the two words. @@ -47,7 +36,7 @@ def levenshtein_distance(first_word: str, second_word: str) -> int: current_row = [i + 1] for j, c2 in enumerate(second_word): - # Calculate insertions, deletions and substitutions + # Calculate insertions, deletions, and substitutions insertions = previous_row[j + 1] + 1 deletions = current_row[j] + 1 substitutions = previous_row[j] + (c1 != c2) @@ -62,9 +51,75 @@ def levenshtein_distance(first_word: str, second_word: str) -> int: return previous_row[-1] +def levenshtein_distance_optimized(first_word: str, second_word: str) -> int: + """ + Compute the Levenshtein distance between two words (strings). + The function is optimized for efficiency by modifying rows in place. + :param first_word: the first word to measure the difference. + :param second_word: the second word to measure the difference. + :return: the Levenshtein distance between the two words. + Examples: + >>> levenshtein_distance_optimized("planet", "planetary") + 3 + >>> levenshtein_distance_optimized("", "test") + 4 + >>> levenshtein_distance_optimized("book", "back") + 2 + >>> levenshtein_distance_optimized("book", "book") + 0 + >>> levenshtein_distance_optimized("test", "") + 4 + >>> levenshtein_distance_optimized("", "") + 0 + >>> levenshtein_distance_optimized("orchestration", "container") + 10 + """ + if len(first_word) < len(second_word): + return levenshtein_distance_optimized(second_word, first_word) + + if len(second_word) == 0: + return len(first_word) + + previous_row = list(range(len(second_word) + 1)) + + for i, c1 in enumerate(first_word): + current_row = [i + 1] + [0] * len(second_word) + + for j, c2 in enumerate(second_word): + insertions = previous_row[j + 1] + 1 + deletions = current_row[j] + 1 + substitutions = previous_row[j] + (c1 != c2) + current_row[j + 1] = min(insertions, deletions, substitutions) + + previous_row = current_row + + return previous_row[-1] + + +def benchmark_levenshtein_distance(func: Callable) -> None: + """ + Benchmark the Levenshtein distance function. + :param str: The name of the function being benchmarked. + :param func: The function to be benchmarked. + """ + from timeit import timeit + + stmt = f"{func.__name__}('sitting', 'kitten')" + setup = f"from __main__ import {func.__name__}" + number = 25_000 + result = timeit(stmt=stmt, setup=setup, number=number) + print(f"{func.__name__:<30} finished {number:,} runs in {result:.5f} seconds") + + if __name__ == "__main__": - first_word = input("Enter the first word:\n").strip() - second_word = input("Enter the second word:\n").strip() + # Get user input for words + first_word = input("Enter the first word for Levenshtein distance:\n").strip() + second_word = input("Enter the second word for Levenshtein distance:\n").strip() + + # Calculate and print Levenshtein distances + print(f"{levenshtein_distance(first_word, second_word) = }") + print(f"{levenshtein_distance_optimized(first_word, second_word) = }") - result = levenshtein_distance(first_word, second_word) - print(f"Levenshtein distance between {first_word} and {second_word} is {result}") + # Benchmark the Levenshtein distance functions + benchmark_levenshtein_distance(levenshtein_distance) + benchmark_levenshtein_distance(levenshtein_distance_optimized) From b8600035768da179adc709814f4b455b844982cc Mon Sep 17 00:00:00 2001 From: Pedram_Mohajer <48964282+pedram-mohajer@users.noreply.github.com> Date: Mon, 27 Nov 2023 12:43:51 -0500 Subject: [PATCH 545/757] Add doctest to is_safe function (#11183) --- backtracking/n_queens.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/backtracking/n_queens.py b/backtracking/n_queens.py index 0f237d95e7c8..2cd8c703fc72 100644 --- a/backtracking/n_queens.py +++ b/backtracking/n_queens.py @@ -24,6 +24,10 @@ def is_safe(board: list[list[int]], row: int, column: int) -> bool: Returns: Boolean Value + >>> is_safe([[0, 0, 0], [0, 0, 0], [0, 0, 0]], 1, 1) + True + >>> is_safe([[1, 0, 0], [0, 0, 0], [0, 0, 0]], 1, 1) + False """ n = len(board) # Size of the board From 0ac97f359f2c4b1a4b96db6a083fac95ca0cfe97 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 27 Nov 2023 19:13:24 +0100 Subject: [PATCH 546/757] [pre-commit.ci] pre-commit autoupdate (#11184) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/pre-commit/mirrors-mypy: v1.7.0 → v1.7.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.7.0...v1.7.1) * updating DIRECTORY.md --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- DIRECTORY.md | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9a0f78fdde5a..28f83a638d7b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -51,7 +51,7 @@ repos: - id: validate-pyproject - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.7.0 + rev: v1.7.1 hooks: - id: mypy args: diff --git a/DIRECTORY.md b/DIRECTORY.md index 438950325380..ea0ba22bcc13 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -428,12 +428,16 @@ * [Haversine Distance](geodesy/haversine_distance.py) * [Lamberts Ellipsoidal Distance](geodesy/lamberts_ellipsoidal_distance.py) +## Geometry + * [Geometry](geometry/geometry.py) + ## Graphics * [Bezier Curve](graphics/bezier_curve.py) * [Vector3 For 2D Rendering](graphics/vector3_for_2d_rendering.py) ## Graphs * [A Star](graphs/a_star.py) + * [Ant Colony Optimization Algorithms](graphs/ant_colony_optimization_algorithms.py) * [Articulation Points](graphs/articulation_points.py) * [Basic Graphs](graphs/basic_graphs.py) * [Bellman Ford](graphs/bellman_ford.py) @@ -718,6 +722,7 @@ * [Sock Merchant](maths/sock_merchant.py) * [Softmax](maths/softmax.py) * [Solovay Strassen Primality Test](maths/solovay_strassen_primality_test.py) + * [Spearman Rank Correlation Coefficient](maths/spearman_rank_correlation_coefficient.py) * Special Numbers * [Armstrong Numbers](maths/special_numbers/armstrong_numbers.py) * [Automorphic Number](maths/special_numbers/automorphic_number.py) From 82e539dc8226abe803aa562402cfe9f19ded9e22 Mon Sep 17 00:00:00 2001 From: Pedram_Mohajer <48964282+pedram-mohajer@users.noreply.github.com> Date: Fri, 1 Dec 2023 11:53:47 -0500 Subject: [PATCH 547/757] Create smallestRange.py (#11179) * Create smallestRange.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update smallestRange.py * Update smallestRange.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update smallestRange.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update and rename smallestRange.py to smallestrange.py * Update smallestrange.py * Update smallestrange.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update smallestrange.py * Rename smallestrange.py to smallest_range.py * Update smallest_range.py * Update smallest_range.py * Update smallest_range.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- greedy_methods/smallest_range.py | 71 ++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 greedy_methods/smallest_range.py diff --git a/greedy_methods/smallest_range.py b/greedy_methods/smallest_range.py new file mode 100644 index 000000000000..e2b7f8d7e96a --- /dev/null +++ b/greedy_methods/smallest_range.py @@ -0,0 +1,71 @@ +""" +smallest_range function takes a list of sorted integer lists and finds the smallest +range that includes at least one number from each list, using a min heap for efficiency. +""" + +from heapq import heappop, heappush +from sys import maxsize + + +def smallest_range(nums: list[list[int]]) -> list[int]: + """ + Find the smallest range from each list in nums. + + Uses min heap for efficiency. The range includes at least one number from each list. + + Args: + nums: List of k sorted integer lists. + + Returns: + list: Smallest range as a two-element list. + + Examples: + >>> smallest_range([[4, 10, 15, 24, 26], [0, 9, 12, 20], [5, 18, 22, 30]]) + [20, 24] + >>> smallest_range([[1, 2, 3], [1, 2, 3], [1, 2, 3]]) + [1, 1] + >>> smallest_range(((1, 2, 3), (1, 2, 3), (1, 2, 3))) + [1, 1] + >>> smallest_range(((-3, -2, -1), (0, 0, 0), (1, 2, 3))) + [-1, 1] + >>> smallest_range([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) + [3, 7] + >>> smallest_range([[0, 0, 0], [0, 0, 0], [0, 0, 0]]) + [0, 0] + >>> smallest_range([[], [], []]) + Traceback (most recent call last): + ... + IndexError: list index out of range + """ + + min_heap: list[tuple[int, int, int]] = [] + current_max = -maxsize - 1 + + for i, items in enumerate(nums): + heappush(min_heap, (items[0], i, 0)) + current_max = max(current_max, items[0]) + + # Initialize smallest_range with large integer values + smallest_range = [-maxsize - 1, maxsize] + + while min_heap: + current_min, list_index, element_index = heappop(min_heap) + + if current_max - current_min < smallest_range[1] - smallest_range[0]: + smallest_range = [current_min, current_max] + + if element_index == len(nums[list_index]) - 1: + break + + next_element = nums[list_index][element_index + 1] + heappush(min_heap, (next_element, list_index, element_index + 1)) + current_max = max(current_max, next_element) + + return smallest_range + + +if __name__ == "__main__": + from doctest import testmod + + testmod() + print(f"{smallest_range([[1, 2, 3], [1, 2, 3], [1, 2, 3]])}") # Output: [1, 1] From a73f37b2ecf29aeee1b0417ac53016f5ad0fbeee Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 4 Dec 2023 20:00:34 +0100 Subject: [PATCH 548/757] [pre-commit.ci] pre-commit autoupdate (#11195) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/tox-dev/pyproject-fmt: 1.5.1 → 1.5.3](https://github.com/tox-dev/pyproject-fmt/compare/1.5.1...1.5.3) * updating DIRECTORY.md --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- DIRECTORY.md | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 28f83a638d7b..5ec7a5765817 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -33,7 +33,7 @@ repos: - tomli - repo: https://github.com/tox-dev/pyproject-fmt - rev: "1.5.1" + rev: "1.5.3" hooks: - id: pyproject-fmt diff --git a/DIRECTORY.md b/DIRECTORY.md index ea0ba22bcc13..2ee72df37f3f 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -507,6 +507,7 @@ * [Minimum Coin Change](greedy_methods/minimum_coin_change.py) * [Minimum Waiting Time](greedy_methods/minimum_waiting_time.py) * [Optimal Merge Pattern](greedy_methods/optimal_merge_pattern.py) + * [Smallest Range](greedy_methods/smallest_range.py) ## Hashes * [Adler32](hashes/adler32.py) From c14a580c9e7340ee1d826a52af0f95c077b564b4 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 11 Dec 2023 19:27:15 +0100 Subject: [PATCH 549/757] [pre-commit.ci] pre-commit autoupdate (#11210) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.1.6 → v0.1.7](https://github.com/astral-sh/ruff-pre-commit/compare/v0.1.6...v0.1.7) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5ec7a5765817..9688f1cbb5fc 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.1.6 + rev: v0.1.7 hooks: - id: ruff From 2d0ed135a08dbe7da8c696d70ee7fb1a01f2cc91 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 18 Dec 2023 21:17:46 +0100 Subject: [PATCH 550/757] [pre-commit.ci] pre-commit autoupdate (#11215) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.1.7 → v0.1.8](https://github.com/astral-sh/ruff-pre-commit/compare/v0.1.7...v0.1.8) - [github.com/psf/black: 23.11.0 → 23.12.0](https://github.com/psf/black/compare/23.11.0...23.12.0) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9688f1cbb5fc..c8a11e38aeab 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,12 +16,12 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.1.7 + rev: v0.1.8 hooks: - id: ruff - repo: https://github.com/psf/black - rev: 23.11.0 + rev: 23.12.0 hooks: - id: black From b46fc1de04350f91971187d831d8e3292ea0bace Mon Sep 17 00:00:00 2001 From: Indrajeet Mishra Date: Wed, 20 Dec 2023 04:35:27 +0530 Subject: [PATCH 551/757] Corrected the Python Doctest command in equilibrium_index_in_array.py script (#11212) Co-authored-by: Indrajeet Mishra --- data_structures/arrays/equilibrium_index_in_array.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data_structures/arrays/equilibrium_index_in_array.py b/data_structures/arrays/equilibrium_index_in_array.py index 8802db6206bb..0717a45d9f4b 100644 --- a/data_structures/arrays/equilibrium_index_in_array.py +++ b/data_structures/arrays/equilibrium_index_in_array.py @@ -3,7 +3,7 @@ Reference: https://www.geeksforgeeks.org/equilibrium-index-of-an-array/ Python doctest can be run with the following command: -python -m doctest -v equilibrium_index.py +python -m doctest -v equilibrium_index_in_array.py Given a sequence arr[] of size n, this function returns an equilibrium index (if any) or -1 if no equilibrium index exists. From 7b9f82cc447c2d2ce91373c097bf610d5b0f906a Mon Sep 17 00:00:00 2001 From: Tushar Pamnani <121151091+tusharpamnani@users.noreply.github.com> Date: Wed, 20 Dec 2023 07:29:51 +0530 Subject: [PATCH 552/757] optimize quicksort implementation (#11196) * optimize quicksort implementation * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Apply suggestions from code review * Update quick_sort.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Tianyi Zheng --- sorts/quick_sort.py | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/sorts/quick_sort.py b/sorts/quick_sort.py index b79d3eac3e48..6b95fc144426 100644 --- a/sorts/quick_sort.py +++ b/sorts/quick_sort.py @@ -13,10 +13,10 @@ def quick_sort(collection: list) -> list: - """A pure Python implementation of quick sort algorithm + """A pure Python implementation of quicksort algorithm. :param collection: a mutable collection of comparable items - :return: the same collection ordered by ascending + :return: the same collection ordered in ascending order Examples: >>> quick_sort([0, 5, 3, 2, 2]) @@ -26,23 +26,26 @@ def quick_sort(collection: list) -> list: >>> quick_sort([-2, 5, 0, -45]) [-45, -2, 0, 5] """ + # Base case: if the collection has 0 or 1 elements, it is already sorted if len(collection) < 2: return collection - pivot_index = randrange(len(collection)) # Use random element as pivot - pivot = collection[pivot_index] - greater: list[int] = [] # All elements greater than pivot - lesser: list[int] = [] # All elements less than or equal to pivot - for element in collection[:pivot_index]: - (greater if element > pivot else lesser).append(element) + # Randomly select a pivot index and remove the pivot element from the collection + pivot_index = randrange(len(collection)) + pivot = collection.pop(pivot_index) - for element in collection[pivot_index + 1 :]: - (greater if element > pivot else lesser).append(element) + # Partition the remaining elements into two groups: lesser or equal, and greater + lesser = [item for item in collection if item <= pivot] + greater = [item for item in collection if item > pivot] + # Recursively sort the lesser and greater groups, and combine with the pivot return [*quick_sort(lesser), pivot, *quick_sort(greater)] if __name__ == "__main__": + # Get user input and convert it into a list of integers user_input = input("Enter numbers separated by a comma:\n").strip() unsorted = [int(item) for item in user_input.split(",")] + + # Print the result of sorting the user-provided list print(quick_sort(unsorted)) From 94c8e1ab73032d27bc8c60b733bb93393b9f1b02 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 25 Dec 2023 19:24:39 +0100 Subject: [PATCH 553/757] [pre-commit.ci] pre-commit autoupdate (#11223) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.1.8 → v0.1.9](https://github.com/astral-sh/ruff-pre-commit/compare/v0.1.8...v0.1.9) - [github.com/psf/black: 23.12.0 → 23.12.1](https://github.com/psf/black/compare/23.12.0...23.12.1) - [github.com/pre-commit/mirrors-mypy: v1.7.1 → v1.8.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.7.1...v1.8.0) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c8a11e38aeab..61ec3a54a69c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,12 +16,12 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.1.8 + rev: v0.1.9 hooks: - id: ruff - repo: https://github.com/psf/black - rev: 23.12.0 + rev: 23.12.1 hooks: - id: black @@ -51,7 +51,7 @@ repos: - id: validate-pyproject - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.7.1 + rev: v1.8.0 hooks: - id: mypy args: From 51c5c87b9ab4eb04c3825cd20cfdba0f31a098f5 Mon Sep 17 00:00:00 2001 From: Param Thakkar <128291516+ParamThakkar123@users.noreply.github.com> Date: Wed, 27 Dec 2023 14:05:29 +0530 Subject: [PATCH 554/757] File moved to neural_network/activation_functions (#11216) * added GELU activation functions file * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update gaussian_error_linear_unit.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update gaussian_error_linear_unit.py * Delete neural_network/activation_functions/gaussian_error_linear_unit.py * Rename maths/gaussian_error_linear_unit.py to neural_network/activation_functions/gaussian_error_linear_unit.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .../activation_functions}/gaussian_error_linear_unit.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {maths => neural_network/activation_functions}/gaussian_error_linear_unit.py (100%) diff --git a/maths/gaussian_error_linear_unit.py b/neural_network/activation_functions/gaussian_error_linear_unit.py similarity index 100% rename from maths/gaussian_error_linear_unit.py rename to neural_network/activation_functions/gaussian_error_linear_unit.py From 9caf4784aada17dc75348f77cc8c356df503c0f3 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 8 Jan 2024 19:16:25 +0100 Subject: [PATCH 555/757] [pre-commit.ci] pre-commit autoupdate (#11231) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/astral-sh/ruff-pre-commit: v0.1.9 → v0.1.11](https://github.com/astral-sh/ruff-pre-commit/compare/v0.1.9...v0.1.11) * updating DIRECTORY.md --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- DIRECTORY.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 61ec3a54a69c..0e06ba7a5250 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.1.9 + rev: v0.1.11 hooks: - id: ruff diff --git a/DIRECTORY.md b/DIRECTORY.md index 2ee72df37f3f..b5392fd09114 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -631,7 +631,6 @@ * [Floor](maths/floor.py) * [Gamma](maths/gamma.py) * [Gaussian](maths/gaussian.py) - * [Gaussian Error Linear Unit](maths/gaussian_error_linear_unit.py) * [Gcd Of N Numbers](maths/gcd_of_n_numbers.py) * [Germain Primes](maths/germain_primes.py) * [Greatest Common Divisor](maths/greatest_common_divisor.py) @@ -791,6 +790,7 @@ * Activation Functions * [Binary Step](neural_network/activation_functions/binary_step.py) * [Exponential Linear Unit](neural_network/activation_functions/exponential_linear_unit.py) + * [Gaussian Error Linear Unit](neural_network/activation_functions/gaussian_error_linear_unit.py) * [Leaky Rectified Linear Unit](neural_network/activation_functions/leaky_rectified_linear_unit.py) * [Mish](neural_network/activation_functions/mish.py) * [Rectified Linear Unit](neural_network/activation_functions/rectified_linear_unit.py) From 227944eb2933b22a102eb88703b4a0b648f39af5 Mon Sep 17 00:00:00 2001 From: Piotr Idzik <65706193+vil02@users.noreply.github.com> Date: Fri, 12 Jan 2024 17:12:15 +0100 Subject: [PATCH 556/757] fix: consider months and days in `years_old` (#11234) * fix: do not consider months in `calculate_age` * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update get_top_billionaires.py * Update get_top_billionaires.py * Update get_top_billionaires.py * TODAY = datetime.utcnow() * Update get_top_billionaires.py * Update build.yml --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- .github/workflows/build.yml | 2 +- web_programming/get_top_billionaires.py | 72 ++++++++++++------------- 2 files changed, 37 insertions(+), 37 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 60c1d6d119d0..1631feb2ba06 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: actions/setup-python@v4 + - uses: actions/setup-python@v5 with: python-version: 3.12 allow-prereleases: true diff --git a/web_programming/get_top_billionaires.py b/web_programming/get_top_billionaires.py index 6f986acb9181..703b635eef82 100644 --- a/web_programming/get_top_billionaires.py +++ b/web_programming/get_top_billionaires.py @@ -3,7 +3,7 @@ This works for some of us but fails for others. """ -from datetime import UTC, datetime, timedelta +from datetime import UTC, date, datetime import requests from rich import box @@ -11,8 +11,7 @@ from rich import table as rich_table LIMIT = 10 -TODAY = datetime.now() - +TODAY = datetime.now(tz=UTC) API_URL = ( "https://www.forbes.com/forbesapi/person/rtb/0/position/true.json" "?fields=personName,gender,source,countryOfCitizenship,birthDate,finalWorth" @@ -20,40 +19,40 @@ ) -def calculate_age(unix_date: float) -> str: - """Calculates age from given unix time format. +def years_old(birth_timestamp: int, today: date | None = None) -> int: + """ + Calculate the age in years based on the given birth date. Only the year, month, + and day are used in the calculation. The time of day is ignored. + + Args: + birth_timestamp: The date of birth. + today: (useful for writing tests) or if None then datetime.date.today(). Returns: - Age as string - - >>> from datetime import datetime, UTC - >>> years_since_create = datetime.now(tz=UTC).year - 2022 - >>> int(calculate_age(-657244800000)) - years_since_create - 73 - >>> int(calculate_age(46915200000)) - years_since_create - 51 + int: The age in years. + + Examples: + >>> today = date(2024, 1, 12) + >>> years_old(birth_timestamp=datetime(1959, 11, 20).timestamp(), today=today) + 64 + >>> years_old(birth_timestamp=datetime(1970, 2, 13).timestamp(), today=today) + 53 + >>> all( + ... years_old(datetime(today.year - i, 1, 12).timestamp(), today=today) == i + ... for i in range(1, 111) + ... ) + True """ - # Convert date from milliseconds to seconds - unix_date /= 1000 - - if unix_date < 0: - # Handle timestamp before epoch - epoch = datetime.fromtimestamp(0, tz=UTC) - seconds_since_epoch = (datetime.now(tz=UTC) - epoch).seconds - birthdate = ( - epoch - timedelta(seconds=abs(unix_date) - seconds_since_epoch) - ).date() - else: - birthdate = datetime.fromtimestamp(unix_date, tz=UTC).date() - return str( - TODAY.year - - birthdate.year - - ((TODAY.month, TODAY.day) < (birthdate.month, birthdate.day)) + today = today or TODAY.date() + birth_date = datetime.fromtimestamp(birth_timestamp, tz=UTC).date() + return (today.year - birth_date.year) - ( + (today.month, today.day) < (birth_date.month, birth_date.day) ) -def get_forbes_real_time_billionaires() -> list[dict[str, str]]: - """Get top 10 realtime billionaires using forbes API. +def get_forbes_real_time_billionaires() -> list[dict[str, int | str]]: + """ + Get the top 10 real-time billionaires using Forbes API. Returns: List of top 10 realtime billionaires data. @@ -66,21 +65,22 @@ def get_forbes_real_time_billionaires() -> list[dict[str, str]]: "Country": person["countryOfCitizenship"], "Gender": person["gender"], "Worth ($)": f"{person['finalWorth'] / 1000:.1f} Billion", - "Age": calculate_age(person["birthDate"]), + "Age": years_old(person["birthDate"]), } for person in response_json["personList"]["personsLists"] ] -def display_billionaires(forbes_billionaires: list[dict[str, str]]) -> None: - """Display Forbes real time billionaires in a rich table. +def display_billionaires(forbes_billionaires: list[dict[str, int | str]]) -> None: + """ + Display Forbes real-time billionaires in a rich table. Args: - forbes_billionaires (list): Forbes top 10 real time billionaires + forbes_billionaires (list): Forbes top 10 real-time billionaires """ table = rich_table.Table( - title=f"Forbes Top {LIMIT} Real Time Billionaires at {TODAY:%Y-%m-%d %H:%M}", + title=f"Forbes Top {LIMIT} Real-Time Billionaires at {TODAY:%Y-%m-%d %H:%M}", style="green", highlight=True, box=box.SQUARE, From a56f24e83d971c8f49d194b859b9b7acbf7df084 Mon Sep 17 00:00:00 2001 From: Piotr Idzik <65706193+vil02@users.noreply.github.com> Date: Fri, 12 Jan 2024 17:46:26 +0100 Subject: [PATCH 557/757] fix: use `GITHUB_ACTOR` in `git config` (#11233) --- .github/workflows/directory_writer.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/directory_writer.yml b/.github/workflows/directory_writer.yml index 702c15f1e29b..e92c93604904 100644 --- a/.github/workflows/directory_writer.yml +++ b/.github/workflows/directory_writer.yml @@ -15,8 +15,8 @@ jobs: - name: Write DIRECTORY.md run: | scripts/build_directory_md.py 2>&1 | tee DIRECTORY.md - git config --global user.name github-actions - git config --global user.email '${GITHUB_ACTOR}@users.noreply.github.com' + git config --global user.name "$GITHUB_ACTOR" + git config --global user.email "$GITHUB_ACTOR@users.noreply.github.com" git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/$GITHUB_REPOSITORY - name: Update DIRECTORY.md run: | From ffb93adf46971be35699e7642d79e90284b3c7f1 Mon Sep 17 00:00:00 2001 From: Piotr Idzik <65706193+vil02@users.noreply.github.com> Date: Fri, 12 Jan 2024 18:25:59 +0100 Subject: [PATCH 558/757] chore: update `actions/setup-python` to `v5` (#11236) --- .github/workflows/directory_writer.yml | 2 +- .github/workflows/project_euler.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/directory_writer.yml b/.github/workflows/directory_writer.yml index e92c93604904..55d89f455a25 100644 --- a/.github/workflows/directory_writer.yml +++ b/.github/workflows/directory_writer.yml @@ -9,7 +9,7 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 - - uses: actions/setup-python@v4 + - uses: actions/setup-python@v5 with: python-version: 3.x - name: Write DIRECTORY.md diff --git a/.github/workflows/project_euler.yml b/.github/workflows/project_euler.yml index 7bbccf76e192..59e1208a650d 100644 --- a/.github/workflows/project_euler.yml +++ b/.github/workflows/project_euler.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: actions/setup-python@v4 + - uses: actions/setup-python@v5 with: python-version: 3.x - name: Install pytest and pytest-cov @@ -27,7 +27,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: actions/setup-python@v4 + - uses: actions/setup-python@v5 with: python-version: 3.x - name: Install pytest and requests From 13559aee437dab6ed88ecb1a6737cb39094c9e24 Mon Sep 17 00:00:00 2001 From: Piotr Idzik <65706193+vil02@users.noreply.github.com> Date: Sat, 13 Jan 2024 12:24:58 +0100 Subject: [PATCH 559/757] style: use proper indentation in `ruff.yml` (#11237) * style: use proper indentation in `ruff.yml` * chore: run `prettier` on `yml` files * Update .pre-commit-config.yaml * Update .pre-commit-config.yaml * Update .pre-commit-config.yaml * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update .pre-commit-config.yaml * chore: run prettier on workflow files --------- Co-authored-by: Christian Clauss Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 10 +++++----- .github/workflows/ruff.yml | 6 +++--- .pre-commit-config.yaml | 8 +++++++- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1631feb2ba06..906edfdae1ed 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -25,10 +25,10 @@ jobs: - name: Run tests # TODO: #8818 Re-enable quantum tests run: pytest - --ignore=quantum/q_fourier_transform.py - --ignore=project_euler/ - --ignore=scripts/validate_solutions.py - --cov-report=term-missing:skip-covered - --cov=. . + --ignore=quantum/q_fourier_transform.py + --ignore=project_euler/ + --ignore=scripts/validate_solutions.py + --cov-report=term-missing:skip-covered + --cov=. . - if: ${{ success() }} run: scripts/build_directory_md.py 2>&1 | tee DIRECTORY.md diff --git a/.github/workflows/ruff.yml b/.github/workflows/ruff.yml index 496f1460e074..9ebabed3600a 100644 --- a/.github/workflows/ruff.yml +++ b/.github/workflows/ruff.yml @@ -11,6 +11,6 @@ jobs: ruff: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - run: pip install --user ruff - - run: ruff --output-format=github . + - uses: actions/checkout@v4 + - run: pip install --user ruff + - run: ruff --output-format=github . diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0e06ba7a5250..31e141049441 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -13,7 +13,7 @@ repos: - repo: https://github.com/MarcoGorelli/auto-walrus rev: v0.2.2 hooks: - - id: auto-walrus + - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.1.11 @@ -59,3 +59,9 @@ repos: - --install-types # See mirrors-mypy README.md - --non-interactive additional_dependencies: [types-requests] + + - repo: https://github.com/pre-commit/mirrors-prettier + rev: "v3.1.0" + hooks: + - id: prettier + types_or: [toml, yaml] From dd47651bfca06b31941827ed3f41517bf5718508 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 15 Jan 2024 19:19:36 +0100 Subject: [PATCH 560/757] [pre-commit.ci] pre-commit autoupdate (#11246) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.1.11 → v0.1.13](https://github.com/astral-sh/ruff-pre-commit/compare/v0.1.11...v0.1.13) - [github.com/tox-dev/pyproject-fmt: 1.5.3 → 1.6.0](https://github.com/tox-dev/pyproject-fmt/compare/1.5.3...1.6.0) - [github.com/pre-commit/mirrors-prettier: v3.1.0 → v4.0.0-alpha.8](https://github.com/pre-commit/mirrors-prettier/compare/v3.1.0...v4.0.0-alpha.8) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 31e141049441..97603510b426 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.1.11 + rev: v0.1.13 hooks: - id: ruff @@ -33,7 +33,7 @@ repos: - tomli - repo: https://github.com/tox-dev/pyproject-fmt - rev: "1.5.3" + rev: "1.6.0" hooks: - id: pyproject-fmt @@ -61,7 +61,7 @@ repos: additional_dependencies: [types-requests] - repo: https://github.com/pre-commit/mirrors-prettier - rev: "v3.1.0" + rev: "v4.0.0-alpha.8" hooks: - id: prettier types_or: [toml, yaml] From 4b6f688344b8347f555f10ca04b80ee36b5a1e82 Mon Sep 17 00:00:00 2001 From: AtomicVar Date: Tue, 16 Jan 2024 16:39:54 +0800 Subject: [PATCH 561/757] Use compiled black as the pre-commit formatter (#11247) * Use compiled black as the pre-commit formatter * ruff-format * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Keep GitHub Actions up to date with Dependabot --------- Co-authored-by: Christian Clauss Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .github/.github/dependabot.yml | 8 ++++++++ .pre-commit-config.yaml | 6 +----- audio_filters/butterworth_filter.py | 16 ++++++++++++---- conversions/convert_number_to_words.py | 2 +- digital_image_processing/filters/gabor_filter.py | 6 +++--- ...rian_path_and_circuit_for_undirected_graph.py | 2 +- physics/n_body_simulation.py | 4 +--- project_euler/problem_056/sol1.py | 4 +--- 8 files changed, 28 insertions(+), 20 deletions(-) create mode 100644 .github/.github/dependabot.yml diff --git a/.github/.github/dependabot.yml b/.github/.github/dependabot.yml new file mode 100644 index 000000000000..15e494ec867e --- /dev/null +++ b/.github/.github/dependabot.yml @@ -0,0 +1,8 @@ +# Keep GitHub Actions up to date with Dependabot... +# https://docs.github.com/en/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 97603510b426..38cc7c8fc3ff 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -19,11 +19,7 @@ repos: rev: v0.1.13 hooks: - id: ruff - - - repo: https://github.com/psf/black - rev: 23.12.1 - hooks: - - id: black + - id: ruff-format - repo: https://github.com/codespell-project/codespell rev: v2.2.6 diff --git a/audio_filters/butterworth_filter.py b/audio_filters/butterworth_filter.py index cffedb7a68fd..6449bc3f3dce 100644 --- a/audio_filters/butterworth_filter.py +++ b/audio_filters/butterworth_filter.py @@ -11,7 +11,9 @@ def make_lowpass( - frequency: int, samplerate: int, q_factor: float = 1 / sqrt(2) # noqa: B008 + frequency: int, + samplerate: int, + q_factor: float = 1 / sqrt(2), # noqa: B008 ) -> IIRFilter: """ Creates a low-pass filter @@ -39,7 +41,9 @@ def make_lowpass( def make_highpass( - frequency: int, samplerate: int, q_factor: float = 1 / sqrt(2) # noqa: B008 + frequency: int, + samplerate: int, + q_factor: float = 1 / sqrt(2), # noqa: B008 ) -> IIRFilter: """ Creates a high-pass filter @@ -67,7 +71,9 @@ def make_highpass( def make_bandpass( - frequency: int, samplerate: int, q_factor: float = 1 / sqrt(2) # noqa: B008 + frequency: int, + samplerate: int, + q_factor: float = 1 / sqrt(2), # noqa: B008 ) -> IIRFilter: """ Creates a band-pass filter @@ -96,7 +102,9 @@ def make_bandpass( def make_allpass( - frequency: int, samplerate: int, q_factor: float = 1 / sqrt(2) # noqa: B008 + frequency: int, + samplerate: int, + q_factor: float = 1 / sqrt(2), # noqa: B008 ) -> IIRFilter: """ Creates an all-pass filter diff --git a/conversions/convert_number_to_words.py b/conversions/convert_number_to_words.py index 0c428928b31d..dbab44c72e1f 100644 --- a/conversions/convert_number_to_words.py +++ b/conversions/convert_number_to_words.py @@ -41,7 +41,7 @@ def max_value(cls, system: str) -> int: >>> NumberingSystem.max_value("indian") == 10**19 - 1 True """ - match (system_enum := cls[system.upper()]): + match system_enum := cls[system.upper()]: case cls.SHORT: max_exp = system_enum.value[0][0] + 3 case cls.LONG: diff --git a/digital_image_processing/filters/gabor_filter.py b/digital_image_processing/filters/gabor_filter.py index 8f9212a35a79..aaec567f4c99 100644 --- a/digital_image_processing/filters/gabor_filter.py +++ b/digital_image_processing/filters/gabor_filter.py @@ -48,9 +48,9 @@ def gabor_filter_kernel( _y = -sin_theta * px + cos_theta * py # fill kernel - gabor[y, x] = np.exp( - -(_x**2 + gamma**2 * _y**2) / (2 * sigma**2) - ) * np.cos(2 * np.pi * _x / lambd + psi) + gabor[y, x] = np.exp(-(_x**2 + gamma**2 * _y**2) / (2 * sigma**2)) * np.cos( + 2 * np.pi * _x / lambd + psi + ) return gabor diff --git a/graphs/eulerian_path_and_circuit_for_undirected_graph.py b/graphs/eulerian_path_and_circuit_for_undirected_graph.py index 6b4ea8e21e8b..5b146eaa845b 100644 --- a/graphs/eulerian_path_and_circuit_for_undirected_graph.py +++ b/graphs/eulerian_path_and_circuit_for_undirected_graph.py @@ -56,7 +56,7 @@ def main(): g4 = {1: [2, 3], 2: [1, 3], 3: [1, 2]} g5 = { 1: [], - 2: [] + 2: [], # all degree is zero } max_node = 10 diff --git a/physics/n_body_simulation.py b/physics/n_body_simulation.py index 46330844df61..ec008784ba62 100644 --- a/physics/n_body_simulation.py +++ b/physics/n_body_simulation.py @@ -165,9 +165,7 @@ def update_system(self, delta_time: float) -> None: # Calculation of the distance using Pythagoras's theorem # Extra factor due to the softening technique - distance = (dif_x**2 + dif_y**2 + self.softening_factor) ** ( - 1 / 2 - ) + distance = (dif_x**2 + dif_y**2 + self.softening_factor) ** (1 / 2) # Newton's law of universal gravitation. force_x += ( diff --git a/project_euler/problem_056/sol1.py b/project_euler/problem_056/sol1.py index c772bec58692..828dbd3a8ddf 100644 --- a/project_euler/problem_056/sol1.py +++ b/project_euler/problem_056/sol1.py @@ -30,9 +30,7 @@ def solution(a: int = 100, b: int = 100) -> int: # RETURN the MAXIMUM from the list of SUMs of the list of INT converted from STR of # BASE raised to the POWER return max( - sum(int(x) for x in str(base**power)) - for base in range(a) - for power in range(b) + sum(int(x) for x in str(base**power)) for base in range(a) for power in range(b) ) From 0101dd42dc83f567bddebc386b17f2b4f6bbaa36 Mon Sep 17 00:00:00 2001 From: Ataf Fazledin Ahamed Date: Tue, 16 Jan 2024 14:43:33 +0600 Subject: [PATCH 562/757] Fixed Inappropriate Logical Expression (#11203) Signed-off-by: fazledyn-or --- data_structures/binary_tree/red_black_tree.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data_structures/binary_tree/red_black_tree.py b/data_structures/binary_tree/red_black_tree.py index 4ebe0e927ca0..fc299301da8a 100644 --- a/data_structures/binary_tree/red_black_tree.py +++ b/data_structures/binary_tree/red_black_tree.py @@ -451,7 +451,7 @@ def is_left(self) -> bool: """Returns true iff this node is the left child of its parent.""" if self.parent is None: return False - return self.parent.left is self.parent.left is self + return self.parent.left is self def is_right(self) -> bool: """Returns true iff this node is the right child of its parent.""" From 05a5cdacc3cfd9814ad6f5cb2d4dec86109b640a Mon Sep 17 00:00:00 2001 From: Suyash Dongre <109069262+Suyashd999@users.noreply.github.com> Date: Thu, 18 Jan 2024 18:09:27 +0530 Subject: [PATCH 563/757] Added doctest to skew_heap.py (#11147) * Added doctest to skew_heap.py * Update skew_heap.py * Update data_structures/heap/skew_heap.py Co-authored-by: Saptadeep Banerjee <69459134+imSanko@users.noreply.github.com> * Update skew_heap.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update skew_heap.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: Saptadeep Banerjee <69459134+imSanko@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- data_structures/heap/skew_heap.py | 45 +++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/data_structures/heap/skew_heap.py b/data_structures/heap/skew_heap.py index c4c13b08276a..0839db711cb1 100644 --- a/data_structures/heap/skew_heap.py +++ b/data_structures/heap/skew_heap.py @@ -21,14 +21,55 @@ def __init__(self, value: T) -> None: @property def value(self) -> T: - """Return the value of the node.""" + """ + Return the value of the node. + + >>> SkewNode(0).value + 0 + >>> SkewNode(3.14159).value + 3.14159 + >>> SkewNode("hello").value + 'hello' + >>> SkewNode(None).value + + >>> SkewNode(True).value + True + >>> SkewNode([]).value + [] + >>> SkewNode({}).value + {} + >>> SkewNode(set()).value + set() + >>> SkewNode(0.0).value + 0.0 + >>> SkewNode(-1e-10).value + -1e-10 + >>> SkewNode(10).value + 10 + >>> SkewNode(-10.5).value + -10.5 + >>> SkewNode().value + Traceback (most recent call last): + ... + TypeError: SkewNode.__init__() missing 1 required positional argument: 'value' + """ return self._value @staticmethod def merge( root1: SkewNode[T] | None, root2: SkewNode[T] | None ) -> SkewNode[T] | None: - """Merge 2 nodes together.""" + """ + Merge 2 nodes together. + >>> SkewNode.merge(SkewNode(10),SkewNode(-10.5)).value + -10.5 + >>> SkewNode.merge(SkewNode(10),SkewNode(10.5)).value + 10 + >>> SkewNode.merge(SkewNode(10),SkewNode(10)).value + 10 + >>> SkewNode.merge(SkewNode(-100),SkewNode(-10.5)).value + -100 + """ if not root1: return root2 From 3952ba703a5b84a37891a001037c5c366d20941a Mon Sep 17 00:00:00 2001 From: AtomicVar Date: Thu, 18 Jan 2024 20:41:29 +0800 Subject: [PATCH 564/757] Add categorical focal cross-entropy loss algorithm (#11248) --- machine_learning/loss_functions.py | 102 +++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/machine_learning/loss_functions.py b/machine_learning/loss_functions.py index 36a760326f3d..f05fa0cbe686 100644 --- a/machine_learning/loss_functions.py +++ b/machine_learning/loss_functions.py @@ -148,6 +148,108 @@ def categorical_cross_entropy( return -np.sum(y_true * np.log(y_pred)) +def categorical_focal_cross_entropy( + y_true: np.ndarray, + y_pred: np.ndarray, + alpha: np.ndarray = None, + gamma: float = 2.0, + epsilon: float = 1e-15, +) -> float: + """ + Calculate the mean categorical focal cross-entropy (CFCE) loss between true + labels and predicted probabilities for multi-class classification. + + CFCE loss is a generalization of binary focal cross-entropy for multi-class + classification. It addresses class imbalance by focusing on hard examples. + + CFCE = -Σ alpha * (1 - y_pred)**gamma * y_true * log(y_pred) + + Reference: [Lin et al., 2018](https://arxiv.org/pdf/1708.02002.pdf) + + Parameters: + - y_true: True labels in one-hot encoded form. + - y_pred: Predicted probabilities for each class. + - alpha: Array of weighting factors for each class. + - gamma: Focusing parameter for modulating the loss (default: 2.0). + - epsilon: Small constant to avoid numerical instability. + + Returns: + - The mean categorical focal cross-entropy loss. + + >>> true_labels = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]]) + >>> pred_probs = np.array([[0.9, 0.1, 0.0], [0.2, 0.7, 0.1], [0.0, 0.1, 0.9]]) + >>> alpha = np.array([0.6, 0.2, 0.7]) + >>> categorical_focal_cross_entropy(true_labels, pred_probs, alpha) + 0.0025966118981496423 + + >>> true_labels = np.array([[0, 1, 0], [0, 0, 1]]) + >>> pred_probs = np.array([[0.05, 0.95, 0], [0.1, 0.8, 0.1]]) + >>> alpha = np.array([0.25, 0.25, 0.25]) + >>> categorical_focal_cross_entropy(true_labels, pred_probs, alpha) + 0.23315276982014324 + + >>> true_labels = np.array([[1, 0], [0, 1]]) + >>> pred_probs = np.array([[0.9, 0.1, 0.0], [0.2, 0.7, 0.1]]) + >>> categorical_cross_entropy(true_labels, pred_probs) + Traceback (most recent call last): + ... + ValueError: Input arrays must have the same shape. + + >>> true_labels = np.array([[2, 0, 1], [1, 0, 0]]) + >>> pred_probs = np.array([[0.9, 0.1, 0.0], [0.2, 0.7, 0.1]]) + >>> categorical_focal_cross_entropy(true_labels, pred_probs) + Traceback (most recent call last): + ... + ValueError: y_true must be one-hot encoded. + + >>> true_labels = np.array([[1, 0, 1], [1, 0, 0]]) + >>> pred_probs = np.array([[0.9, 0.1, 0.0], [0.2, 0.7, 0.1]]) + >>> categorical_focal_cross_entropy(true_labels, pred_probs) + Traceback (most recent call last): + ... + ValueError: y_true must be one-hot encoded. + + >>> true_labels = np.array([[1, 0, 0], [0, 1, 0]]) + >>> pred_probs = np.array([[0.9, 0.1, 0.1], [0.2, 0.7, 0.1]]) + >>> categorical_focal_cross_entropy(true_labels, pred_probs) + Traceback (most recent call last): + ... + ValueError: Predicted probabilities must sum to approximately 1. + + >>> true_labels = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]]) + >>> pred_probs = np.array([[0.9, 0.1, 0.0], [0.2, 0.7, 0.1], [0.0, 0.1, 0.9]]) + >>> alpha = np.array([0.6, 0.2]) + >>> categorical_focal_cross_entropy(true_labels, pred_probs, alpha) + Traceback (most recent call last): + ... + ValueError: Length of alpha must match the number of classes. + """ + if y_true.shape != y_pred.shape: + raise ValueError("Shape of y_true and y_pred must be the same.") + + if alpha is None: + alpha = np.ones(y_true.shape[1]) + + if np.any((y_true != 0) & (y_true != 1)) or np.any(y_true.sum(axis=1) != 1): + raise ValueError("y_true must be one-hot encoded.") + + if len(alpha) != y_true.shape[1]: + raise ValueError("Length of alpha must match the number of classes.") + + if not np.all(np.isclose(np.sum(y_pred, axis=1), 1, rtol=epsilon, atol=epsilon)): + raise ValueError("Predicted probabilities must sum to approximately 1.") + + # Clip predicted probabilities to avoid log(0) + y_pred = np.clip(y_pred, epsilon, 1 - epsilon) + + # Calculate loss for each class and sum across classes + cfce_loss = -np.sum( + alpha * np.power(1 - y_pred, gamma) * y_true * np.log(y_pred), axis=1 + ) + + return np.mean(cfce_loss) + + def hinge_loss(y_true: np.ndarray, y_pred: np.ndarray) -> float: """ Calculate the mean hinge loss for between true labels and predicted probabilities From b01571dc4f5754d3da44b8a0b6dabb44986c666e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 22 Jan 2024 19:20:43 +0100 Subject: [PATCH 565/757] [pre-commit.ci] pre-commit autoupdate (#11255) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.1.13 → v0.1.14](https://github.com/astral-sh/ruff-pre-commit/compare/v0.1.13...v0.1.14) - [github.com/tox-dev/pyproject-fmt: 1.6.0 → 1.7.0](https://github.com/tox-dev/pyproject-fmt/compare/1.6.0...1.7.0) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 38cc7c8fc3ff..7fae092d043c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.1.13 + rev: v0.1.14 hooks: - id: ruff - id: ruff-format @@ -29,7 +29,7 @@ repos: - tomli - repo: https://github.com/tox-dev/pyproject-fmt - rev: "1.6.0" + rev: "1.7.0" hooks: - id: pyproject-fmt From b092d7755cf94b4758440b68bc97ac30154f4c55 Mon Sep 17 00:00:00 2001 From: Geoffrey Logovi <52314615+geoffreylgv@users.noreply.github.com> Date: Wed, 24 Jan 2024 06:15:39 +0000 Subject: [PATCH 566/757] fixes #11256 : computer vision link update in .computer_vision/README.md (#11257) --- computer_vision/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/computer_vision/README.md b/computer_vision/README.md index 8d2f4a130d05..1657128fd25e 100644 --- a/computer_vision/README.md +++ b/computer_vision/README.md @@ -8,4 +8,4 @@ Image processing and computer vision are a little different from each other. Ima While computer vision comes from modelling image processing using the techniques of machine learning, computer vision applies machine learning to recognize patterns for interpretation of images (much like the process of visual reasoning of human vision). * -* +* From c0e700c91c63c1b3ea50575b10a6c1665dfd6404 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 29 Jan 2024 21:00:37 +0100 Subject: [PATCH 567/757] [pre-commit.ci] pre-commit autoupdate (#11261) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/abravalheri/validate-pyproject: v0.15 → v0.16](https://github.com/abravalheri/validate-pyproject/compare/v0.15...v0.16) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7fae092d043c..0d13745a5a47 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -42,7 +42,7 @@ repos: pass_filenames: false - repo: https://github.com/abravalheri/validate-pyproject - rev: v0.15 + rev: v0.16 hooks: - id: validate-pyproject From c1d29ba459648bf8111e19e32988cb36ee8a94b0 Mon Sep 17 00:00:00 2001 From: AtomicVar Date: Tue, 30 Jan 2024 16:18:56 +0800 Subject: [PATCH 568/757] Add smooth l1 loss algorithm (#11239) --- machine_learning/loss_functions.py | 56 ++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/machine_learning/loss_functions.py b/machine_learning/loss_functions.py index f05fa0cbe686..16e5a3278b73 100644 --- a/machine_learning/loss_functions.py +++ b/machine_learning/loss_functions.py @@ -573,6 +573,62 @@ def perplexity_loss( return np.mean(perp_losses) +def smooth_l1_loss(y_true: np.ndarray, y_pred: np.ndarray, beta: float = 1.0) -> float: + """ + Calculate the Smooth L1 Loss between y_true and y_pred. + + The Smooth L1 Loss is less sensitive to outliers than the L2 Loss and is often used + in regression problems, such as object detection. + + Smooth L1 Loss = + 0.5 * (x - y)^2 / beta, if |x - y| < beta + |x - y| - 0.5 * beta, otherwise + + Reference: + https://pytorch.org/docs/stable/generated/torch.nn.SmoothL1Loss.html + + Args: + y_true: Array of true values. + y_pred: Array of predicted values. + beta: Specifies the threshold at which to change between L1 and L2 loss. + + Returns: + The calculated Smooth L1 Loss between y_true and y_pred. + + Raises: + ValueError: If the length of the two arrays is not the same. + + >>> y_true = np.array([3, 5, 2, 7]) + >>> y_pred = np.array([2.9, 4.8, 2.1, 7.2]) + >>> smooth_l1_loss(y_true, y_pred, 1.0) + 0.012500000000000022 + + >>> y_true = np.array([2, 4, 6]) + >>> y_pred = np.array([1, 5, 7]) + >>> smooth_l1_loss(y_true, y_pred, 1.0) + 0.5 + + >>> y_true = np.array([1, 3, 5, 7]) + >>> y_pred = np.array([1, 3, 5, 7]) + >>> smooth_l1_loss(y_true, y_pred, 1.0) + 0.0 + + >>> y_true = np.array([1, 3, 5]) + >>> y_pred = np.array([1, 3, 5, 7]) + >>> smooth_l1_loss(y_true, y_pred, 1.0) + Traceback (most recent call last): + ... + ValueError: The length of the two arrays should be the same. + """ + + if len(y_true) != len(y_pred): + raise ValueError("The length of the two arrays should be the same.") + + diff = np.abs(y_true - y_pred) + loss = np.where(diff < beta, 0.5 * diff**2 / beta, diff - 0.5 * beta) + return np.mean(loss) + + if __name__ == "__main__": import doctest From 8995f45cb505e9cb1aafe3b35c6a00d9aff5f871 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Thu, 1 Feb 2024 07:10:35 +0100 Subject: [PATCH 569/757] Rename .github/.github/dependabot.yml to .github/dependabot.yml (#11264) * Rename .github/.github/dependabot.yml to .github/dependabot.yml * runs-on: macos-14 # ubuntu-latest * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update build.yml --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .github/{.github => }/dependabot.yml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/{.github => }/dependabot.yml (100%) diff --git a/.github/.github/dependabot.yml b/.github/dependabot.yml similarity index 100% rename from .github/.github/dependabot.yml rename to .github/dependabot.yml From 6a169740e8c71c6c8236b09eb7b523895fedcfbd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Feb 2024 08:11:41 +0100 Subject: [PATCH 570/757] Bump actions/cache from 3 to 4 (#11265) Bumps [actions/cache](https://github.com/actions/cache) from 3 to 4. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 906edfdae1ed..a113b4608678 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,7 +14,7 @@ jobs: with: python-version: 3.12 allow-prereleases: true - - uses: actions/cache@v3 + - uses: actions/cache@v4 with: path: ~/.cache/pip key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }} From 4128f19170135cf7ccadb2afa8b2ab0a464c5765 Mon Sep 17 00:00:00 2001 From: Anthony Klarman <148516349+tonguegrease@users.noreply.github.com> Date: Fri, 2 Feb 2024 03:22:58 -0500 Subject: [PATCH 571/757] Fixed lines that needed to be uncommented after Hacktoberfest (#11267) * uncommented lines * uncommented lines * Update CODEOWNERS --------- Co-authored-by: Christian Clauss --- .github/CODEOWNERS | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index a0531cdeec69..d2ac43c7df31 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -21,15 +21,15 @@ # /cellular_automata/ -# /ciphers/ @cclauss # TODO: Uncomment this line after Hacktoberfest +# /ciphers/ # /compression/ # /computer_vision/ -# /conversions/ @cclauss # TODO: Uncomment this line after Hacktoberfest +# /conversions/ -# /data_structures/ @cclauss # TODO: Uncomment this line after Hacktoberfest +# /data_structures/ # /digital_image_processing/ @@ -67,7 +67,7 @@ # /neural_network/ -# /other/ @cclauss # TODO: Uncomment this line after Hacktoberfest +# /other/ # /project_euler/ @@ -81,7 +81,7 @@ # /sorts/ -# /strings/ @cclauss # TODO: Uncomment this line after Hacktoberfest +# /strings/ # /traversals/ From ed8d9209daff975eb3be6e0bf8cfa13e330347ca Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 5 Feb 2024 20:48:10 +0100 Subject: [PATCH 572/757] [pre-commit.ci] pre-commit autoupdate (#11275) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/astral-sh/ruff-pre-commit: v0.1.14 → v0.2.0](https://github.com/astral-sh/ruff-pre-commit/compare/v0.1.14...v0.2.0) * Upgrade pyproject.toml * Revert sudoku_solver.py RUF017 Avoid quadratic list summation --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- .pre-commit-config.yaml | 2 +- ciphers/mixed_keyword_cypher.py | 2 +- data_structures/arrays/sudoku_solver.py | 2 +- data_structures/linked_list/is_palindrome.py | 4 +--- .../filters/gaussian_filter.py | 4 +--- electronics/resistor_equivalence.py | 8 ++------ hashes/hamming_code.py | 18 ++++++------------ machine_learning/k_means_clust.py | 2 +- .../sequential_minimum_optimization.py | 2 +- neural_network/convolution_neural_network.py | 6 +++--- pyproject.toml | 14 +++++++------- 11 files changed, 25 insertions(+), 39 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0d13745a5a47..c29c6982643e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.1.14 + rev: v0.2.0 hooks: - id: ruff - id: ruff-format diff --git a/ciphers/mixed_keyword_cypher.py b/ciphers/mixed_keyword_cypher.py index b984808fced6..1b186108a73e 100644 --- a/ciphers/mixed_keyword_cypher.py +++ b/ciphers/mixed_keyword_cypher.py @@ -67,7 +67,7 @@ def mixed_keyword( if verbose: print(mapping) # create the encrypted text by mapping the plaintext to the modified alphabet - return "".join(mapping[char] if char in mapping else char for char in plaintext) + return "".join(mapping.get(char, char) for char in plaintext) if __name__ == "__main__": diff --git a/data_structures/arrays/sudoku_solver.py b/data_structures/arrays/sudoku_solver.py index 8d38bd7295ea..20ac32e3b071 100644 --- a/data_structures/arrays/sudoku_solver.py +++ b/data_structures/arrays/sudoku_solver.py @@ -22,7 +22,7 @@ def cross(items_a, items_b): + [cross(rs, cs) for rs in ("ABC", "DEF", "GHI") for cs in ("123", "456", "789")] ) units = {s: [u for u in unitlist if s in u] for s in squares} -peers = {s: set(sum(units[s], [])) - {s} for s in squares} +peers = {s: set(sum(units[s], [])) - {s} for s in squares} # noqa: RUF017 def test(): diff --git a/data_structures/linked_list/is_palindrome.py b/data_structures/linked_list/is_palindrome.py index f949d9a2f201..da788e3e5045 100644 --- a/data_structures/linked_list/is_palindrome.py +++ b/data_structures/linked_list/is_palindrome.py @@ -171,11 +171,9 @@ def is_palindrome_dict(head: ListNode | None) -> bool: if len(v) % 2 != 0: middle += 1 else: - step = 0 - for i in range(len(v)): + for step, i in enumerate(range(len(v))): if v[i] + v[len(v) - 1 - step] != checksum: return False - step += 1 if middle > 1: return False return True diff --git a/digital_image_processing/filters/gaussian_filter.py b/digital_image_processing/filters/gaussian_filter.py index 87fa67fb65ea..634d836e5edc 100644 --- a/digital_image_processing/filters/gaussian_filter.py +++ b/digital_image_processing/filters/gaussian_filter.py @@ -22,11 +22,9 @@ def gaussian_filter(image, k_size, sigma): # im2col, turn the k_size*k_size pixels into a row and np.vstack all rows image_array = zeros((dst_height * dst_width, k_size * k_size)) - row = 0 - for i, j in product(range(dst_height), range(dst_width)): + for row, (i, j) in enumerate(product(range(dst_height), range(dst_width))): window = ravel(image[i : i + k_size, j : j + k_size]) image_array[row, :] = window - row += 1 # turn the kernel into shape(k*k, 1) gaussian_kernel = gen_gaussian_kernel(k_size, sigma) diff --git a/electronics/resistor_equivalence.py b/electronics/resistor_equivalence.py index 55e7f2d6b5d2..c4ea7d4b757e 100644 --- a/electronics/resistor_equivalence.py +++ b/electronics/resistor_equivalence.py @@ -20,13 +20,11 @@ def resistor_parallel(resistors: list[float]) -> float: """ first_sum = 0.00 - index = 0 - for resistor in resistors: + for index, resistor in enumerate(resistors): if resistor <= 0: msg = f"Resistor at index {index} has a negative or zero value!" raise ValueError(msg) first_sum += 1 / float(resistor) - index += 1 return 1 / first_sum @@ -44,13 +42,11 @@ def resistor_series(resistors: list[float]) -> float: ValueError: Resistor at index 2 has a negative value! """ sum_r = 0.00 - index = 0 - for resistor in resistors: + for index, resistor in enumerate(resistors): sum_r += resistor if resistor < 0: msg = f"Resistor at index {index} has a negative value!" raise ValueError(msg) - index += 1 return sum_r diff --git a/hashes/hamming_code.py b/hashes/hamming_code.py index 4a6efcf23f63..b34fdd4c7a74 100644 --- a/hashes/hamming_code.py +++ b/hashes/hamming_code.py @@ -123,8 +123,7 @@ def emitter_converter(size_par, data): # Bit counter one for a given parity cont_bo = 0 # counter to control the loop reading - cont_loop = 0 - for x in data_ord: + for cont_loop, x in enumerate(data_ord): if x is not None: try: aux = (bin_pos[cont_loop])[-1 * (bp)] @@ -132,7 +131,6 @@ def emitter_converter(size_par, data): aux = "0" if aux == "1" and x == "1": cont_bo += 1 - cont_loop += 1 parity.append(cont_bo % 2) qtd_bp += 1 @@ -164,10 +162,10 @@ def receptor_converter(size_par, data): parity_received = [] data_output = [] - for x in range(1, len(data) + 1): + for i, item in enumerate(data, 1): # Performs a template of bit positions - who should be given, # and who should be parity - if qtd_bp < size_par and (np.log(x) / np.log(2)).is_integer(): + if qtd_bp < size_par and (np.log(i) / np.log(2)).is_integer(): data_out_gab.append("P") qtd_bp = qtd_bp + 1 else: @@ -175,10 +173,9 @@ def receptor_converter(size_par, data): # Sorts the data to the new output size if data_out_gab[-1] == "D": - data_output.append(data[cont_data]) + data_output.append(item) else: - parity_received.append(data[cont_data]) - cont_data += 1 + parity_received.append(item) # -----------calculates the parity with the data data_out = [] @@ -215,9 +212,7 @@ def receptor_converter(size_par, data): for bp in range(1, size_par + 1): # Bit counter one for a certain parity cont_bo = 0 - # Counter to control loop reading - cont_loop = 0 - for x in data_ord: + for cont_loop, x in enumerate(data_ord): if x is not None: try: aux = (bin_pos[cont_loop])[-1 * (bp)] @@ -225,7 +220,6 @@ def receptor_converter(size_par, data): aux = "0" if aux == "1" and x == "1": cont_bo += 1 - cont_loop += 1 parity.append(str(cont_bo % 2)) qtd_bp += 1 diff --git a/machine_learning/k_means_clust.py b/machine_learning/k_means_clust.py index ebad66ac8e8f..4a219edc3bb1 100644 --- a/machine_learning/k_means_clust.py +++ b/machine_learning/k_means_clust.py @@ -237,7 +237,7 @@ def report_generator( [ ("sum", "sum"), ("mean_with_zeros", lambda x: np.mean(np.nan_to_num(x))), - ("mean_without_zeros", lambda x: x.replace(0, np.NaN).mean()), + ("mean_without_zeros", lambda x: x.replace(0, np.nan).mean()), ( "mean_25-75", lambda x: np.mean( diff --git a/machine_learning/sequential_minimum_optimization.py b/machine_learning/sequential_minimum_optimization.py index 9e2304859f8d..9ee8c52fb2e9 100644 --- a/machine_learning/sequential_minimum_optimization.py +++ b/machine_learning/sequential_minimum_optimization.py @@ -589,7 +589,7 @@ def plot_partition_boundary( ax.contour( xrange, yrange, - np.mat(grid).T, + np.asmatrix(grid).T, levels=(-1, 0, 1), linestyles=("--", "-", "--"), linewidths=(1, 1, 1), diff --git a/neural_network/convolution_neural_network.py b/neural_network/convolution_neural_network.py index f2e88fe7bd88..e9726a0cb4a7 100644 --- a/neural_network/convolution_neural_network.py +++ b/neural_network/convolution_neural_network.py @@ -41,11 +41,11 @@ def __init__( self.rate_weight = rate_w self.rate_thre = rate_t self.w_conv1 = [ - np.mat(-1 * np.random.rand(self.conv1[0], self.conv1[0]) + 0.5) + np.asmatrix(-1 * np.random.rand(self.conv1[0], self.conv1[0]) + 0.5) for i in range(self.conv1[1]) ] - self.wkj = np.mat(-1 * np.random.rand(self.num_bp3, self.num_bp2) + 0.5) - self.vji = np.mat(-1 * np.random.rand(self.num_bp2, self.num_bp1) + 0.5) + self.wkj = np.asmatrix(-1 * np.random.rand(self.num_bp3, self.num_bp2) + 0.5) + self.vji = np.asmatrix(-1 * np.random.rand(self.num_bp2, self.num_bp1) + 0.5) self.thre_conv1 = -2 * np.random.rand(self.conv1[1]) + 1 self.thre_bp2 = -2 * np.random.rand(self.num_bp2) + 1 self.thre_bp3 = -2 * np.random.rand(self.num_bp3) + 1 diff --git a/pyproject.toml b/pyproject.toml index c7163dc78371..2e7da519da8b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [tool.ruff] -ignore = [ # `ruff rule S101` for a description of that rule +lint.ignore = [ # `ruff rule S101` for a description of that rule "ARG001", # Unused function argument `amount` -- FIX ME? "B904", # Within an `except` clause, raise exceptions with `raise ... from err` -- FIX ME "B905", # `zip()` without an explicit `strict=` parameter -- FIX ME @@ -31,7 +31,7 @@ ignore = [ # `ruff rule S101` for a description of that rule "SLF001", # Private member accessed: `_Iterator` -- FIX ME "UP038", # Use `X | Y` in `{}` call instead of `(X, Y)` -- DO NOT FIX ] -select = [ # https://beta.ruff.rs/docs/rules +lint.select = [ # https://beta.ruff.rs/docs/rules "A", # flake8-builtins "ARG", # flake8-unused-arguments "ASYNC", # flake8-async @@ -84,13 +84,13 @@ select = [ # https://beta.ruff.rs/docs/rules # "TCH", # flake8-type-checking # "TRY", # tryceratops ] -show-source = true -target-version = "py311" +output-format = "full" +target-version = "py312" -[tool.ruff.mccabe] # DO NOT INCREASE THIS VALUE +[tool.ruff.lint.mccabe] # DO NOT INCREASE THIS VALUE max-complexity = 17 # default: 10 -[tool.ruff.per-file-ignores] +[tool.ruff.lint.per-file-ignores] "arithmetic_analysis/newton_raphson.py" = ["PGH001"] "audio_filters/show_response.py" = ["ARG002"] "data_structures/binary_tree/binary_search_tree_recursive.py" = ["BLE001"] @@ -110,7 +110,7 @@ max-complexity = 17 # default: 10 "project_euler/problem_099/sol1.py" = ["SIM115"] "sorts/external_sort.py" = ["SIM115"] -[tool.ruff.pylint] # DO NOT INCREASE THESE VALUES +[tool.ruff.lint.pylint] # DO NOT INCREASE THESE VALUES allow-magic-value-types = ["float", "int", "str"] max-args = 10 # default: 5 max-branches = 20 # default: 12 From 5d6846b2bd1fa16edfc89025e00f69a802774faa Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 12 Feb 2024 21:53:20 +0100 Subject: [PATCH 573/757] [pre-commit.ci] pre-commit autoupdate (#11292) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.2.0 → v0.2.1](https://github.com/astral-sh/ruff-pre-commit/compare/v0.2.0...v0.2.1) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c29c6982643e..79d7d58d0863 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.2.0 + rev: v0.2.1 hooks: - id: ruff - id: ruff-format From c6ca1942e14a6e88c7ea1b96ef3a6d17ca843f52 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 19 Feb 2024 23:00:06 +0100 Subject: [PATCH 574/757] [pre-commit.ci] pre-commit autoupdate (#11296) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.2.1 → v0.2.2](https://github.com/astral-sh/ruff-pre-commit/compare/v0.2.1...v0.2.2) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 79d7d58d0863..be8364a7fc0b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.2.1 + rev: v0.2.2 hooks: - id: ruff - id: ruff-format From fd27953d44416a5f1541ed6e6923844b6070d086 Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Tue, 12 Mar 2024 11:35:49 +0300 Subject: [PATCH 575/757] Reenable files when TensorFlow supports the current Python (#11318) * Remove python_version < '3.12' for tensorflow * Reenable dynamic_programming/k_means_clustering_tensorflow.py * updating DIRECTORY.md * Try to fix ruff * Try to fix ruff * Try to fix ruff * Try to fix ruff * Try to fix ruff * Reenable machine_learning/lstm/lstm_prediction.py * updating DIRECTORY.md * Try to fix ruff * Reenable computer_vision/cnn_classification.py * updating DIRECTORY.md * Reenable neural_network/input_data.py * updating DIRECTORY.md * Try to fix ruff * Try to fix ruff * Try to fix mypy * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Try to fix ruff * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: MaximSmolskiy Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- DIRECTORY.md | 5 +++++ ciphers/rsa_cipher.py | 17 +++++++---------- ...on.py.DISABLED.txt => cnn_classification.py} | 0 ...LED.txt => k_means_clustering_tensorflow.py} | 0 ...ction.py.DISABLED.txt => lstm_prediction.py} | 8 ++++---- ...put_data.py.DEPRECATED.txt => input_data.py} | 9 +++++++-- other/lfu_cache.py | 5 +++-- requirements.txt | 2 +- 8 files changed, 27 insertions(+), 19 deletions(-) rename computer_vision/{cnn_classification.py.DISABLED.txt => cnn_classification.py} (100%) rename dynamic_programming/{k_means_clustering_tensorflow.py.DISABLED.txt => k_means_clustering_tensorflow.py} (100%) rename machine_learning/lstm/{lstm_prediction.py.DISABLED.txt => lstm_prediction.py} (90%) rename neural_network/{input_data.py.DEPRECATED.txt => input_data.py} (98%) diff --git a/DIRECTORY.md b/DIRECTORY.md index b5392fd09114..2f828aa512a9 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -134,6 +134,7 @@ * [Run Length Encoding](compression/run_length_encoding.py) ## Computer Vision + * [Cnn Classification](computer_vision/cnn_classification.py) * [Flip Augmentation](computer_vision/flip_augmentation.py) * [Haralick Descriptors](computer_vision/haralick_descriptors.py) * [Harris Corner](computer_vision/harris_corner.py) @@ -344,6 +345,7 @@ * [Floyd Warshall](dynamic_programming/floyd_warshall.py) * [Integer Partition](dynamic_programming/integer_partition.py) * [Iterating Through Submasks](dynamic_programming/iterating_through_submasks.py) + * [K Means Clustering Tensorflow](dynamic_programming/k_means_clustering_tensorflow.py) * [Knapsack](dynamic_programming/knapsack.py) * [Largest Divisible Subset](dynamic_programming/largest_divisible_subset.py) * [Longest Common Subsequence](dynamic_programming/longest_common_subsequence.py) @@ -571,6 +573,8 @@ * [Local Weighted Learning](machine_learning/local_weighted_learning/local_weighted_learning.py) * [Logistic Regression](machine_learning/logistic_regression.py) * [Loss Functions](machine_learning/loss_functions.py) + * Lstm + * [Lstm Prediction](machine_learning/lstm/lstm_prediction.py) * [Mfcc](machine_learning/mfcc.py) * [Multilayer Perceptron Classifier](machine_learning/multilayer_perceptron_classifier.py) * [Polynomial Regression](machine_learning/polynomial_regression.py) @@ -801,6 +805,7 @@ * [Swish](neural_network/activation_functions/swish.py) * [Back Propagation Neural Network](neural_network/back_propagation_neural_network.py) * [Convolution Neural Network](neural_network/convolution_neural_network.py) + * [Input Data](neural_network/input_data.py) * [Simple Neural Network](neural_network/simple_neural_network.py) ## Other diff --git a/ciphers/rsa_cipher.py b/ciphers/rsa_cipher.py index 9c41cdc5d472..3bc2ebe5fc74 100644 --- a/ciphers/rsa_cipher.py +++ b/ciphers/rsa_cipher.py @@ -76,11 +76,9 @@ def encrypt_and_write_to_file( key_size, n, e = read_key_file(key_filename) if key_size < block_size * 8: sys.exit( - "ERROR: Block size is {} bits and key size is {} bits. The RSA cipher " - "requires the block size to be equal to or greater than the key size. " - "Either decrease the block size or use different keys.".format( - block_size * 8, key_size - ) + f"ERROR: Block size is {block_size * 8} bits and key size is {key_size} " + "bits. The RSA cipher requires the block size to be equal to or greater " + "than the key size. Either decrease the block size or use different keys." ) encrypted_blocks = [str(i) for i in encrypt_message(message, (n, e), block_size)] @@ -102,11 +100,10 @@ def read_from_file_and_decrypt(message_filename: str, key_filename: str) -> str: if key_size < block_size * 8: sys.exit( - "ERROR: Block size is {} bits and key size is {} bits. The RSA cipher " - "requires the block size to be equal to or greater than the key size. " - "Did you specify the correct key file and encrypted file?".format( - block_size * 8, key_size - ) + f"ERROR: Block size is {block_size * 8} bits and key size is {key_size} " + "bits. The RSA cipher requires the block size to be equal to or greater " + "than the key size. Did you specify the correct key file and encrypted " + "file?" ) encrypted_blocks = [] diff --git a/computer_vision/cnn_classification.py.DISABLED.txt b/computer_vision/cnn_classification.py similarity index 100% rename from computer_vision/cnn_classification.py.DISABLED.txt rename to computer_vision/cnn_classification.py diff --git a/dynamic_programming/k_means_clustering_tensorflow.py.DISABLED.txt b/dynamic_programming/k_means_clustering_tensorflow.py similarity index 100% rename from dynamic_programming/k_means_clustering_tensorflow.py.DISABLED.txt rename to dynamic_programming/k_means_clustering_tensorflow.py diff --git a/machine_learning/lstm/lstm_prediction.py.DISABLED.txt b/machine_learning/lstm/lstm_prediction.py similarity index 90% rename from machine_learning/lstm/lstm_prediction.py.DISABLED.txt rename to machine_learning/lstm/lstm_prediction.py index 16530e935ea7..ecbd451266ad 100644 --- a/machine_learning/lstm/lstm_prediction.py.DISABLED.txt +++ b/machine_learning/lstm/lstm_prediction.py @@ -17,11 +17,11 @@ make sure you set the price column on line number 21. Here we use a dataset which have the price on 3rd column. """ - df = pd.read_csv("sample_data.csv", header=None) - len_data = df.shape[:1][0] + sample_data = pd.read_csv("sample_data.csv", header=None) + len_data = sample_data.shape[:1][0] # If you're using some other dataset input the target column - actual_data = df.iloc[:, 1:2] - actual_data = actual_data.values.reshape(len_data, 1) + actual_data = sample_data.iloc[:, 1:2] + actual_data = actual_data.to_numpy().reshape(len_data, 1) actual_data = MinMaxScaler().fit_transform(actual_data) look_back = 10 forward_days = 5 diff --git a/neural_network/input_data.py.DEPRECATED.txt b/neural_network/input_data.py similarity index 98% rename from neural_network/input_data.py.DEPRECATED.txt rename to neural_network/input_data.py index a58e64907e45..2128449c03e9 100644 --- a/neural_network/input_data.py.DEPRECATED.txt +++ b/neural_network/input_data.py @@ -18,9 +18,9 @@ """ -import collections import gzip import os +import typing import urllib import numpy @@ -28,7 +28,12 @@ from tensorflow.python.platform import gfile from tensorflow.python.util.deprecation import deprecated -_Datasets = collections.namedtuple("_Datasets", ["train", "validation", "test"]) + +class _Datasets(typing.NamedTuple): + train: "_DataSet" + validation: "_DataSet" + test: "_DataSet" + # CVDF mirror of http://yann.lecun.com/exdb/mnist/ DEFAULT_SOURCE_URL = "https://storage.googleapis.com/cvdf-datasets/mnist/" diff --git a/other/lfu_cache.py b/other/lfu_cache.py index b68ba3a4605c..788fdf19bb60 100644 --- a/other/lfu_cache.py +++ b/other/lfu_cache.py @@ -24,8 +24,9 @@ def __init__(self, key: T | None, val: U | None): self.prev: DoubleLinkedListNode[T, U] | None = None def __repr__(self) -> str: - return "Node: key: {}, val: {}, freq: {}, has next: {}, has prev: {}".format( - self.key, self.val, self.freq, self.next is not None, self.prev is not None + return ( + f"Node: key: {self.key}, val: {self.val}, freq: {self.freq}, " + f"has next: {self.next is not None}, has prev: {self.prev is not None}" ) diff --git a/requirements.txt b/requirements.txt index 8937f6bb0dae..bb3d671393b9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -17,7 +17,7 @@ rich scikit-learn statsmodels sympy -tensorflow ; python_version < '3.12' +tensorflow tweepy # yulewalker # uncomment once audio_filters/equal_loudness_filter.py is fixed typing_extensions From 5f95d6f805088aa7e21849b1ba97cdcf059333a9 Mon Sep 17 00:00:00 2001 From: guangwu Date: Tue, 12 Mar 2024 16:40:32 +0800 Subject: [PATCH 576/757] fix: function name typo (#11319) * fix: function name typo Signed-off-by: guoguangwu * lfu_cache.py: Use f-strings * rsa_cipher.py: Use f-strings --------- Signed-off-by: guoguangwu Co-authored-by: Christian Clauss --- ciphers/rsa_cipher.py | 3 +-- machine_learning/astar.py | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/ciphers/rsa_cipher.py b/ciphers/rsa_cipher.py index 3bc2ebe5fc74..ac9782a49fff 100644 --- a/ciphers/rsa_cipher.py +++ b/ciphers/rsa_cipher.py @@ -102,8 +102,7 @@ def read_from_file_and_decrypt(message_filename: str, key_filename: str) -> str: sys.exit( f"ERROR: Block size is {block_size * 8} bits and key size is {key_size} " "bits. The RSA cipher requires the block size to be equal to or greater " - "than the key size. Did you specify the correct key file and encrypted " - "file?" + "than the key size. Were the correct key file and encrypted file specified?" ) encrypted_blocks = [] diff --git a/machine_learning/astar.py b/machine_learning/astar.py index 7a60ed225a2d..ff5208266343 100644 --- a/machine_learning/astar.py +++ b/machine_learning/astar.py @@ -57,7 +57,7 @@ def __init__(self, world_size=(5, 5)): def show(self): print(self.w) - def get_neigbours(self, cell): + def get_neighbours(self, cell): """ Return the neighbours of cell """ @@ -110,7 +110,7 @@ def astar(world, start, goal): _closed.append(_open.pop(min_f)) if current == goal: break - for n in world.get_neigbours(current): + for n in world.get_neighbours(current): for c in _closed: if c == n: continue From bc8df6de3143b417c4d174200fd7edd0dbba4ce3 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 13 Mar 2024 07:52:41 +0100 Subject: [PATCH 577/757] [pre-commit.ci] pre-commit autoupdate (#11322) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/astral-sh/ruff-pre-commit: v0.2.2 → v0.3.2](https://github.com/astral-sh/ruff-pre-commit/compare/v0.2.2...v0.3.2) - [github.com/pre-commit/mirrors-mypy: v1.8.0 → v1.9.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.8.0...v1.9.0) * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 4 +- backtracking/all_combinations.py | 7 +- backtracking/all_permutations.py | 9 ++- backtracking/all_subsequences.py | 1 + backtracking/coloring.py | 8 +- backtracking/hamiltonian_cycle.py | 10 +-- backtracking/minimax.py | 1 + backtracking/n_queens.py | 11 +-- backtracking/n_queens_math.py | 1 + backtracking/sudoku.py | 1 + backtracking/sum_of_subsets.py | 11 +-- boolean_algebra/nor_gate.py | 1 + cellular_automata/conways_game_of_life.py | 1 + cellular_automata/game_of_life.py | 3 +- cellular_automata/nagel_schrekenberg.py | 1 + ciphers/a1z26.py | 1 + ciphers/atbash.py | 3 +- ciphers/base32.py | 1 + ciphers/enigma_machine2.py | 1 + ciphers/fractionated_morse_cipher.py | 1 + ciphers/hill_cipher.py | 1 + ciphers/permutation_cipher.py | 1 + ciphers/rail_fence_cipher.py | 2 +- ciphers/rsa_factorization.py | 1 + ciphers/xor_cipher.py | 33 ++++---- compression/burrows_wheeler.py | 1 + compression/lempel_ziv.py | 4 +- compression/lempel_ziv_decompress.py | 4 +- compression/lz77.py | 1 - computer_vision/haralick_descriptors.py | 1 + computer_vision/horn_schunck.py | 16 ++-- conversions/decimal_to_hexadecimal.py | 2 +- conversions/prefix_conversions.py | 1 + conversions/temperature_conversions.py | 2 +- .../arrays/pairs_with_given_sum.py | 1 + data_structures/arrays/sparse_table.py | 15 ++-- data_structures/arrays/sudoku_solver.py | 1 + data_structures/binary_tree/avl_tree.py | 1 + .../binary_tree/binary_search_tree.py | 1 + .../binary_search_tree_recursive.py | 1 + .../binary_tree/binary_tree_node_sum.py | 1 - .../binary_tree/diameter_of_binary_tree.py | 1 + .../flatten_binarytree_to_linkedlist.py | 1 + .../binary_tree/floor_and_ceiling.py | 1 + data_structures/binary_tree/is_sorted.py | 1 + data_structures/binary_tree/is_sum_tree.py | 1 + .../binary_tree/merge_two_binary_trees.py | 1 + .../binary_tree/mirror_binary_tree.py | 1 + .../binary_tree/non_recursive_segment_tree.py | 1 + .../number_of_possible_binary_trees.py | 1 + data_structures/binary_tree/red_black_tree.py | 1 + .../binary_tree/segment_tree_other.py | 1 + data_structures/binary_tree/symmetric_tree.py | 1 + data_structures/binary_tree/wavelet_tree.py | 1 + data_structures/disjoint_set/disjoint_set.py | 4 +- data_structures/hashing/bloom_filter.py | 1 + data_structures/hashing/double_hash.py | 1 + data_structures/hashing/hash_map.py | 1 + .../hashing/number_theory/prime_numbers.py | 2 +- data_structures/linked_list/__init__.py | 1 + .../linked_list/merge_two_lists.py | 1 + data_structures/linked_list/skip_list.py | 1 + data_structures/queue/double_ended_queue.py | 1 + data_structures/queue/linked_queue.py | 3 +- .../queue/queue_on_pseudo_stack.py | 1 + .../stacks/dijkstras_two_stack_algorithm.py | 1 + .../stacks/stack_with_singly_linked_list.py | 3 +- .../convert_to_negative.py | 3 +- digital_image_processing/dithering/burkes.py | 1 + .../filters/bilateral_filter.py | 1 + .../filters/gaussian_filter.py | 1 + .../filters/median_filter.py | 1 + .../histogram_stretch.py | 1 + digital_image_processing/resize/resize.py | 3 +- digital_image_processing/sepia.py | 3 +- .../test_digital_image_processing.py | 1 + divide_and_conquer/convex_hull.py | 1 + divide_and_conquer/kth_order_statistic.py | 1 + divide_and_conquer/max_subarray.py | 1 + divide_and_conquer/peak.py | 1 + dynamic_programming/all_construct.py | 1 + dynamic_programming/bitmask.py | 1 + dynamic_programming/fast_fibonacci.py | 1 + .../iterating_through_submasks.py | 1 + .../longest_increasing_subsequence.py | 1 + .../matrix_chain_multiplication.py | 1 + dynamic_programming/max_subarray_sum.py | 1 + electronics/charging_capacitor.py | 1 + electronics/charging_inductor.py | 1 + electronics/resistor_color_code.py | 1 + financial/exponential_moving_average.py | 16 ++-- financial/simple_moving_average.py | 1 + fractals/koch_snowflake.py | 1 - fractals/mandelbrot.py | 1 - fractals/sierpinski_triangle.py | 1 + graphs/bi_directional_dijkstra.py | 1 - graphs/bidirectional_a_star.py | 1 + graphs/bidirectional_breadth_first_search.py | 1 + graphs/boruvka.py | 37 ++++----- graphs/breadth_first_search.py | 3 +- graphs/breadth_first_search_2.py | 1 + graphs/breadth_first_search_shortest_path.py | 1 + .../breadth_first_search_shortest_path_2.py | 9 ++- ...dth_first_search_zero_one_shortest_path.py | 1 + graphs/deep_clone_graph.py | 1 + graphs/depth_first_search.py | 1 + graphs/depth_first_search_2.py | 2 +- graphs/dijkstra.py | 1 + graphs/even_tree.py | 1 + graphs/frequent_pattern_graph_miner.py | 1 + graphs/graph_adjacency_list.py | 1 + graphs/graph_adjacency_matrix.py | 1 + graphs/graphs_floyd_warshall.py | 4 +- graphs/minimum_spanning_tree_prims2.py | 1 + graphs/page_rank.py | 1 + graphs/prim.py | 4 +- greedy_methods/gas_station.py | 1 + hashes/adler32.py | 12 +-- hashes/hamming_code.py | 76 +++++++++---------- hashes/luhn.py | 3 +- hashes/sdbm.py | 30 ++++---- hashes/sha1.py | 1 + knapsack/knapsack.py | 5 +- knapsack/tests/test_knapsack.py | 1 + linear_algebra/gaussian_elimination.py | 1 - linear_algebra/jacobi_iteration_method.py | 1 + linear_algebra/lu_decomposition.py | 1 + linear_algebra/src/conjugate_gradient.py | 1 + linear_algebra/src/lib.py | 13 ++-- linear_algebra/src/rayleigh_quotient.py | 1 + linear_algebra/src/test_linear_algebra.py | 1 + linear_algebra/src/transformations_2d.py | 1 + linear_programming/simplex.py | 1 + machine_learning/apriori_algorithm.py | 1 + machine_learning/astar.py | 1 + machine_learning/automatic_differentiation.py | 1 + machine_learning/data_transformations.py | 1 + machine_learning/decision_tree.py | 1 + machine_learning/frequent_pattern_growth.py | 1 + machine_learning/gradient_descent.py | 1 + machine_learning/k_means_clust.py | 1 + .../linear_discriminant_analysis.py | 65 ++++++++-------- machine_learning/linear_regression.py | 1 + machine_learning/logistic_regression.py | 1 + machine_learning/lstm/lstm_prediction.py | 9 ++- machine_learning/mfcc.py | 1 - machine_learning/self_organizing_map.py | 1 + .../sequential_minimum_optimization.py | 1 - machine_learning/similarity_search.py | 1 + maths/allocation_number.py | 1 + maths/area.py | 1 + maths/area_under_curve.py | 1 + maths/basic_maths.py | 1 + maths/binomial_distribution.py | 3 +- maths/chinese_remainder_theorem.py | 1 + maths/continued_fraction.py | 1 - maths/entropy.py | 1 + maths/gamma.py | 1 + maths/gaussian.py | 1 + maths/interquartile_range.py | 1 + maths/is_square_free.py | 1 + maths/karatsuba.py | 2 +- maths/lucas_lehmer_primality_test.py | 14 ++-- maths/maclaurin_series.py | 1 + maths/max_sum_sliding_window.py | 1 + maths/modular_exponential.py | 8 +- maths/monte_carlo.py | 1 + maths/numerical_analysis/adams_bashforth.py | 1 + maths/numerical_analysis/nevilles_method.py | 14 ++-- maths/numerical_analysis/newton_raphson.py | 1 + .../numerical_integration.py | 1 + maths/numerical_analysis/runge_kutta_gills.py | 1 + maths/numerical_analysis/secant_method.py | 1 + maths/prime_factors.py | 1 + maths/series/geometric_series.py | 1 - maths/series/p_series.py | 1 - maths/sieve_of_eratosthenes.py | 1 + maths/solovay_strassen_primality_test.py | 1 - maths/special_numbers/armstrong_numbers.py | 1 + maths/special_numbers/weird_number.py | 1 + maths/tanh.py | 1 + maths/triplet_sum.py | 1 + maths/two_pointer.py | 1 + maths/two_sum.py | 1 + maths/volume.py | 1 + matrix/matrix_multiplication_recursion.py | 1 + networking_flow/ford_fulkerson.py | 1 + .../activation_functions/binary_step.py | 1 - .../rectified_linear_unit.py | 1 + .../soboleva_modified_hyperbolic_tangent.py | 1 - .../back_propagation_neural_network.py | 1 + neural_network/convolution_neural_network.py | 27 +++---- neural_network/input_data.py | 1 - other/davis_putnam_logemann_loveland.py | 1 + other/fischer_yates_shuffle.py | 1 + other/gauss_easter.py | 1 + other/majority_vote_algorithm.py | 1 + other/quine.py | 1 + other/word_search.py | 1 - .../archimedes_principle_of_buoyant_force.py | 1 - physics/center_of_mass.py | 1 + physics/in_static_equilibrium.py | 1 + physics/n_body_simulation.py | 1 - physics/rms_speed_of_molecule.py | 1 - project_euler/problem_002/sol4.py | 1 + project_euler/problem_003/sol1.py | 1 + project_euler/problem_006/sol3.py | 1 + project_euler/problem_007/sol2.py | 1 + project_euler/problem_007/sol3.py | 1 + project_euler/problem_008/sol2.py | 1 + project_euler/problem_008/sol3.py | 1 + project_euler/problem_010/sol2.py | 1 + project_euler/problem_013/sol1.py | 1 + project_euler/problem_014/sol2.py | 1 + project_euler/problem_015/sol1.py | 1 + project_euler/problem_018/solution.py | 1 + project_euler/problem_020/sol2.py | 1 + project_euler/problem_020/sol3.py | 1 + project_euler/problem_021/sol1.py | 1 + project_euler/problem_022/sol1.py | 1 + project_euler/problem_022/sol2.py | 1 + project_euler/problem_024/sol1.py | 1 + project_euler/problem_025/sol2.py | 1 + project_euler/problem_030/sol1.py | 3 +- project_euler/problem_032/sol32.py | 1 + project_euler/problem_033/sol1.py | 1 + project_euler/problem_035/sol1.py | 1 + project_euler/problem_036/sol1.py | 1 + project_euler/problem_038/sol1.py | 1 + project_euler/problem_041/sol1.py | 1 + project_euler/problem_042/solution42.py | 1 + project_euler/problem_043/sol1.py | 1 - project_euler/problem_050/sol1.py | 1 + project_euler/problem_051/sol1.py | 1 + project_euler/problem_053/sol1.py | 1 + project_euler/problem_054/sol1.py | 1 + project_euler/problem_058/sol1.py | 1 + project_euler/problem_059/sol1.py | 1 + project_euler/problem_067/sol1.py | 1 + project_euler/problem_067/sol2.py | 1 + project_euler/problem_070/sol1.py | 1 + project_euler/problem_074/sol1.py | 1 - project_euler/problem_074/sol2.py | 1 + project_euler/problem_077/sol1.py | 1 + project_euler/problem_079/sol1.py | 1 + project_euler/problem_080/sol1.py | 1 + project_euler/problem_081/sol1.py | 1 + project_euler/problem_085/sol1.py | 1 + project_euler/problem_086/sol1.py | 1 - project_euler/problem_091/sol1.py | 1 - project_euler/problem_101/sol1.py | 1 + project_euler/problem_102/sol1.py | 1 + project_euler/problem_107/sol1.py | 1 + project_euler/problem_123/sol1.py | 1 + project_euler/problem_144/sol1.py | 1 - project_euler/problem_145/sol1.py | 1 + project_euler/problem_173/sol1.py | 1 - project_euler/problem_180/sol1.py | 1 + project_euler/problem_191/sol1.py | 1 - project_euler/problem_203/sol1.py | 1 + project_euler/problem_551/sol1.py | 1 - scheduling/highest_response_ratio_next.py | 1 + scheduling/job_sequence_with_deadline.py | 1 + .../non_preemptive_shortest_job_first.py | 1 - scheduling/round_robin.py | 1 + scheduling/shortest_job_first.py | 1 + searches/binary_search.py | 1 + searches/binary_tree_traversal.py | 1 + searches/fibonacci_search.py | 1 + searches/jump_search.py | 3 +- searches/quick_select.py | 1 + searches/simple_binary_search.py | 1 + searches/tabu_search.py | 1 + searches/ternary_search.py | 1 + sorts/bitonic_sort.py | 1 + sorts/bucket_sort.py | 1 + sorts/dutch_national_flag_sort.py | 1 - sorts/insertion_sort.py | 3 +- sorts/intro_sort.py | 1 + sorts/msd_radix_sort.py | 1 + sorts/odd_even_transposition_parallel.py | 1 + sorts/pigeon_sort.py | 15 ++-- sorts/quick_sort.py | 1 + sorts/radix_sort.py | 1 + sorts/recursive_insertion_sort.py | 1 + sorts/slowsort.py | 1 + sorts/tree_sort.py | 1 + strings/boyer_moore_search.py | 1 + strings/check_anagrams.py | 1 + strings/top_k_frequent_words.py | 1 - web_programming/co2_emission.py | 1 + web_programming/emails_from_url.py | 1 + web_programming/fetch_github_info.py | 1 + web_programming/fetch_jobs.py | 1 + web_programming/get_amazon_product_data.py | 1 - web_programming/recaptcha_verification.py | 1 + web_programming/search_books_by_isbn.py | 1 + 297 files changed, 498 insertions(+), 295 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index be8364a7fc0b..a17c4c323c30 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.2.2 + rev: v0.3.2 hooks: - id: ruff - id: ruff-format @@ -47,7 +47,7 @@ repos: - id: validate-pyproject - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.8.0 + rev: v1.9.0 hooks: - id: mypy args: diff --git a/backtracking/all_combinations.py b/backtracking/all_combinations.py index 407304948c39..390decf3a05b 100644 --- a/backtracking/all_combinations.py +++ b/backtracking/all_combinations.py @@ -1,9 +1,10 @@ """ - In this problem, we want to determine all possible combinations of k - numbers out of 1 ... n. We use backtracking to solve this problem. +In this problem, we want to determine all possible combinations of k +numbers out of 1 ... n. We use backtracking to solve this problem. - Time complexity: O(C(n,k)) which is O(n choose k) = O((n!/(k! * (n - k)!))), +Time complexity: O(C(n,k)) which is O(n choose k) = O((n!/(k! * (n - k)!))), """ + from __future__ import annotations from itertools import combinations diff --git a/backtracking/all_permutations.py b/backtracking/all_permutations.py index ff8a53e0dd0e..c483cd62c99b 100644 --- a/backtracking/all_permutations.py +++ b/backtracking/all_permutations.py @@ -1,10 +1,11 @@ """ - In this problem, we want to determine all possible permutations - of the given sequence. We use backtracking to solve this problem. +In this problem, we want to determine all possible permutations +of the given sequence. We use backtracking to solve this problem. - Time complexity: O(n! * n), - where n denotes the length of the given sequence. +Time complexity: O(n! * n), +where n denotes the length of the given sequence. """ + from __future__ import annotations diff --git a/backtracking/all_subsequences.py b/backtracking/all_subsequences.py index c465fc542407..7844a829d046 100644 --- a/backtracking/all_subsequences.py +++ b/backtracking/all_subsequences.py @@ -5,6 +5,7 @@ Time complexity: O(2^n), where n denotes the length of the given sequence. """ + from __future__ import annotations from typing import Any diff --git a/backtracking/coloring.py b/backtracking/coloring.py index 9d539de8a3c4..f10cdbcf9d26 100644 --- a/backtracking/coloring.py +++ b/backtracking/coloring.py @@ -1,9 +1,9 @@ """ - Graph Coloring also called "m coloring problem" - consists of coloring a given graph with at most m colors - such that no adjacent vertices are assigned the same color +Graph Coloring also called "m coloring problem" +consists of coloring a given graph with at most m colors +such that no adjacent vertices are assigned the same color - Wikipedia: https://en.wikipedia.org/wiki/Graph_coloring +Wikipedia: https://en.wikipedia.org/wiki/Graph_coloring """ diff --git a/backtracking/hamiltonian_cycle.py b/backtracking/hamiltonian_cycle.py index e9916f83f861..f6e4212e47f4 100644 --- a/backtracking/hamiltonian_cycle.py +++ b/backtracking/hamiltonian_cycle.py @@ -1,10 +1,10 @@ """ - A Hamiltonian cycle (Hamiltonian circuit) is a graph cycle - through a graph that visits each node exactly once. - Determining whether such paths and cycles exist in graphs - is the 'Hamiltonian path problem', which is NP-complete. +A Hamiltonian cycle (Hamiltonian circuit) is a graph cycle +through a graph that visits each node exactly once. +Determining whether such paths and cycles exist in graphs +is the 'Hamiltonian path problem', which is NP-complete. - Wikipedia: https://en.wikipedia.org/wiki/Hamiltonian_path +Wikipedia: https://en.wikipedia.org/wiki/Hamiltonian_path """ diff --git a/backtracking/minimax.py b/backtracking/minimax.py index 6dece2990a1c..4eef90b75483 100644 --- a/backtracking/minimax.py +++ b/backtracking/minimax.py @@ -7,6 +7,7 @@ leaves of game tree is stored in scores[] height is maximum height of Game tree """ + from __future__ import annotations import math diff --git a/backtracking/n_queens.py b/backtracking/n_queens.py index 2cd8c703fc72..81668b17a0ac 100644 --- a/backtracking/n_queens.py +++ b/backtracking/n_queens.py @@ -1,12 +1,13 @@ """ - The nqueens problem is of placing N queens on a N * N - chess board such that no queen can attack any other queens placed - on that chess board. - This means that one queen cannot have any other queen on its horizontal, vertical and - diagonal lines. +The nqueens problem is of placing N queens on a N * N +chess board such that no queen can attack any other queens placed +on that chess board. +This means that one queen cannot have any other queen on its horizontal, vertical and +diagonal lines. """ + from __future__ import annotations solution = [] diff --git a/backtracking/n_queens_math.py b/backtracking/n_queens_math.py index f3b08ab0a05f..287d1f090373 100644 --- a/backtracking/n_queens_math.py +++ b/backtracking/n_queens_math.py @@ -75,6 +75,7 @@ for another one or vice versa. """ + from __future__ import annotations diff --git a/backtracking/sudoku.py b/backtracking/sudoku.py index 6e4e3e8780f2..8f5459c76d45 100644 --- a/backtracking/sudoku.py +++ b/backtracking/sudoku.py @@ -9,6 +9,7 @@ have solved the puzzle. else, we backtrack and place another number in that cell and repeat this process. """ + from __future__ import annotations Matrix = list[list[int]] diff --git a/backtracking/sum_of_subsets.py b/backtracking/sum_of_subsets.py index c5e23321cb0c..f34d3ca34339 100644 --- a/backtracking/sum_of_subsets.py +++ b/backtracking/sum_of_subsets.py @@ -1,11 +1,12 @@ """ - The sum-of-subsetsproblem states that a set of non-negative integers, and a - value M, determine all possible subsets of the given set whose summation sum - equal to given M. +The sum-of-subsetsproblem states that a set of non-negative integers, and a +value M, determine all possible subsets of the given set whose summation sum +equal to given M. - Summation of the chosen numbers must be equal to given number M and one number - can be used only once. +Summation of the chosen numbers must be equal to given number M and one number +can be used only once. """ + from __future__ import annotations diff --git a/boolean_algebra/nor_gate.py b/boolean_algebra/nor_gate.py index 0c8ab1c0af61..d4d6f0da23ea 100644 --- a/boolean_algebra/nor_gate.py +++ b/boolean_algebra/nor_gate.py @@ -12,6 +12,7 @@ Code provided by Akshaj Vishwanathan https://www.geeksforgeeks.org/logic-gates-in-python """ + from collections.abc import Callable diff --git a/cellular_automata/conways_game_of_life.py b/cellular_automata/conways_game_of_life.py index 84f4d5be40da..364a34c3aba6 100644 --- a/cellular_automata/conways_game_of_life.py +++ b/cellular_automata/conways_game_of_life.py @@ -2,6 +2,7 @@ Conway's Game of Life implemented in Python. https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life """ + from __future__ import annotations from PIL import Image diff --git a/cellular_automata/game_of_life.py b/cellular_automata/game_of_life.py index d691a2b73af0..67e647d6475b 100644 --- a/cellular_automata/game_of_life.py +++ b/cellular_automata/game_of_life.py @@ -26,7 +26,8 @@ 4. Any dead cell with exactly three live neighbours be- comes a live cell, as if by reproduction. - """ +""" + import random import sys diff --git a/cellular_automata/nagel_schrekenberg.py b/cellular_automata/nagel_schrekenberg.py index 3fd6afca0153..bcdca902afee 100644 --- a/cellular_automata/nagel_schrekenberg.py +++ b/cellular_automata/nagel_schrekenberg.py @@ -24,6 +24,7 @@ >>> simulate(construct_highway(5, 2, -2), 3, 0, 2) [[0, -1, 0, -1, 0], [0, -1, 0, -1, -1], [0, -1, -1, 1, -1], [-1, 1, -1, 0, -1]] """ + from random import randint, random diff --git a/ciphers/a1z26.py b/ciphers/a1z26.py index 0f0eb7c5c083..a1377ea6d397 100644 --- a/ciphers/a1z26.py +++ b/ciphers/a1z26.py @@ -5,6 +5,7 @@ https://www.dcode.fr/letter-number-cipher http://bestcodes.weebly.com/a1z26.html """ + from __future__ import annotations diff --git a/ciphers/atbash.py b/ciphers/atbash.py index 0a86a800c51a..4e8f663ed02d 100644 --- a/ciphers/atbash.py +++ b/ciphers/atbash.py @@ -1,4 +1,5 @@ -""" https://en.wikipedia.org/wiki/Atbash """ +"""https://en.wikipedia.org/wiki/Atbash""" + import string diff --git a/ciphers/base32.py b/ciphers/base32.py index 1924d1e185d7..911afa2452c0 100644 --- a/ciphers/base32.py +++ b/ciphers/base32.py @@ -3,6 +3,7 @@ https://en.wikipedia.org/wiki/Base32 """ + B32_CHARSET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567" diff --git a/ciphers/enigma_machine2.py b/ciphers/enigma_machine2.py index ec0d44e4a6c6..163aa7172c11 100644 --- a/ciphers/enigma_machine2.py +++ b/ciphers/enigma_machine2.py @@ -14,6 +14,7 @@ Created by TrapinchO """ + from __future__ import annotations RotorPositionT = tuple[int, int, int] diff --git a/ciphers/fractionated_morse_cipher.py b/ciphers/fractionated_morse_cipher.py index c1d5dc6d50aa..6c4c415abac1 100644 --- a/ciphers/fractionated_morse_cipher.py +++ b/ciphers/fractionated_morse_cipher.py @@ -8,6 +8,7 @@ http://practicalcryptography.com/ciphers/fractionated-morse-cipher/ """ + import string MORSE_CODE_DICT = { diff --git a/ciphers/hill_cipher.py b/ciphers/hill_cipher.py index 1201fda901e5..ea337a72dc04 100644 --- a/ciphers/hill_cipher.py +++ b/ciphers/hill_cipher.py @@ -35,6 +35,7 @@ https://www.youtube.com/watch?v=4RhLNDqcjpA """ + import string import numpy diff --git a/ciphers/permutation_cipher.py b/ciphers/permutation_cipher.py index c3f3fd1f7f94..9e1c64a7b4ea 100644 --- a/ciphers/permutation_cipher.py +++ b/ciphers/permutation_cipher.py @@ -7,6 +7,7 @@ For more info: https://www.nku.edu/~christensen/1402%20permutation%20ciphers.pdf """ + import random diff --git a/ciphers/rail_fence_cipher.py b/ciphers/rail_fence_cipher.py index 47ee7db89831..5b2311a115e4 100644 --- a/ciphers/rail_fence_cipher.py +++ b/ciphers/rail_fence_cipher.py @@ -1,4 +1,4 @@ -""" https://en.wikipedia.org/wiki/Rail_fence_cipher """ +"""https://en.wikipedia.org/wiki/Rail_fence_cipher""" def encrypt(input_string: str, key: int) -> str: diff --git a/ciphers/rsa_factorization.py b/ciphers/rsa_factorization.py index 9ee52777ed83..0a358a4fc2d4 100644 --- a/ciphers/rsa_factorization.py +++ b/ciphers/rsa_factorization.py @@ -7,6 +7,7 @@ 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 diff --git a/ciphers/xor_cipher.py b/ciphers/xor_cipher.py index e30955d41ff1..24d88a0fd588 100644 --- a/ciphers/xor_cipher.py +++ b/ciphers/xor_cipher.py @@ -1,21 +1,22 @@ """ - author: Christian Bender - date: 21.12.2017 - class: XORCipher - - This class implements the XOR-cipher algorithm and provides - some useful methods for encrypting and decrypting strings and - files. - - Overview about methods - - - encrypt : list of char - - decrypt : list of char - - encrypt_string : str - - decrypt_string : str - - encrypt_file : boolean - - decrypt_file : boolean +author: Christian Bender +date: 21.12.2017 +class: XORCipher + +This class implements the XOR-cipher algorithm and provides +some useful methods for encrypting and decrypting strings and +files. + +Overview about methods + +- encrypt : list of char +- decrypt : list of char +- encrypt_string : str +- decrypt_string : str +- encrypt_file : boolean +- decrypt_file : boolean """ + from __future__ import annotations diff --git a/compression/burrows_wheeler.py b/compression/burrows_wheeler.py index 52bb045d9398..ce493a70c8f9 100644 --- a/compression/burrows_wheeler.py +++ b/compression/burrows_wheeler.py @@ -10,6 +10,7 @@ original character. The BWT is thus a "free" method of improving the efficiency of text compression algorithms, costing only some extra computation. """ + from __future__ import annotations from typing import TypedDict diff --git a/compression/lempel_ziv.py b/compression/lempel_ziv.py index ea6f33944a91..ac3f0c6cfc06 100644 --- a/compression/lempel_ziv.py +++ b/compression/lempel_ziv.py @@ -1,6 +1,6 @@ """ - One of the several implementations of Lempel–Ziv–Welch compression algorithm - https://en.wikipedia.org/wiki/Lempel%E2%80%93Ziv%E2%80%93Welch +One of the several implementations of Lempel–Ziv–Welch compression algorithm +https://en.wikipedia.org/wiki/Lempel%E2%80%93Ziv%E2%80%93Welch """ import math diff --git a/compression/lempel_ziv_decompress.py b/compression/lempel_ziv_decompress.py index ddedc3d6d32a..0e49c83fb790 100644 --- a/compression/lempel_ziv_decompress.py +++ b/compression/lempel_ziv_decompress.py @@ -1,6 +1,6 @@ """ - One of the several implementations of Lempel–Ziv–Welch decompression algorithm - https://en.wikipedia.org/wiki/Lempel%E2%80%93Ziv%E2%80%93Welch +One of the several implementations of Lempel–Ziv–Welch decompression algorithm +https://en.wikipedia.org/wiki/Lempel%E2%80%93Ziv%E2%80%93Welch """ import math diff --git a/compression/lz77.py b/compression/lz77.py index 1b201c59f186..09b8b021e9d5 100644 --- a/compression/lz77.py +++ b/compression/lz77.py @@ -28,7 +28,6 @@ en.wikipedia.org/wiki/LZ77_and_LZ78 """ - from dataclasses import dataclass __version__ = "0.1" diff --git a/computer_vision/haralick_descriptors.py b/computer_vision/haralick_descriptors.py index 007421e34263..712bd49668f8 100644 --- a/computer_vision/haralick_descriptors.py +++ b/computer_vision/haralick_descriptors.py @@ -2,6 +2,7 @@ https://en.wikipedia.org/wiki/Image_texture https://en.wikipedia.org/wiki/Co-occurrence_matrix#Application_to_image_analysis """ + import imageio.v2 as imageio import numpy as np diff --git a/computer_vision/horn_schunck.py b/computer_vision/horn_schunck.py index b63e0268294c..f33b5b1c794b 100644 --- a/computer_vision/horn_schunck.py +++ b/computer_vision/horn_schunck.py @@ -1,12 +1,12 @@ """ - The Horn-Schunck method estimates the optical flow for every single pixel of - a sequence of images. - It works by assuming brightness constancy between two consecutive frames - and smoothness in the optical flow. - - Useful resources: - Wikipedia: https://en.wikipedia.org/wiki/Horn%E2%80%93Schunck_method - Paper: http://image.diku.dk/imagecanon/material/HornSchunckOptical_Flow.pdf +The Horn-Schunck method estimates the optical flow for every single pixel of +a sequence of images. +It works by assuming brightness constancy between two consecutive frames +and smoothness in the optical flow. + +Useful resources: +Wikipedia: https://en.wikipedia.org/wiki/Horn%E2%80%93Schunck_method +Paper: http://image.diku.dk/imagecanon/material/HornSchunckOptical_Flow.pdf """ from typing import SupportsIndex diff --git a/conversions/decimal_to_hexadecimal.py b/conversions/decimal_to_hexadecimal.py index b1fb4f082242..ee79592de5ca 100644 --- a/conversions/decimal_to_hexadecimal.py +++ b/conversions/decimal_to_hexadecimal.py @@ -1,4 +1,4 @@ -""" Convert Base 10 (Decimal) Values to Hexadecimal Representations """ +"""Convert Base 10 (Decimal) Values to Hexadecimal Representations""" # set decimal value for each hexadecimal digit values = { diff --git a/conversions/prefix_conversions.py b/conversions/prefix_conversions.py index 06b759e355a7..714677f3b242 100644 --- a/conversions/prefix_conversions.py +++ b/conversions/prefix_conversions.py @@ -1,6 +1,7 @@ """ Convert International System of Units (SI) and Binary prefixes """ + from __future__ import annotations from enum import Enum diff --git a/conversions/temperature_conversions.py b/conversions/temperature_conversions.py index f7af6c8f1e2b..dde1d2f0f166 100644 --- a/conversions/temperature_conversions.py +++ b/conversions/temperature_conversions.py @@ -1,4 +1,4 @@ -""" Convert between different units of temperature """ +"""Convert between different units of temperature""" def celsius_to_fahrenheit(celsius: float, ndigits: int = 2) -> float: diff --git a/data_structures/arrays/pairs_with_given_sum.py b/data_structures/arrays/pairs_with_given_sum.py index c4a5ceeae456..b27bd78e1e0f 100644 --- a/data_structures/arrays/pairs_with_given_sum.py +++ b/data_structures/arrays/pairs_with_given_sum.py @@ -6,6 +6,7 @@ https://practice.geeksforgeeks.org/problems/count-pairs-with-given-sum5022/0 """ + from itertools import combinations diff --git a/data_structures/arrays/sparse_table.py b/data_structures/arrays/sparse_table.py index a15d5649e712..4606fe908607 100644 --- a/data_structures/arrays/sparse_table.py +++ b/data_structures/arrays/sparse_table.py @@ -1,15 +1,16 @@ """ - Sparse table is a data structure that allows answering range queries on - a static number list, i.e. the elements do not change throughout all the queries. +Sparse table is a data structure that allows answering range queries on +a static number list, i.e. the elements do not change throughout all the queries. - The implementation below will solve the problem of Range Minimum Query: - Finding the minimum value of a subset [L..R] of a static number list. +The implementation below will solve the problem of Range Minimum Query: +Finding the minimum value of a subset [L..R] of a static number list. - Overall time complexity: O(nlogn) - Overall space complexity: O(nlogn) +Overall time complexity: O(nlogn) +Overall space complexity: O(nlogn) - Wikipedia link: https://en.wikipedia.org/wiki/Range_minimum_query +Wikipedia link: https://en.wikipedia.org/wiki/Range_minimum_query """ + from math import log2 diff --git a/data_structures/arrays/sudoku_solver.py b/data_structures/arrays/sudoku_solver.py index 20ac32e3b071..c9dffcde2379 100644 --- a/data_structures/arrays/sudoku_solver.py +++ b/data_structures/arrays/sudoku_solver.py @@ -3,6 +3,7 @@ only minimal changes to work with modern versions of Python. If you have improvements, please make them in a separate file. """ + import random import time diff --git a/data_structures/binary_tree/avl_tree.py b/data_structures/binary_tree/avl_tree.py index 4c1fb17afe86..041ed7e36d16 100644 --- a/data_structures/binary_tree/avl_tree.py +++ b/data_structures/binary_tree/avl_tree.py @@ -5,6 +5,7 @@ For testing run: python avl_tree.py """ + from __future__ import annotations import math diff --git a/data_structures/binary_tree/binary_search_tree.py b/data_structures/binary_tree/binary_search_tree.py index 9071f03dcc8c..08a60a12065d 100644 --- a/data_structures/binary_tree/binary_search_tree.py +++ b/data_structures/binary_tree/binary_search_tree.py @@ -88,6 +88,7 @@ >>> not t True """ + from __future__ import annotations from collections.abc import Iterable, Iterator diff --git a/data_structures/binary_tree/binary_search_tree_recursive.py b/data_structures/binary_tree/binary_search_tree_recursive.py index 13b9b392175c..6af1b053f42c 100644 --- a/data_structures/binary_tree/binary_search_tree_recursive.py +++ b/data_structures/binary_tree/binary_search_tree_recursive.py @@ -7,6 +7,7 @@ To run an example: python binary_search_tree_recursive.py """ + from __future__ import annotations import unittest diff --git a/data_structures/binary_tree/binary_tree_node_sum.py b/data_structures/binary_tree/binary_tree_node_sum.py index 5a13e74e3c9f..066617b616c4 100644 --- a/data_structures/binary_tree/binary_tree_node_sum.py +++ b/data_structures/binary_tree/binary_tree_node_sum.py @@ -8,7 +8,6 @@ frames that could be in memory is `n` """ - from __future__ import annotations from collections.abc import Iterator diff --git a/data_structures/binary_tree/diameter_of_binary_tree.py b/data_structures/binary_tree/diameter_of_binary_tree.py index bbe70b028d24..75e5e7373323 100644 --- a/data_structures/binary_tree/diameter_of_binary_tree.py +++ b/data_structures/binary_tree/diameter_of_binary_tree.py @@ -2,6 +2,7 @@ The diameter/width of a tree is defined as the number of nodes on the longest path between two end nodes. """ + from __future__ import annotations from dataclasses import dataclass diff --git a/data_structures/binary_tree/flatten_binarytree_to_linkedlist.py b/data_structures/binary_tree/flatten_binarytree_to_linkedlist.py index 8820a509ecba..9b2c7b9af24b 100644 --- a/data_structures/binary_tree/flatten_binarytree_to_linkedlist.py +++ b/data_structures/binary_tree/flatten_binarytree_to_linkedlist.py @@ -10,6 +10,7 @@ Author: Arunkumar A Date: 04/09/2023 """ + from __future__ import annotations diff --git a/data_structures/binary_tree/floor_and_ceiling.py b/data_structures/binary_tree/floor_and_ceiling.py index f8a1adbd967b..b464aefad3a2 100644 --- a/data_structures/binary_tree/floor_and_ceiling.py +++ b/data_structures/binary_tree/floor_and_ceiling.py @@ -9,6 +9,7 @@ Author : Arunkumar Date : 14th October 2023 """ + from __future__ import annotations from collections.abc import Iterator diff --git a/data_structures/binary_tree/is_sorted.py b/data_structures/binary_tree/is_sorted.py index 5876c5a9c96a..509a426611e5 100644 --- a/data_structures/binary_tree/is_sorted.py +++ b/data_structures/binary_tree/is_sorted.py @@ -13,6 +13,7 @@ Runtime: O(n) Space: O(1) """ + from __future__ import annotations from collections.abc import Iterator diff --git a/data_structures/binary_tree/is_sum_tree.py b/data_structures/binary_tree/is_sum_tree.py index 3f9cf1d560a6..846bea0fe0f2 100644 --- a/data_structures/binary_tree/is_sum_tree.py +++ b/data_structures/binary_tree/is_sum_tree.py @@ -3,6 +3,7 @@ of the values of its left and right subtrees? https://www.geeksforgeeks.org/check-if-a-given-binary-tree-is-sumtree """ + from __future__ import annotations from collections.abc import Iterator diff --git a/data_structures/binary_tree/merge_two_binary_trees.py b/data_structures/binary_tree/merge_two_binary_trees.py index 3380f8c5fb31..6bbb30428704 100644 --- a/data_structures/binary_tree/merge_two_binary_trees.py +++ b/data_structures/binary_tree/merge_two_binary_trees.py @@ -5,6 +5,7 @@ both nodes to the new value of the merged node. Otherwise, the NOT null node will be used as the node of new tree. """ + from __future__ import annotations diff --git a/data_structures/binary_tree/mirror_binary_tree.py b/data_structures/binary_tree/mirror_binary_tree.py index 39305c2a9da2..62e2f08dd4e0 100644 --- a/data_structures/binary_tree/mirror_binary_tree.py +++ b/data_structures/binary_tree/mirror_binary_tree.py @@ -3,6 +3,7 @@ Leetcode problem reference: https://leetcode.com/problems/mirror-binary-tree/ """ + from __future__ import annotations from collections.abc import Iterator diff --git a/data_structures/binary_tree/non_recursive_segment_tree.py b/data_structures/binary_tree/non_recursive_segment_tree.py index 04164e5cba4e..42c78a3a1be0 100644 --- a/data_structures/binary_tree/non_recursive_segment_tree.py +++ b/data_structures/binary_tree/non_recursive_segment_tree.py @@ -35,6 +35,7 @@ >>> st.query(0, 2) [1, 2, 3] """ + from __future__ import annotations from collections.abc import Callable diff --git a/data_structures/binary_tree/number_of_possible_binary_trees.py b/data_structures/binary_tree/number_of_possible_binary_trees.py index 684c518b1eb6..1c3dff37e7d9 100644 --- a/data_structures/binary_tree/number_of_possible_binary_trees.py +++ b/data_structures/binary_tree/number_of_possible_binary_trees.py @@ -6,6 +6,7 @@ Further details at Wikipedia: https://en.wikipedia.org/wiki/Catalan_number """ + """ Our Contribution: Basically we Create the 2 function: diff --git a/data_structures/binary_tree/red_black_tree.py b/data_structures/binary_tree/red_black_tree.py index fc299301da8a..3b5845cd957b 100644 --- a/data_structures/binary_tree/red_black_tree.py +++ b/data_structures/binary_tree/red_black_tree.py @@ -2,6 +2,7 @@ psf/black : true ruff : passed """ + from __future__ import annotations from collections.abc import Iterator diff --git a/data_structures/binary_tree/segment_tree_other.py b/data_structures/binary_tree/segment_tree_other.py index cc77c4951f1a..95f21ddd4777 100644 --- a/data_structures/binary_tree/segment_tree_other.py +++ b/data_structures/binary_tree/segment_tree_other.py @@ -3,6 +3,7 @@ allowing queries to be done later in log(N) time function takes 2 values and returns a same type value """ + from collections.abc import Sequence from queue import Queue diff --git a/data_structures/binary_tree/symmetric_tree.py b/data_structures/binary_tree/symmetric_tree.py index 331a25849c1c..98a766cab988 100644 --- a/data_structures/binary_tree/symmetric_tree.py +++ b/data_structures/binary_tree/symmetric_tree.py @@ -4,6 +4,7 @@ Leetcode reference: https://leetcode.com/problems/symmetric-tree/ """ + from __future__ import annotations from dataclasses import dataclass diff --git a/data_structures/binary_tree/wavelet_tree.py b/data_structures/binary_tree/wavelet_tree.py index 041e140f5b15..2da571e8d326 100644 --- a/data_structures/binary_tree/wavelet_tree.py +++ b/data_structures/binary_tree/wavelet_tree.py @@ -7,6 +7,7 @@ 2. https://www.youtube.com/watch?v=4aSv9PcecDw&t=811s 3. https://www.youtube.com/watch?v=CybAgVF-MMc&t=1178s """ + from __future__ import annotations test_array = [2, 1, 4, 5, 6, 0, 8, 9, 1, 2, 0, 6, 4, 2, 0, 6, 5, 3, 2, 7] diff --git a/data_structures/disjoint_set/disjoint_set.py b/data_structures/disjoint_set/disjoint_set.py index 12dafb2d935e..edc4736b6132 100644 --- a/data_structures/disjoint_set/disjoint_set.py +++ b/data_structures/disjoint_set/disjoint_set.py @@ -1,6 +1,6 @@ """ - Disjoint set. - Reference: https://en.wikipedia.org/wiki/Disjoint-set_data_structure +Disjoint set. +Reference: https://en.wikipedia.org/wiki/Disjoint-set_data_structure """ diff --git a/data_structures/hashing/bloom_filter.py b/data_structures/hashing/bloom_filter.py index 7fd0985bdc33..eb2cb4b79c46 100644 --- a/data_structures/hashing/bloom_filter.py +++ b/data_structures/hashing/bloom_filter.py @@ -58,6 +58,7 @@ >>> bloom.bitstring '01100101' """ + from hashlib import md5, sha256 HASH_FUNCTIONS = (sha256, md5) diff --git a/data_structures/hashing/double_hash.py b/data_structures/hashing/double_hash.py index 76c6c86814ec..324282cbfd8d 100644 --- a/data_structures/hashing/double_hash.py +++ b/data_structures/hashing/double_hash.py @@ -11,6 +11,7 @@ Reference: https://en.wikipedia.org/wiki/Double_hashing """ + from .hash_table import HashTable from .number_theory.prime_numbers import is_prime, next_prime diff --git a/data_structures/hashing/hash_map.py b/data_structures/hashing/hash_map.py index 6a6f8e54d5e9..9213d6930f67 100644 --- a/data_structures/hashing/hash_map.py +++ b/data_structures/hashing/hash_map.py @@ -7,6 +7,7 @@ Modern Dictionaries by Raymond Hettinger https://www.youtube.com/watch?v=p33CVV29OG8 """ + from collections.abc import Iterator, MutableMapping from dataclasses import dataclass from typing import Generic, TypeVar diff --git a/data_structures/hashing/number_theory/prime_numbers.py b/data_structures/hashing/number_theory/prime_numbers.py index 0c25896f9880..2549a1477b2b 100644 --- a/data_structures/hashing/number_theory/prime_numbers.py +++ b/data_structures/hashing/number_theory/prime_numbers.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 """ - module to operations with prime numbers +module to operations with prime numbers """ import math diff --git a/data_structures/linked_list/__init__.py b/data_structures/linked_list/__init__.py index 225113f72cee..00ef337a1211 100644 --- a/data_structures/linked_list/__init__.py +++ b/data_structures/linked_list/__init__.py @@ -5,6 +5,7 @@ head node gives us access of the complete list - Last node: points to null """ + from __future__ import annotations from typing import Any diff --git a/data_structures/linked_list/merge_two_lists.py b/data_structures/linked_list/merge_two_lists.py index ca0d3bb48540..e47dbdadcf39 100644 --- a/data_structures/linked_list/merge_two_lists.py +++ b/data_structures/linked_list/merge_two_lists.py @@ -1,6 +1,7 @@ """ Algorithm that merges two sorted linked lists into one sorted linked list. """ + from __future__ import annotations from collections.abc import Iterable, Iterator diff --git a/data_structures/linked_list/skip_list.py b/data_structures/linked_list/skip_list.py index 4413c53e520e..88d3e0daddf0 100644 --- a/data_structures/linked_list/skip_list.py +++ b/data_structures/linked_list/skip_list.py @@ -2,6 +2,7 @@ Based on "Skip Lists: A Probabilistic Alternative to Balanced Trees" by William Pugh https://epaperpress.com/sortsearch/download/skiplist.pdf """ + from __future__ import annotations from random import random diff --git a/data_structures/queue/double_ended_queue.py b/data_structures/queue/double_ended_queue.py index 17a23038d288..607d0bda3df4 100644 --- a/data_structures/queue/double_ended_queue.py +++ b/data_structures/queue/double_ended_queue.py @@ -1,6 +1,7 @@ """ Implementation of double ended queue. """ + from __future__ import annotations from collections.abc import Iterable diff --git a/data_structures/queue/linked_queue.py b/data_structures/queue/linked_queue.py index 3af97d28e4f7..80f6d309af9a 100644 --- a/data_structures/queue/linked_queue.py +++ b/data_structures/queue/linked_queue.py @@ -1,4 +1,5 @@ -""" A Queue using a linked list like structure """ +"""A Queue using a linked list like structure""" + from __future__ import annotations from collections.abc import Iterator diff --git a/data_structures/queue/queue_on_pseudo_stack.py b/data_structures/queue/queue_on_pseudo_stack.py index d9845100008e..2da67ecc263c 100644 --- a/data_structures/queue/queue_on_pseudo_stack.py +++ b/data_structures/queue/queue_on_pseudo_stack.py @@ -1,4 +1,5 @@ """Queue represented by a pseudo stack (represented by a list with pop and append)""" + from typing import Any diff --git a/data_structures/stacks/dijkstras_two_stack_algorithm.py b/data_structures/stacks/dijkstras_two_stack_algorithm.py index 976c9a53c931..94d19156f1c3 100644 --- a/data_structures/stacks/dijkstras_two_stack_algorithm.py +++ b/data_structures/stacks/dijkstras_two_stack_algorithm.py @@ -29,6 +29,7 @@ NOTE: It only works with whole numbers. """ + __author__ = "Alexander Joslin" import operator as op diff --git a/data_structures/stacks/stack_with_singly_linked_list.py b/data_structures/stacks/stack_with_singly_linked_list.py index f5ce83b863ce..8e77c2b967ef 100644 --- a/data_structures/stacks/stack_with_singly_linked_list.py +++ b/data_structures/stacks/stack_with_singly_linked_list.py @@ -1,4 +1,5 @@ -""" A Stack using a linked list like structure """ +"""A Stack using a linked list like structure""" + from __future__ import annotations from collections.abc import Iterator diff --git a/digital_image_processing/convert_to_negative.py b/digital_image_processing/convert_to_negative.py index 7df44138973c..9bf2d8f2c075 100644 --- a/digital_image_processing/convert_to_negative.py +++ b/digital_image_processing/convert_to_negative.py @@ -1,6 +1,7 @@ """ - Implemented an algorithm using opencv to convert a colored image into its negative +Implemented an algorithm using opencv to convert a colored image into its negative """ + from cv2 import destroyAllWindows, imread, imshow, waitKey diff --git a/digital_image_processing/dithering/burkes.py b/digital_image_processing/dithering/burkes.py index 35aedc16d404..4b59356d8f08 100644 --- a/digital_image_processing/dithering/burkes.py +++ b/digital_image_processing/dithering/burkes.py @@ -1,6 +1,7 @@ """ Implementation Burke's algorithm (dithering) """ + import numpy as np from cv2 import destroyAllWindows, imread, imshow, waitKey diff --git a/digital_image_processing/filters/bilateral_filter.py b/digital_image_processing/filters/bilateral_filter.py index 199ac4d9939a..6ef4434d959c 100644 --- a/digital_image_processing/filters/bilateral_filter.py +++ b/digital_image_processing/filters/bilateral_filter.py @@ -9,6 +9,7 @@ Output: img:A 2d zero padded image with values in between 0 and 1 """ + import math import sys diff --git a/digital_image_processing/filters/gaussian_filter.py b/digital_image_processing/filters/gaussian_filter.py index 634d836e5edc..0c34e59fafe5 100644 --- a/digital_image_processing/filters/gaussian_filter.py +++ b/digital_image_processing/filters/gaussian_filter.py @@ -1,6 +1,7 @@ """ Implementation of gaussian filter algorithm """ + from itertools import product from cv2 import COLOR_BGR2GRAY, cvtColor, imread, imshow, waitKey diff --git a/digital_image_processing/filters/median_filter.py b/digital_image_processing/filters/median_filter.py index 174018569d62..fc8b582ef67a 100644 --- a/digital_image_processing/filters/median_filter.py +++ b/digital_image_processing/filters/median_filter.py @@ -1,6 +1,7 @@ """ Implementation of median filter algorithm """ + from cv2 import COLOR_BGR2GRAY, cvtColor, imread, imshow, waitKey from numpy import divide, int8, multiply, ravel, sort, zeros_like diff --git a/digital_image_processing/histogram_equalization/histogram_stretch.py b/digital_image_processing/histogram_equalization/histogram_stretch.py index 5ea7773e32d9..1270c964dee6 100644 --- a/digital_image_processing/histogram_equalization/histogram_stretch.py +++ b/digital_image_processing/histogram_equalization/histogram_stretch.py @@ -3,6 +3,7 @@ @author: Binish125 """ + import copy import os diff --git a/digital_image_processing/resize/resize.py b/digital_image_processing/resize/resize.py index 4836521f9f58..7bde118da69b 100644 --- a/digital_image_processing/resize/resize.py +++ b/digital_image_processing/resize/resize.py @@ -1,4 +1,5 @@ -""" Multiple image resizing techniques """ +"""Multiple image resizing techniques""" + import numpy as np from cv2 import destroyAllWindows, imread, imshow, waitKey diff --git a/digital_image_processing/sepia.py b/digital_image_processing/sepia.py index e9dd2c06066d..1924a80451e5 100644 --- a/digital_image_processing/sepia.py +++ b/digital_image_processing/sepia.py @@ -1,6 +1,7 @@ """ - Implemented an algorithm using opencv to tone an image with sepia technique +Implemented an algorithm using opencv to tone an image with sepia technique """ + from cv2 import destroyAllWindows, imread, imshow, waitKey diff --git a/digital_image_processing/test_digital_image_processing.py b/digital_image_processing/test_digital_image_processing.py index 7993110d6bdd..d1200f4d65ca 100644 --- a/digital_image_processing/test_digital_image_processing.py +++ b/digital_image_processing/test_digital_image_processing.py @@ -1,6 +1,7 @@ """ PyTest's for Digital Image Processing """ + import numpy as np from cv2 import COLOR_BGR2GRAY, cvtColor, imread from numpy import array, uint8 diff --git a/divide_and_conquer/convex_hull.py b/divide_and_conquer/convex_hull.py index 1d1bf301def5..a5d8b713bdbc 100644 --- a/divide_and_conquer/convex_hull.py +++ b/divide_and_conquer/convex_hull.py @@ -12,6 +12,7 @@ which have not been implemented here, yet. """ + from __future__ import annotations from collections.abc import Iterable diff --git a/divide_and_conquer/kth_order_statistic.py b/divide_and_conquer/kth_order_statistic.py index 666ad1a39b8a..23fd8be5ea47 100644 --- a/divide_and_conquer/kth_order_statistic.py +++ b/divide_and_conquer/kth_order_statistic.py @@ -8,6 +8,7 @@ For more information of this algorithm: https://web.stanford.edu/class/archive/cs/cs161/cs161.1138/lectures/08/Small08.pdf """ + from __future__ import annotations from random import choice diff --git a/divide_and_conquer/max_subarray.py b/divide_and_conquer/max_subarray.py index 851ef621a24c..0fad7ab5d920 100644 --- a/divide_and_conquer/max_subarray.py +++ b/divide_and_conquer/max_subarray.py @@ -6,6 +6,7 @@ This divide-and-conquer algorithm finds the maximum subarray in O(n log n) time. """ + from __future__ import annotations import time diff --git a/divide_and_conquer/peak.py b/divide_and_conquer/peak.py index e60f28bfbe29..71ab5ac86574 100644 --- a/divide_and_conquer/peak.py +++ b/divide_and_conquer/peak.py @@ -7,6 +7,7 @@ (From Kleinberg and Tardos. Algorithm Design. Addison Wesley 2006: Chapter 5 Solved Exercise 1) """ + from __future__ import annotations diff --git a/dynamic_programming/all_construct.py b/dynamic_programming/all_construct.py index 6e53a702cbb1..5d585fc7fcec 100644 --- a/dynamic_programming/all_construct.py +++ b/dynamic_programming/all_construct.py @@ -2,6 +2,7 @@ Program to list all the ways a target string can be constructed from the given list of substrings """ + from __future__ import annotations diff --git a/dynamic_programming/bitmask.py b/dynamic_programming/bitmask.py index 56bb8e96ba02..a6e6a0cda7bf 100644 --- a/dynamic_programming/bitmask.py +++ b/dynamic_programming/bitmask.py @@ -8,6 +8,7 @@ a person can do only one task and a task is performed only by one person. Find the total no of ways in which the tasks can be distributed. """ + from collections import defaultdict diff --git a/dynamic_programming/fast_fibonacci.py b/dynamic_programming/fast_fibonacci.py index f48186a34c25..9f956ca2f979 100644 --- a/dynamic_programming/fast_fibonacci.py +++ b/dynamic_programming/fast_fibonacci.py @@ -4,6 +4,7 @@ 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 diff --git a/dynamic_programming/iterating_through_submasks.py b/dynamic_programming/iterating_through_submasks.py index 4d0a250e8dfe..372dd2c74a71 100644 --- a/dynamic_programming/iterating_through_submasks.py +++ b/dynamic_programming/iterating_through_submasks.py @@ -5,6 +5,7 @@ its submasks. The mask s is submask of m if only bits that were included in bitmask are set """ + from __future__ import annotations diff --git a/dynamic_programming/longest_increasing_subsequence.py b/dynamic_programming/longest_increasing_subsequence.py index d827893763c5..2a78e2e7ad1d 100644 --- a/dynamic_programming/longest_increasing_subsequence.py +++ b/dynamic_programming/longest_increasing_subsequence.py @@ -10,6 +10,7 @@ Example: [10, 22, 9, 33, 21, 50, 41, 60, 80] as input will return [10, 22, 33, 41, 60, 80] as output """ + from __future__ import annotations diff --git a/dynamic_programming/matrix_chain_multiplication.py b/dynamic_programming/matrix_chain_multiplication.py index 084254a61f6c..da6e525ce816 100644 --- a/dynamic_programming/matrix_chain_multiplication.py +++ b/dynamic_programming/matrix_chain_multiplication.py @@ -38,6 +38,7 @@ arr = [40, 20, 30, 10, 30] output: 26000 """ + from collections.abc import Iterator from contextlib import contextmanager from functools import cache diff --git a/dynamic_programming/max_subarray_sum.py b/dynamic_programming/max_subarray_sum.py index c76943472b97..8c1dc0889a85 100644 --- a/dynamic_programming/max_subarray_sum.py +++ b/dynamic_programming/max_subarray_sum.py @@ -9,6 +9,7 @@ Reference: https://en.wikipedia.org/wiki/Maximum_subarray_problem """ + from collections.abc import Sequence diff --git a/electronics/charging_capacitor.py b/electronics/charging_capacitor.py index 4029b0ecf267..0021e4e345e0 100644 --- a/electronics/charging_capacitor.py +++ b/electronics/charging_capacitor.py @@ -14,6 +14,7 @@ time 't' from the initiation of charging a capacitor with the help of the exponential function containing RC. Both at charging and discharging of a capacitor. """ + from math import exp # value of exp = 2.718281828459… diff --git a/electronics/charging_inductor.py b/electronics/charging_inductor.py index e5c0126c248a..8a3bbc0bbfcd 100644 --- a/electronics/charging_inductor.py +++ b/electronics/charging_inductor.py @@ -25,6 +25,7 @@ in its 'magnetic field'.with the help 'RL-time-constant' we can find current at any time in inductor while it is charging. """ + from math import exp # value of exp = 2.718281828459… diff --git a/electronics/resistor_color_code.py b/electronics/resistor_color_code.py index b0534b813def..189d19946d9d 100644 --- a/electronics/resistor_color_code.py +++ b/electronics/resistor_color_code.py @@ -58,6 +58,7 @@ https://learn.parallax.com/support/reference/resistor-color-codes https://byjus.com/physics/resistor-colour-codes/ """ + valid_colors: list = [ "Black", "Brown", diff --git a/financial/exponential_moving_average.py b/financial/exponential_moving_average.py index 0b6cea3b4c91..b56eb2712415 100644 --- a/financial/exponential_moving_average.py +++ b/financial/exponential_moving_average.py @@ -1,12 +1,12 @@ """ - Calculate the exponential moving average (EMA) on the series of stock prices. - Wikipedia Reference: https://en.wikipedia.org/wiki/Exponential_smoothing - https://www.investopedia.com/terms/e/ema.asp#toc-what-is-an-exponential - -moving-average-ema - - Exponential moving average is used in finance to analyze changes stock prices. - EMA is used in conjunction with Simple moving average (SMA), EMA reacts to the - changes in the value quicker than SMA, which is one of the advantages of using EMA. +Calculate the exponential moving average (EMA) on the series of stock prices. +Wikipedia Reference: https://en.wikipedia.org/wiki/Exponential_smoothing +https://www.investopedia.com/terms/e/ema.asp#toc-what-is-an-exponential +-moving-average-ema + +Exponential moving average is used in finance to analyze changes stock prices. +EMA is used in conjunction with Simple moving average (SMA), EMA reacts to the +changes in the value quicker than SMA, which is one of the advantages of using EMA. """ from collections.abc import Iterator diff --git a/financial/simple_moving_average.py b/financial/simple_moving_average.py index d5d68ffd3dab..f5ae444fd027 100644 --- a/financial/simple_moving_average.py +++ b/financial/simple_moving_average.py @@ -6,6 +6,7 @@ Reference: https://en.wikipedia.org/wiki/Moving_average """ + from collections.abc import Sequence diff --git a/fractals/koch_snowflake.py b/fractals/koch_snowflake.py index b0aaa86b11d8..30cd4b39c7c1 100644 --- a/fractals/koch_snowflake.py +++ b/fractals/koch_snowflake.py @@ -20,7 +20,6 @@ - numpy """ - from __future__ import annotations import matplotlib.pyplot as plt # type: ignore diff --git a/fractals/mandelbrot.py b/fractals/mandelbrot.py index 84dbda997562..5eb9af0aafe1 100644 --- a/fractals/mandelbrot.py +++ b/fractals/mandelbrot.py @@ -15,7 +15,6 @@ (see also https://en.wikipedia.org/wiki/Plotting_algorithms_for_the_Mandelbrot_set ) """ - import colorsys from PIL import Image # type: ignore diff --git a/fractals/sierpinski_triangle.py b/fractals/sierpinski_triangle.py index 45f7ab84cfff..ceb2001b681d 100644 --- a/fractals/sierpinski_triangle.py +++ b/fractals/sierpinski_triangle.py @@ -22,6 +22,7 @@ This code was written by editing the code from https://www.riannetrujillo.com/blog/python-fractal/ """ + import sys import turtle diff --git a/graphs/bi_directional_dijkstra.py b/graphs/bi_directional_dijkstra.py index 529a235db625..7b9eac6c8587 100644 --- a/graphs/bi_directional_dijkstra.py +++ b/graphs/bi_directional_dijkstra.py @@ -10,7 +10,6 @@ # Author: Swayam Singh (https://github.com/practice404) - from queue import PriorityQueue from typing import Any diff --git a/graphs/bidirectional_a_star.py b/graphs/bidirectional_a_star.py index 373d67142aa9..00f623de3493 100644 --- a/graphs/bidirectional_a_star.py +++ b/graphs/bidirectional_a_star.py @@ -1,6 +1,7 @@ """ https://en.wikipedia.org/wiki/Bidirectional_search """ + from __future__ import annotations import time diff --git a/graphs/bidirectional_breadth_first_search.py b/graphs/bidirectional_breadth_first_search.py index 511b080a9add..71c5a9aff08f 100644 --- a/graphs/bidirectional_breadth_first_search.py +++ b/graphs/bidirectional_breadth_first_search.py @@ -1,6 +1,7 @@ """ https://en.wikipedia.org/wiki/Bidirectional_search """ + from __future__ import annotations import time diff --git a/graphs/boruvka.py b/graphs/boruvka.py index 2715a3085948..3dc059ff6a62 100644 --- a/graphs/boruvka.py +++ b/graphs/boruvka.py @@ -1,29 +1,30 @@ """Borůvka's algorithm. - Determines the minimum spanning tree (MST) of a graph using the Borůvka's algorithm. - Borůvka's algorithm is a greedy algorithm for finding a minimum spanning tree in a - connected graph, or a minimum spanning forest if a graph that is not connected. +Determines the minimum spanning tree (MST) of a graph using the Borůvka's algorithm. +Borůvka's algorithm is a greedy algorithm for finding a minimum spanning tree in a +connected graph, or a minimum spanning forest if a graph that is not connected. - The time complexity of this algorithm is O(ELogV), where E represents the number - of edges, while V represents the number of nodes. - O(number_of_edges Log number_of_nodes) +The time complexity of this algorithm is O(ELogV), where E represents the number +of edges, while V represents the number of nodes. +O(number_of_edges Log number_of_nodes) - The space complexity of this algorithm is O(V + E), since we have to keep a couple - of lists whose sizes are equal to the number of nodes, as well as keep all the - edges of a graph inside of the data structure itself. +The space complexity of this algorithm is O(V + E), since we have to keep a couple +of lists whose sizes are equal to the number of nodes, as well as keep all the +edges of a graph inside of the data structure itself. - Borůvka's algorithm gives us pretty much the same result as other MST Algorithms - - they all find the minimum spanning tree, and the time complexity is approximately - the same. +Borůvka's algorithm gives us pretty much the same result as other MST Algorithms - +they all find the minimum spanning tree, and the time complexity is approximately +the same. - One advantage that Borůvka's algorithm has compared to the alternatives is that it - doesn't need to presort the edges or maintain a priority queue in order to find the - minimum spanning tree. - Even though that doesn't help its complexity, since it still passes the edges logE - times, it is a bit simpler to code. +One advantage that Borůvka's algorithm has compared to the alternatives is that it +doesn't need to presort the edges or maintain a priority queue in order to find the +minimum spanning tree. +Even though that doesn't help its complexity, since it still passes the edges logE +times, it is a bit simpler to code. - Details: https://en.wikipedia.org/wiki/Bor%C5%AFvka%27s_algorithm +Details: https://en.wikipedia.org/wiki/Bor%C5%AFvka%27s_algorithm """ + from __future__ import annotations from typing import Any diff --git a/graphs/breadth_first_search.py b/graphs/breadth_first_search.py index 171d3875f3c5..cab79be39ed3 100644 --- a/graphs/breadth_first_search.py +++ b/graphs/breadth_first_search.py @@ -1,6 +1,7 @@ #!/usr/bin/python -""" Author: OMKAR PATHAK """ +"""Author: OMKAR PATHAK""" + from __future__ import annotations from queue import Queue diff --git a/graphs/breadth_first_search_2.py b/graphs/breadth_first_search_2.py index a0b92b90b456..ccadfa346bf1 100644 --- a/graphs/breadth_first_search_2.py +++ b/graphs/breadth_first_search_2.py @@ -12,6 +12,7 @@ mark w as explored add w to Q (at the end) """ + from __future__ import annotations from collections import deque diff --git a/graphs/breadth_first_search_shortest_path.py b/graphs/breadth_first_search_shortest_path.py index d489b110b3a7..c06440bccef3 100644 --- a/graphs/breadth_first_search_shortest_path.py +++ b/graphs/breadth_first_search_shortest_path.py @@ -1,6 +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 __future__ import annotations graph = { diff --git a/graphs/breadth_first_search_shortest_path_2.py b/graphs/breadth_first_search_shortest_path_2.py index b0c8d353ba04..4f9b6e65bdf3 100644 --- a/graphs/breadth_first_search_shortest_path_2.py +++ b/graphs/breadth_first_search_shortest_path_2.py @@ -1,9 +1,10 @@ """Breadth-first search shortest path implementations. - doctest: - python -m doctest -v bfs_shortest_path.py - Manual test: - python bfs_shortest_path.py +doctest: +python -m doctest -v bfs_shortest_path.py +Manual test: +python bfs_shortest_path.py """ + demo_graph = { "A": ["B", "C", "E"], "B": ["A", "D", "E"], diff --git a/graphs/breadth_first_search_zero_one_shortest_path.py b/graphs/breadth_first_search_zero_one_shortest_path.py index 78047c5d2237..d3a255bac1ef 100644 --- a/graphs/breadth_first_search_zero_one_shortest_path.py +++ b/graphs/breadth_first_search_zero_one_shortest_path.py @@ -3,6 +3,7 @@ 0-1-graph is the weighted graph with the weights equal to 0 or 1. Link: https://codeforces.com/blog/entry/22276 """ + from __future__ import annotations from collections import deque diff --git a/graphs/deep_clone_graph.py b/graphs/deep_clone_graph.py index 55678b4c01ec..18ea99c6a52d 100644 --- a/graphs/deep_clone_graph.py +++ b/graphs/deep_clone_graph.py @@ -9,6 +9,7 @@ Each node in the graph contains a value (int) and a list (List[Node]) of its neighbors. """ + from dataclasses import dataclass diff --git a/graphs/depth_first_search.py b/graphs/depth_first_search.py index f20a503ca395..a666e74ce607 100644 --- a/graphs/depth_first_search.py +++ b/graphs/depth_first_search.py @@ -1,4 +1,5 @@ """Non recursive implementation of a DFS algorithm.""" + from __future__ import annotations diff --git a/graphs/depth_first_search_2.py b/graphs/depth_first_search_2.py index 5ff13af33168..8fe48b7f2b42 100644 --- a/graphs/depth_first_search_2.py +++ b/graphs/depth_first_search_2.py @@ -1,6 +1,6 @@ #!/usr/bin/python -""" Author: OMKAR PATHAK """ +"""Author: OMKAR PATHAK""" class Graph: diff --git a/graphs/dijkstra.py b/graphs/dijkstra.py index b0bdfab60649..87e9d2233bb2 100644 --- a/graphs/dijkstra.py +++ b/graphs/dijkstra.py @@ -30,6 +30,7 @@ distance between each vertex that makes up the path from start vertex to target vertex. """ + import heapq diff --git a/graphs/even_tree.py b/graphs/even_tree.py index 92ffb4b232f7..7d47899527a7 100644 --- a/graphs/even_tree.py +++ b/graphs/even_tree.py @@ -12,6 +12,7 @@ Note: The tree input will be such that it can always be decomposed into components containing an even number of nodes. """ + # pylint: disable=invalid-name from collections import defaultdict diff --git a/graphs/frequent_pattern_graph_miner.py b/graphs/frequent_pattern_graph_miner.py index 208e57f9b32f..f8da73f3438e 100644 --- a/graphs/frequent_pattern_graph_miner.py +++ b/graphs/frequent_pattern_graph_miner.py @@ -8,6 +8,7 @@ URL: https://www.researchgate.net/publication/235255851 """ + # fmt: off edge_array = [ ['ab-e1', 'ac-e3', 'ad-e5', 'bc-e4', 'bd-e2', 'be-e6', 'bh-e12', 'cd-e2', 'ce-e4', diff --git a/graphs/graph_adjacency_list.py b/graphs/graph_adjacency_list.py index d0b94f03e9b4..abc75311cd60 100644 --- a/graphs/graph_adjacency_list.py +++ b/graphs/graph_adjacency_list.py @@ -15,6 +15,7 @@ - Make edge weights and vertex values customizable to store whatever the client wants - Support multigraph functionality if the client wants it """ + from __future__ import annotations import random diff --git a/graphs/graph_adjacency_matrix.py b/graphs/graph_adjacency_matrix.py index cdef388d9098..059a6aa9ffb5 100644 --- a/graphs/graph_adjacency_matrix.py +++ b/graphs/graph_adjacency_matrix.py @@ -15,6 +15,7 @@ - Make edge weights and vertex values customizable to store whatever the client wants - Support multigraph functionality if the client wants it """ + from __future__ import annotations import random diff --git a/graphs/graphs_floyd_warshall.py b/graphs/graphs_floyd_warshall.py index 56cf8b9e382b..aaed9ac5df8b 100644 --- a/graphs/graphs_floyd_warshall.py +++ b/graphs/graphs_floyd_warshall.py @@ -1,7 +1,7 @@ # floyd_warshall.py """ - The problem is to find the shortest distance between all pairs of vertices in a - weighted directed graph that can have negative edge weights. +The problem is to find the shortest distance between all pairs of vertices in a +weighted directed graph that can have negative edge weights. """ diff --git a/graphs/minimum_spanning_tree_prims2.py b/graphs/minimum_spanning_tree_prims2.py index 81f30ef615fe..cc918f81dfe8 100644 --- a/graphs/minimum_spanning_tree_prims2.py +++ b/graphs/minimum_spanning_tree_prims2.py @@ -6,6 +6,7 @@ at a time, from an arbitrary starting vertex, at each step adding the cheapest possible connection from the tree to another vertex. """ + from __future__ import annotations from sys import maxsize diff --git a/graphs/page_rank.py b/graphs/page_rank.py index b9e4c4a72a93..c0ce3a94c76b 100644 --- a/graphs/page_rank.py +++ b/graphs/page_rank.py @@ -1,6 +1,7 @@ """ Author: https://github.com/bhushan-borole """ + """ The input graph for the algorithm is: diff --git a/graphs/prim.py b/graphs/prim.py index 6cb1a6def359..5b3ce04441ec 100644 --- a/graphs/prim.py +++ b/graphs/prim.py @@ -1,8 +1,8 @@ """Prim's Algorithm. - Determines the minimum spanning tree(MST) of a graph using the Prim's Algorithm. +Determines the minimum spanning tree(MST) of a graph using the Prim's Algorithm. - Details: https://en.wikipedia.org/wiki/Prim%27s_algorithm +Details: https://en.wikipedia.org/wiki/Prim%27s_algorithm """ import heapq as hq diff --git a/greedy_methods/gas_station.py b/greedy_methods/gas_station.py index 2427375d2664..6391ce379329 100644 --- a/greedy_methods/gas_station.py +++ b/greedy_methods/gas_station.py @@ -23,6 +23,7 @@ start checking from the next station. """ + from dataclasses import dataclass diff --git a/hashes/adler32.py b/hashes/adler32.py index 611ebc88b80f..38d76ab12aa0 100644 --- a/hashes/adler32.py +++ b/hashes/adler32.py @@ -1,11 +1,11 @@ """ - Adler-32 is a checksum algorithm which was invented by Mark Adler in 1995. - Compared to a cyclic redundancy check of the same length, it trades reliability for - speed (preferring the latter). - Adler-32 is more reliable than Fletcher-16, and slightly less reliable than - Fletcher-32.[2] +Adler-32 is a checksum algorithm which was invented by Mark Adler in 1995. +Compared to a cyclic redundancy check of the same length, it trades reliability for +speed (preferring the latter). +Adler-32 is more reliable than Fletcher-16, and slightly less reliable than +Fletcher-32.[2] - source: https://en.wikipedia.org/wiki/Adler-32 +source: https://en.wikipedia.org/wiki/Adler-32 """ MOD_ADLER = 65521 diff --git a/hashes/hamming_code.py b/hashes/hamming_code.py index b34fdd4c7a74..b3095852ac51 100644 --- a/hashes/hamming_code.py +++ b/hashes/hamming_code.py @@ -4,44 +4,44 @@ # Black: True """ - * This code implement the Hamming code: - https://en.wikipedia.org/wiki/Hamming_code - In telecommunication, - Hamming codes are a family of linear error-correcting codes. Hamming - codes can detect up to two-bit errors or correct one-bit errors - without detection of uncorrected errors. By contrast, the simple - parity code cannot correct errors, and can detect only an odd number - of bits in error. Hamming codes are perfect codes, that is, they - achieve the highest possible rate for codes with their block length - and minimum distance of three. - - * the implemented code consists of: - * a function responsible for encoding the message (emitterConverter) - * return the encoded message - * a function responsible for decoding the message (receptorConverter) - * return the decoded message and a ack of data integrity - - * how to use: - to be used you must declare how many parity bits (sizePari) - you want to include in the message. - it is desired (for test purposes) to select a bit to be set - as an error. This serves to check whether the code is working correctly. - Lastly, the variable of the message/word that must be desired to be - encoded (text). - - * how this work: - declaration of variables (sizePari, be, text) - - converts the message/word (text) to binary using the - text_to_bits function - encodes the message using the rules of hamming encoding - decodes the message using the rules of hamming encoding - print the original message, the encoded message and the - decoded message - - forces an error in the coded text variable - decodes the message that was forced the error - print the original message, the encoded message, the bit changed - message and the decoded message +* This code implement the Hamming code: + https://en.wikipedia.org/wiki/Hamming_code - In telecommunication, +Hamming codes are a family of linear error-correcting codes. Hamming +codes can detect up to two-bit errors or correct one-bit errors +without detection of uncorrected errors. By contrast, the simple +parity code cannot correct errors, and can detect only an odd number +of bits in error. Hamming codes are perfect codes, that is, they +achieve the highest possible rate for codes with their block length +and minimum distance of three. + +* the implemented code consists of: + * a function responsible for encoding the message (emitterConverter) + * return the encoded message + * a function responsible for decoding the message (receptorConverter) + * return the decoded message and a ack of data integrity + +* how to use: + to be used you must declare how many parity bits (sizePari) + you want to include in the message. + it is desired (for test purposes) to select a bit to be set + as an error. This serves to check whether the code is working correctly. + Lastly, the variable of the message/word that must be desired to be + encoded (text). + +* how this work: + declaration of variables (sizePari, be, text) + + converts the message/word (text) to binary using the + text_to_bits function + encodes the message using the rules of hamming encoding + decodes the message using the rules of hamming encoding + print the original message, the encoded message and the + decoded message + + forces an error in the coded text variable + decodes the message that was forced the error + print the original message, the encoded message, the bit changed + message and the decoded message """ # Imports diff --git a/hashes/luhn.py b/hashes/luhn.py index bb77fd05c556..a29bf39e3d82 100644 --- a/hashes/luhn.py +++ b/hashes/luhn.py @@ -1,4 +1,5 @@ -""" Luhn Algorithm """ +"""Luhn Algorithm""" + from __future__ import annotations diff --git a/hashes/sdbm.py b/hashes/sdbm.py index a5432874ba7d..a5abc6f3185b 100644 --- a/hashes/sdbm.py +++ b/hashes/sdbm.py @@ -1,21 +1,21 @@ """ - This algorithm was created for sdbm (a public-domain reimplementation of ndbm) - database library. - It was found to do well in scrambling bits, causing better distribution of the keys - and fewer splits. - It also happens to be a good general hashing function with good distribution. - The actual function (pseudo code) is: - for i in i..len(str): - hash(i) = hash(i - 1) * 65599 + str[i]; +This algorithm was created for sdbm (a public-domain reimplementation of ndbm) +database library. +It was found to do well in scrambling bits, causing better distribution of the keys +and fewer splits. +It also happens to be a good general hashing function with good distribution. +The actual function (pseudo code) is: + for i in i..len(str): + hash(i) = hash(i - 1) * 65599 + str[i]; - What is included below is the faster version used in gawk. [there is even a faster, - duff-device version] - The magic constant 65599 was picked out of thin air while experimenting with - different constants. - It turns out to be a prime. - This is one of the algorithms used in berkeley db (see sleepycat) and elsewhere. +What is included below is the faster version used in gawk. [there is even a faster, +duff-device version] +The magic constant 65599 was picked out of thin air while experimenting with +different constants. +It turns out to be a prime. +This is one of the algorithms used in berkeley db (see sleepycat) and elsewhere. - source: http://www.cse.yorku.ca/~oz/hash.html +source: http://www.cse.yorku.ca/~oz/hash.html """ diff --git a/hashes/sha1.py b/hashes/sha1.py index a0fa688f863e..75a1423e9b5f 100644 --- a/hashes/sha1.py +++ b/hashes/sha1.py @@ -25,6 +25,7 @@ Reference: https://deadhacker.com/2006/02/21/sha-1-illustrated/ """ + import argparse import hashlib # hashlib is only used inside the Test class import struct diff --git a/knapsack/knapsack.py b/knapsack/knapsack.py index 18a36c3bcdda..bb507be1ba3c 100644 --- a/knapsack/knapsack.py +++ b/knapsack/knapsack.py @@ -1,6 +1,7 @@ -""" A naive recursive implementation of 0-1 Knapsack Problem - https://en.wikipedia.org/wiki/Knapsack_problem +"""A naive recursive implementation of 0-1 Knapsack Problem +https://en.wikipedia.org/wiki/Knapsack_problem """ + from __future__ import annotations diff --git a/knapsack/tests/test_knapsack.py b/knapsack/tests/test_knapsack.py index 6932bbb3536b..7bfb8780627b 100644 --- a/knapsack/tests/test_knapsack.py +++ b/knapsack/tests/test_knapsack.py @@ -6,6 +6,7 @@ This file contains the test-suite for the knapsack problem. """ + import unittest from knapsack import knapsack as k diff --git a/linear_algebra/gaussian_elimination.py b/linear_algebra/gaussian_elimination.py index a1a35131b157..724773c0db98 100644 --- a/linear_algebra/gaussian_elimination.py +++ b/linear_algebra/gaussian_elimination.py @@ -3,7 +3,6 @@ Gaussian elimination - https://en.wikipedia.org/wiki/Gaussian_elimination """ - import numpy as np from numpy import float64 from numpy.typing import NDArray diff --git a/linear_algebra/jacobi_iteration_method.py b/linear_algebra/jacobi_iteration_method.py index 8c91a19ef1b0..2cc9c103018b 100644 --- a/linear_algebra/jacobi_iteration_method.py +++ b/linear_algebra/jacobi_iteration_method.py @@ -1,6 +1,7 @@ """ Jacobi Iteration Method - https://en.wikipedia.org/wiki/Jacobi_method """ + from __future__ import annotations import numpy as np diff --git a/linear_algebra/lu_decomposition.py b/linear_algebra/lu_decomposition.py index 094b20abfecc..1d364163d9a7 100644 --- a/linear_algebra/lu_decomposition.py +++ b/linear_algebra/lu_decomposition.py @@ -15,6 +15,7 @@ Reference: https://en.wikipedia.org/wiki/LU_decomposition """ + from __future__ import annotations import numpy as np diff --git a/linear_algebra/src/conjugate_gradient.py b/linear_algebra/src/conjugate_gradient.py index 4cf566ec9e36..4c0b58deb978 100644 --- a/linear_algebra/src/conjugate_gradient.py +++ b/linear_algebra/src/conjugate_gradient.py @@ -3,6 +3,7 @@ - https://en.wikipedia.org/wiki/Conjugate_gradient_method - https://en.wikipedia.org/wiki/Definite_symmetric_matrix """ + from typing import Any import numpy as np diff --git a/linear_algebra/src/lib.py b/linear_algebra/src/lib.py index 5074faf31d1d..5af6c62e3ad4 100644 --- a/linear_algebra/src/lib.py +++ b/linear_algebra/src/lib.py @@ -18,6 +18,7 @@ - function square_zero_matrix(N) - function random_matrix(W, H, a, b) """ + from __future__ import annotations import math @@ -96,12 +97,10 @@ def __sub__(self, other: Vector) -> Vector: raise Exception("must have the same size") @overload - def __mul__(self, other: float) -> Vector: - ... + def __mul__(self, other: float) -> Vector: ... @overload - def __mul__(self, other: Vector) -> float: - ... + def __mul__(self, other: Vector) -> float: ... def __mul__(self, other: float | Vector) -> float | Vector: """ @@ -309,12 +308,10 @@ def __sub__(self, other: Matrix) -> Matrix: raise Exception("matrices must have the same dimension!") @overload - def __mul__(self, other: float) -> Matrix: - ... + def __mul__(self, other: float) -> Matrix: ... @overload - def __mul__(self, other: Vector) -> Vector: - ... + def __mul__(self, other: Vector) -> Vector: ... def __mul__(self, other: float | Vector) -> Vector | Matrix: """ diff --git a/linear_algebra/src/rayleigh_quotient.py b/linear_algebra/src/rayleigh_quotient.py index 4773429cbf1b..46bf1671d2b1 100644 --- a/linear_algebra/src/rayleigh_quotient.py +++ b/linear_algebra/src/rayleigh_quotient.py @@ -1,6 +1,7 @@ """ https://en.wikipedia.org/wiki/Rayleigh_quotient """ + from typing import Any import numpy as np diff --git a/linear_algebra/src/test_linear_algebra.py b/linear_algebra/src/test_linear_algebra.py index 95ab408b3d86..fc5f90fd5cbe 100644 --- a/linear_algebra/src/test_linear_algebra.py +++ b/linear_algebra/src/test_linear_algebra.py @@ -6,6 +6,7 @@ This file contains the test-suite for the linear algebra library. """ + import unittest import pytest diff --git a/linear_algebra/src/transformations_2d.py b/linear_algebra/src/transformations_2d.py index cdf42100d5d9..b4185cd2848f 100644 --- a/linear_algebra/src/transformations_2d.py +++ b/linear_algebra/src/transformations_2d.py @@ -11,6 +11,7 @@ reflection(45) = [[0.05064397763545947, 0.893996663600558], [0.893996663600558, 0.7018070490682369]] """ + from math import cos, sin diff --git a/linear_programming/simplex.py b/linear_programming/simplex.py index bbc97d8e22bf..dc171bacd3a2 100644 --- a/linear_programming/simplex.py +++ b/linear_programming/simplex.py @@ -12,6 +12,7 @@ https://en.wikipedia.org/wiki/Simplex_algorithm https://tinyurl.com/simplex4beginners """ + from typing import Any import numpy as np diff --git a/machine_learning/apriori_algorithm.py b/machine_learning/apriori_algorithm.py index d9fd1f82ea3c..09a89ac236bd 100644 --- a/machine_learning/apriori_algorithm.py +++ b/machine_learning/apriori_algorithm.py @@ -10,6 +10,7 @@ WIKI: https://en.wikipedia.org/wiki/Apriori_algorithm Examples: https://www.kaggle.com/code/earthian/apriori-association-rules-mining """ + from itertools import combinations diff --git a/machine_learning/astar.py b/machine_learning/astar.py index ff5208266343..a5859e51fe70 100644 --- a/machine_learning/astar.py +++ b/machine_learning/astar.py @@ -12,6 +12,7 @@ https://en.wikipedia.org/wiki/A*_search_algorithm """ + import numpy as np diff --git a/machine_learning/automatic_differentiation.py b/machine_learning/automatic_differentiation.py index cd2e5cdaa782..5c2708247c21 100644 --- a/machine_learning/automatic_differentiation.py +++ b/machine_learning/automatic_differentiation.py @@ -6,6 +6,7 @@ Author: Poojan Smart Email: smrtpoojan@gmail.com """ + from __future__ import annotations from collections import defaultdict diff --git a/machine_learning/data_transformations.py b/machine_learning/data_transformations.py index ecfd3b9e27c2..a1c28d514fd5 100644 --- a/machine_learning/data_transformations.py +++ b/machine_learning/data_transformations.py @@ -25,6 +25,7 @@ 2. non-gaussian (non-normal) distributions work better with normalization 3. If a column or list of values has extreme values / outliers, use standardization """ + from statistics import mean, stdev diff --git a/machine_learning/decision_tree.py b/machine_learning/decision_tree.py index c67e09c7f114..7f129919a3ce 100644 --- a/machine_learning/decision_tree.py +++ b/machine_learning/decision_tree.py @@ -3,6 +3,7 @@ Input data set: The input data set must be 1-dimensional with continuous labels. Output: The decision tree maps a real number input to a real number output. """ + import numpy as np diff --git a/machine_learning/frequent_pattern_growth.py b/machine_learning/frequent_pattern_growth.py index 205d598464a1..6b9870f5e1d2 100644 --- a/machine_learning/frequent_pattern_growth.py +++ b/machine_learning/frequent_pattern_growth.py @@ -9,6 +9,7 @@ Examples: https://www.javatpoint.com/fp-growth-algorithm-in-data-mining """ + from __future__ import annotations from dataclasses import dataclass, field diff --git a/machine_learning/gradient_descent.py b/machine_learning/gradient_descent.py index 9ffc02bbc284..db38b3c95b52 100644 --- a/machine_learning/gradient_descent.py +++ b/machine_learning/gradient_descent.py @@ -2,6 +2,7 @@ Implementation of gradient descent algorithm for minimizing cost of a linear hypothesis function. """ + import numpy # List of input, output pairs diff --git a/machine_learning/k_means_clust.py b/machine_learning/k_means_clust.py index 4a219edc3bb1..9f6646944458 100644 --- a/machine_learning/k_means_clust.py +++ b/machine_learning/k_means_clust.py @@ -40,6 +40,7 @@ 5. Transfers Dataframe into excel format it must have feature called 'Clust' with k means clustering numbers in it. """ + import warnings import numpy as np diff --git a/machine_learning/linear_discriminant_analysis.py b/machine_learning/linear_discriminant_analysis.py index 88c047157893..606e11f3698e 100644 --- a/machine_learning/linear_discriminant_analysis.py +++ b/machine_learning/linear_discriminant_analysis.py @@ -1,47 +1,48 @@ """ - Linear Discriminant Analysis +Linear Discriminant Analysis - Assumptions About Data : - 1. The input variables has a gaussian distribution. - 2. The variance calculated for each input variables by class grouping is the - same. - 3. The mix of classes in your training set is representative of the problem. +Assumptions About Data : + 1. The input variables has a gaussian distribution. + 2. The variance calculated for each input variables by class grouping is the + same. + 3. The mix of classes in your training set is representative of the problem. - Learning The Model : - The LDA model requires the estimation of statistics from the training data : - 1. Mean of each input value for each class. - 2. Probability of an instance belong to each class. - 3. Covariance for the input data for each class +Learning The Model : + The LDA model requires the estimation of statistics from the training data : + 1. Mean of each input value for each class. + 2. Probability of an instance belong to each class. + 3. Covariance for the input data for each class - Calculate the class means : - mean(x) = 1/n ( for i = 1 to i = n --> sum(xi)) + Calculate the class means : + mean(x) = 1/n ( for i = 1 to i = n --> sum(xi)) - Calculate the class probabilities : - P(y = 0) = count(y = 0) / (count(y = 0) + count(y = 1)) - P(y = 1) = count(y = 1) / (count(y = 0) + count(y = 1)) + Calculate the class probabilities : + P(y = 0) = count(y = 0) / (count(y = 0) + count(y = 1)) + P(y = 1) = count(y = 1) / (count(y = 0) + count(y = 1)) - Calculate the variance : - We can calculate the variance for dataset in two steps : - 1. Calculate the squared difference for each input variable from the - group mean. - 2. Calculate the mean of the squared difference. - ------------------------------------------------ - Squared_Difference = (x - mean(k)) ** 2 - Variance = (1 / (count(x) - count(classes))) * - (for i = 1 to i = n --> sum(Squared_Difference(xi))) + Calculate the variance : + We can calculate the variance for dataset in two steps : + 1. Calculate the squared difference for each input variable from the + group mean. + 2. Calculate the mean of the squared difference. + ------------------------------------------------ + Squared_Difference = (x - mean(k)) ** 2 + Variance = (1 / (count(x) - count(classes))) * + (for i = 1 to i = n --> sum(Squared_Difference(xi))) - Making Predictions : - discriminant(x) = x * (mean / variance) - - ((mean ** 2) / (2 * variance)) + Ln(probability) - --------------------------------------------------------------------------- - After calculating the discriminant value for each class, the class with the - largest discriminant value is taken as the prediction. +Making Predictions : + discriminant(x) = x * (mean / variance) - + ((mean ** 2) / (2 * variance)) + Ln(probability) + --------------------------------------------------------------------------- + After calculating the discriminant value for each class, the class with the + largest discriminant value is taken as the prediction. - Author: @EverLookNeverSee +Author: @EverLookNeverSee """ + from collections.abc import Callable from math import log from os import name, system diff --git a/machine_learning/linear_regression.py b/machine_learning/linear_regression.py index 0847112ad538..39bee5712c16 100644 --- a/machine_learning/linear_regression.py +++ b/machine_learning/linear_regression.py @@ -7,6 +7,7 @@ fit our dataset. In this particular code, I had used a CSGO dataset (ADR vs Rating). We try to best fit a line through dataset and estimate the parameters. """ + import numpy as np import requests diff --git a/machine_learning/logistic_regression.py b/machine_learning/logistic_regression.py index 59a70fd65cf9..090af5382185 100644 --- a/machine_learning/logistic_regression.py +++ b/machine_learning/logistic_regression.py @@ -14,6 +14,7 @@ Coursera ML course https://medium.com/@martinpella/logistic-regression-from-scratch-in-python-124c5636b8ac """ + import numpy as np from matplotlib import pyplot as plt from sklearn import datasets diff --git a/machine_learning/lstm/lstm_prediction.py b/machine_learning/lstm/lstm_prediction.py index ecbd451266ad..f0fd12c9de7f 100644 --- a/machine_learning/lstm/lstm_prediction.py +++ b/machine_learning/lstm/lstm_prediction.py @@ -1,9 +1,10 @@ """ - Create a Long Short Term Memory (LSTM) network model - An LSTM is a type of Recurrent Neural Network (RNN) as discussed at: - * https://colah.github.io/posts/2015-08-Understanding-LSTMs - * https://en.wikipedia.org/wiki/Long_short-term_memory +Create a Long Short Term Memory (LSTM) network model +An LSTM is a type of Recurrent Neural Network (RNN) as discussed at: +* https://colah.github.io/posts/2015-08-Understanding-LSTMs +* https://en.wikipedia.org/wiki/Long_short-term_memory """ + import numpy as np import pandas as pd from sklearn.preprocessing import MinMaxScaler diff --git a/machine_learning/mfcc.py b/machine_learning/mfcc.py index 7ce8ceb50ff2..a1e99ce4ad40 100644 --- a/machine_learning/mfcc.py +++ b/machine_learning/mfcc.py @@ -57,7 +57,6 @@ Author: Amir Lavasani """ - import logging import numpy as np diff --git a/machine_learning/self_organizing_map.py b/machine_learning/self_organizing_map.py index 32fdf1d2b41d..fb9d0074e791 100644 --- a/machine_learning/self_organizing_map.py +++ b/machine_learning/self_organizing_map.py @@ -1,6 +1,7 @@ """ https://en.wikipedia.org/wiki/Self-organizing_map """ + import math diff --git a/machine_learning/sequential_minimum_optimization.py b/machine_learning/sequential_minimum_optimization.py index 9ee8c52fb2e9..be16baca1a4c 100644 --- a/machine_learning/sequential_minimum_optimization.py +++ b/machine_learning/sequential_minimum_optimization.py @@ -30,7 +30,6 @@ https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/tr-98-14.pdf """ - import os import sys import urllib.request diff --git a/machine_learning/similarity_search.py b/machine_learning/similarity_search.py index 7a23ec463c8f..0bc3b17d7e5a 100644 --- a/machine_learning/similarity_search.py +++ b/machine_learning/similarity_search.py @@ -7,6 +7,7 @@ 1. the nearest vector 2. distance between the vector and the nearest vector (float) """ + from __future__ import annotations import math diff --git a/maths/allocation_number.py b/maths/allocation_number.py index d419e74d01ff..52f1ac4bdb23 100644 --- a/maths/allocation_number.py +++ b/maths/allocation_number.py @@ -5,6 +5,7 @@ for i in allocation_list: requests.get(url,headers={'Range':f'bytes={i}'}) """ + from __future__ import annotations diff --git a/maths/area.py b/maths/area.py index ea7216c8fe3f..31a654206977 100644 --- a/maths/area.py +++ b/maths/area.py @@ -2,6 +2,7 @@ Find the area of various geometric shapes Wikipedia reference: https://en.wikipedia.org/wiki/Area """ + from math import pi, sqrt, tan diff --git a/maths/area_under_curve.py b/maths/area_under_curve.py index 0da6546b2e36..10aec768fa09 100644 --- a/maths/area_under_curve.py +++ b/maths/area_under_curve.py @@ -1,6 +1,7 @@ """ Approximates the area under the curve using the trapezoidal rule """ + from __future__ import annotations from collections.abc import Callable diff --git a/maths/basic_maths.py b/maths/basic_maths.py index c9e3d00fa23b..833f31c18b9e 100644 --- a/maths/basic_maths.py +++ b/maths/basic_maths.py @@ -1,4 +1,5 @@ """Implementation of Basic Math in Python.""" + import math diff --git a/maths/binomial_distribution.py b/maths/binomial_distribution.py index 5b56f2d59244..eabcaea0d1b2 100644 --- a/maths/binomial_distribution.py +++ b/maths/binomial_distribution.py @@ -1,5 +1,6 @@ """For more information about the Binomial Distribution - - https://en.wikipedia.org/wiki/Binomial_distribution""" +https://en.wikipedia.org/wiki/Binomial_distribution""" + from math import factorial diff --git a/maths/chinese_remainder_theorem.py b/maths/chinese_remainder_theorem.py index d3e75e77922a..18af63d106e8 100644 --- a/maths/chinese_remainder_theorem.py +++ b/maths/chinese_remainder_theorem.py @@ -11,6 +11,7 @@ 1. Use extended euclid algorithm to find x,y such that a*x + b*y = 1 2. Take n = ra*by + rb*ax """ + from __future__ import annotations diff --git a/maths/continued_fraction.py b/maths/continued_fraction.py index 04ff0b6ff0d2..2c38bf88b1e9 100644 --- a/maths/continued_fraction.py +++ b/maths/continued_fraction.py @@ -4,7 +4,6 @@ https://en.wikipedia.org/wiki/Continued_fraction """ - from fractions import Fraction from math import floor diff --git a/maths/entropy.py b/maths/entropy.py index 23753d884484..76fac4ee717d 100644 --- a/maths/entropy.py +++ b/maths/entropy.py @@ -4,6 +4,7 @@ Implementation of entropy of information https://en.wikipedia.org/wiki/Entropy_(information_theory) """ + from __future__ import annotations import math diff --git a/maths/gamma.py b/maths/gamma.py index 822bbc74456f..e328cd8b22b7 100644 --- a/maths/gamma.py +++ b/maths/gamma.py @@ -8,6 +8,7 @@ the non-positive integers Python's Standard Library math.gamma() function overflows around gamma(171.624). """ + import math from numpy import inf diff --git a/maths/gaussian.py b/maths/gaussian.py index 51ebc2e25849..0e02010a9c67 100644 --- a/maths/gaussian.py +++ b/maths/gaussian.py @@ -1,6 +1,7 @@ """ Reference: https://en.wikipedia.org/wiki/Gaussian_function """ + from numpy import exp, pi, sqrt diff --git a/maths/interquartile_range.py b/maths/interquartile_range.py index d4d72e73ef49..e91a651647d4 100644 --- a/maths/interquartile_range.py +++ b/maths/interquartile_range.py @@ -7,6 +7,7 @@ Script inspired by this Wikipedia article: https://en.wikipedia.org/wiki/Interquartile_range """ + from __future__ import annotations diff --git a/maths/is_square_free.py b/maths/is_square_free.py index 08c70dc32c38..a336c37e8dbc 100644 --- a/maths/is_square_free.py +++ b/maths/is_square_free.py @@ -3,6 +3,7 @@ psf/black : True ruff : True """ + from __future__ import annotations diff --git a/maths/karatsuba.py b/maths/karatsuba.py index 3d29e31d2107..0e063fb44b83 100644 --- a/maths/karatsuba.py +++ b/maths/karatsuba.py @@ -1,4 +1,4 @@ -""" Multiply two numbers using Karatsuba algorithm """ +"""Multiply two numbers using Karatsuba algorithm""" def karatsuba(a: int, b: int) -> int: diff --git a/maths/lucas_lehmer_primality_test.py b/maths/lucas_lehmer_primality_test.py index 0a5621aacd79..292387414dee 100644 --- a/maths/lucas_lehmer_primality_test.py +++ b/maths/lucas_lehmer_primality_test.py @@ -1,13 +1,13 @@ """ - In mathematics, the Lucas–Lehmer test (LLT) is a primality test for Mersenne - numbers. https://en.wikipedia.org/wiki/Lucas%E2%80%93Lehmer_primality_test +In mathematics, the Lucas–Lehmer test (LLT) is a primality test for Mersenne +numbers. https://en.wikipedia.org/wiki/Lucas%E2%80%93Lehmer_primality_test - A Mersenne number is a number that is one less than a power of two. - That is M_p = 2^p - 1 - https://en.wikipedia.org/wiki/Mersenne_prime +A Mersenne number is a number that is one less than a power of two. +That is M_p = 2^p - 1 +https://en.wikipedia.org/wiki/Mersenne_prime - The Lucas–Lehmer test is the primality test used by the - Great Internet Mersenne Prime Search (GIMPS) to locate large primes. +The Lucas–Lehmer test is the primality test used by the +Great Internet Mersenne Prime Search (GIMPS) to locate large primes. """ diff --git a/maths/maclaurin_series.py b/maths/maclaurin_series.py index d5c3c3ab958b..6ec5551a5e6e 100644 --- a/maths/maclaurin_series.py +++ b/maths/maclaurin_series.py @@ -1,6 +1,7 @@ """ https://en.wikipedia.org/wiki/Taylor_series#Trigonometric_functions """ + from math import factorial, pi diff --git a/maths/max_sum_sliding_window.py b/maths/max_sum_sliding_window.py index c6f9b4ed0ad7..090117429604 100644 --- a/maths/max_sum_sliding_window.py +++ b/maths/max_sum_sliding_window.py @@ -6,6 +6,7 @@ called 'Window sliding technique' where the nested loops can be converted to a single loop to reduce time complexity. """ + from __future__ import annotations diff --git a/maths/modular_exponential.py b/maths/modular_exponential.py index 42987dbf3a24..a27e29ebc02a 100644 --- a/maths/modular_exponential.py +++ b/maths/modular_exponential.py @@ -1,8 +1,8 @@ """ - Modular Exponential. - Modular exponentiation is a type of exponentiation performed over a modulus. - For more explanation, please check - https://en.wikipedia.org/wiki/Modular_exponentiation +Modular Exponential. +Modular exponentiation is a type of exponentiation performed over a modulus. +For more explanation, please check +https://en.wikipedia.org/wiki/Modular_exponentiation """ """Calculate Modular Exponential.""" diff --git a/maths/monte_carlo.py b/maths/monte_carlo.py index 474f1f65deb4..d174a0b188a2 100644 --- a/maths/monte_carlo.py +++ b/maths/monte_carlo.py @@ -1,6 +1,7 @@ """ @author: MatteoRaso """ + from collections.abc import Callable from math import pi, sqrt from random import uniform diff --git a/maths/numerical_analysis/adams_bashforth.py b/maths/numerical_analysis/adams_bashforth.py index d61f022a413d..fb406171098a 100644 --- a/maths/numerical_analysis/adams_bashforth.py +++ b/maths/numerical_analysis/adams_bashforth.py @@ -4,6 +4,7 @@ https://en.wikipedia.org/wiki/Linear_multistep_method Author : Ravi Kumar """ + from collections.abc import Callable from dataclasses import dataclass diff --git a/maths/numerical_analysis/nevilles_method.py b/maths/numerical_analysis/nevilles_method.py index 1f48b43fbd22..256b61f5f218 100644 --- a/maths/numerical_analysis/nevilles_method.py +++ b/maths/numerical_analysis/nevilles_method.py @@ -1,11 +1,11 @@ """ - Python program to show how to interpolate and evaluate a polynomial - using Neville's method. - Neville’s method evaluates a polynomial that passes through a - given set of x and y points for a particular x value (x0) using the - Newton polynomial form. - Reference: - https://rpubs.com/aaronsc32/nevilles-method-polynomial-interpolation +Python program to show how to interpolate and evaluate a polynomial +using Neville's method. +Neville’s method evaluates a polynomial that passes through a +given set of x and y points for a particular x value (x0) using the +Newton polynomial form. +Reference: + https://rpubs.com/aaronsc32/nevilles-method-polynomial-interpolation """ diff --git a/maths/numerical_analysis/newton_raphson.py b/maths/numerical_analysis/newton_raphson.py index feee38f905dd..10fb244bf426 100644 --- a/maths/numerical_analysis/newton_raphson.py +++ b/maths/numerical_analysis/newton_raphson.py @@ -9,6 +9,7 @@ Reference: https://en.wikipedia.org/wiki/Newton%27s_method """ + from collections.abc import Callable RealFunc = Callable[[float], float] diff --git a/maths/numerical_analysis/numerical_integration.py b/maths/numerical_analysis/numerical_integration.py index 4ac562644a07..f64436ec48c1 100644 --- a/maths/numerical_analysis/numerical_integration.py +++ b/maths/numerical_analysis/numerical_integration.py @@ -1,6 +1,7 @@ """ Approximates the area under the curve using the trapezoidal rule """ + from __future__ import annotations from collections.abc import Callable diff --git a/maths/numerical_analysis/runge_kutta_gills.py b/maths/numerical_analysis/runge_kutta_gills.py index 2bd9cd6129b8..451cde4cb935 100644 --- a/maths/numerical_analysis/runge_kutta_gills.py +++ b/maths/numerical_analysis/runge_kutta_gills.py @@ -4,6 +4,7 @@ https://www.geeksforgeeks.org/gills-4th-order-method-to-solve-differential-equations/ Author : Ravi Kumar """ + from collections.abc import Callable from math import sqrt diff --git a/maths/numerical_analysis/secant_method.py b/maths/numerical_analysis/secant_method.py index d39cb0ff30ef..9fff8222cdde 100644 --- a/maths/numerical_analysis/secant_method.py +++ b/maths/numerical_analysis/secant_method.py @@ -2,6 +2,7 @@ Implementing Secant method in Python Author: dimgrichr """ + from math import exp diff --git a/maths/prime_factors.py b/maths/prime_factors.py index e520ae3a6d04..47abcf10e618 100644 --- a/maths/prime_factors.py +++ b/maths/prime_factors.py @@ -1,6 +1,7 @@ """ python/black : True """ + from __future__ import annotations diff --git a/maths/series/geometric_series.py b/maths/series/geometric_series.py index b8d6a86206be..55c42fd90e99 100644 --- a/maths/series/geometric_series.py +++ b/maths/series/geometric_series.py @@ -9,7 +9,6 @@ python3 geometric_series.py """ - from __future__ import annotations diff --git a/maths/series/p_series.py b/maths/series/p_series.py index a091a6f3fecf..93812f443857 100644 --- a/maths/series/p_series.py +++ b/maths/series/p_series.py @@ -9,7 +9,6 @@ python3 p_series.py """ - from __future__ import annotations diff --git a/maths/sieve_of_eratosthenes.py b/maths/sieve_of_eratosthenes.py index a0520aa5cf50..3923dc3e1612 100644 --- a/maths/sieve_of_eratosthenes.py +++ b/maths/sieve_of_eratosthenes.py @@ -10,6 +10,7 @@ doctest provider: Bruno Simas Hadlich (https://github.com/brunohadlich) Also thanks to Dmitry (https://github.com/LizardWizzard) for finding the problem """ + from __future__ import annotations import math diff --git a/maths/solovay_strassen_primality_test.py b/maths/solovay_strassen_primality_test.py index 1d11d458369a..b2d905b07bed 100644 --- a/maths/solovay_strassen_primality_test.py +++ b/maths/solovay_strassen_primality_test.py @@ -9,7 +9,6 @@ https://en.wikipedia.org/wiki/Solovay%E2%80%93Strassen_primality_test """ - import random diff --git a/maths/special_numbers/armstrong_numbers.py b/maths/special_numbers/armstrong_numbers.py index b037aacb16c3..b2b4010a8f5b 100644 --- a/maths/special_numbers/armstrong_numbers.py +++ b/maths/special_numbers/armstrong_numbers.py @@ -8,6 +8,7 @@ On-Line Encyclopedia of Integer Sequences entry: https://oeis.org/A005188 """ + PASSING = (1, 153, 370, 371, 1634, 24678051, 115132219018763992565095597973971522401) FAILING: tuple = (-153, -1, 0, 1.2, 200, "A", [], {}, None) diff --git a/maths/special_numbers/weird_number.py b/maths/special_numbers/weird_number.py index 2834a9fee31e..5c9240d0ea4e 100644 --- a/maths/special_numbers/weird_number.py +++ b/maths/special_numbers/weird_number.py @@ -3,6 +3,7 @@ Fun fact: The set of weird numbers has positive asymptotic density. """ + from math import sqrt diff --git a/maths/tanh.py b/maths/tanh.py index 38a369d9118d..011d6f17e22b 100644 --- a/maths/tanh.py +++ b/maths/tanh.py @@ -9,6 +9,7 @@ Script inspired from its corresponding Wikipedia article https://en.wikipedia.org/wiki/Activation_function """ + import numpy as np diff --git a/maths/triplet_sum.py b/maths/triplet_sum.py index af77ed145bce..e74f67daad47 100644 --- a/maths/triplet_sum.py +++ b/maths/triplet_sum.py @@ -3,6 +3,7 @@ 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 diff --git a/maths/two_pointer.py b/maths/two_pointer.py index d0fb0fc9c2f1..8a6d8eb7aff0 100644 --- a/maths/two_pointer.py +++ b/maths/two_pointer.py @@ -17,6 +17,7 @@ [1]: https://github.com/TheAlgorithms/Python/blob/master/other/two_sum.py """ + from __future__ import annotations diff --git a/maths/two_sum.py b/maths/two_sum.py index 12ad332d6c4e..58c933a5078a 100644 --- a/maths/two_sum.py +++ b/maths/two_sum.py @@ -11,6 +11,7 @@ Because nums[0] + nums[1] = 2 + 7 = 9, return [0, 1]. """ + from __future__ import annotations diff --git a/maths/volume.py b/maths/volume.py index b4df4e475783..33be9bdd131a 100644 --- a/maths/volume.py +++ b/maths/volume.py @@ -3,6 +3,7 @@ * https://en.wikipedia.org/wiki/Volume * https://en.wikipedia.org/wiki/Spherical_cap """ + from __future__ import annotations from math import pi, pow diff --git a/matrix/matrix_multiplication_recursion.py b/matrix/matrix_multiplication_recursion.py index 287142480ce7..57c4d80de017 100644 --- a/matrix/matrix_multiplication_recursion.py +++ b/matrix/matrix_multiplication_recursion.py @@ -7,6 +7,7 @@ Perform matrix multiplication using a recursive algorithm. https://en.wikipedia.org/wiki/Matrix_multiplication """ + # type Matrix = list[list[int]] # psf/black currenttly fails on this line Matrix = list[list[int]] diff --git a/networking_flow/ford_fulkerson.py b/networking_flow/ford_fulkerson.py index 7d5fb522e012..b47d3b68f3d1 100644 --- a/networking_flow/ford_fulkerson.py +++ b/networking_flow/ford_fulkerson.py @@ -6,6 +6,7 @@ (1) Start with initial flow as 0 (2) Choose the augmenting path from source to sink and add the path to flow """ + graph = [ [0, 16, 13, 0, 0, 0], [0, 0, 10, 12, 0, 0], diff --git a/neural_network/activation_functions/binary_step.py b/neural_network/activation_functions/binary_step.py index 8f8f4d405fd2..d3d774602182 100644 --- a/neural_network/activation_functions/binary_step.py +++ b/neural_network/activation_functions/binary_step.py @@ -8,7 +8,6 @@ https://en.wikipedia.org/wiki/Activation_function """ - import numpy as np diff --git a/neural_network/activation_functions/rectified_linear_unit.py b/neural_network/activation_functions/rectified_linear_unit.py index 458c6bd5c391..2d5cf96fd387 100644 --- a/neural_network/activation_functions/rectified_linear_unit.py +++ b/neural_network/activation_functions/rectified_linear_unit.py @@ -9,6 +9,7 @@ Script inspired from its corresponding Wikipedia article https://en.wikipedia.org/wiki/Rectifier_(neural_networks) """ + from __future__ import annotations import numpy as np diff --git a/neural_network/activation_functions/soboleva_modified_hyperbolic_tangent.py b/neural_network/activation_functions/soboleva_modified_hyperbolic_tangent.py index 603ac0b7e120..a053e690ba44 100644 --- a/neural_network/activation_functions/soboleva_modified_hyperbolic_tangent.py +++ b/neural_network/activation_functions/soboleva_modified_hyperbolic_tangent.py @@ -8,7 +8,6 @@ https://en.wikipedia.org/wiki/Soboleva_modified_hyperbolic_tangent """ - import numpy as np diff --git a/neural_network/back_propagation_neural_network.py b/neural_network/back_propagation_neural_network.py index bdd096b3f653..7e0bdbbe2857 100644 --- a/neural_network/back_propagation_neural_network.py +++ b/neural_network/back_propagation_neural_network.py @@ -17,6 +17,7 @@ Date: 2017.11.23 """ + import numpy as np from matplotlib import pyplot as plt diff --git a/neural_network/convolution_neural_network.py b/neural_network/convolution_neural_network.py index e9726a0cb4a7..07cc456b7466 100644 --- a/neural_network/convolution_neural_network.py +++ b/neural_network/convolution_neural_network.py @@ -1,18 +1,19 @@ """ - - - - - - -- - - - - - - - - - - - - - - - - - - - - - - - Name - - CNN - Convolution Neural Network For Photo Recognizing - Goal - - Recognize Handing Writing Word Photo - Detail: Total 5 layers neural network - * Convolution layer - * Pooling layer - * Input layer layer of BP - * Hidden layer of BP - * Output layer of BP - Author: Stephen Lee - Github: 245885195@qq.com - Date: 2017.9.20 - - - - - - -- - - - - - - - - - - - - - - - - - - - - - - + - - - - - -- - - - - - - - - - - - - - - - - - - - - - - +Name - - CNN - Convolution Neural Network For Photo Recognizing +Goal - - Recognize Handing Writing Word Photo +Detail: Total 5 layers neural network + * Convolution layer + * Pooling layer + * Input layer layer of BP + * Hidden layer of BP + * Output layer of BP +Author: Stephen Lee +Github: 245885195@qq.com +Date: 2017.9.20 +- - - - - -- - - - - - - - - - - - - - - - - - - - - - - """ + import pickle import numpy as np diff --git a/neural_network/input_data.py b/neural_network/input_data.py index 2128449c03e9..f7ae86b48e65 100644 --- a/neural_network/input_data.py +++ b/neural_network/input_data.py @@ -17,7 +17,6 @@ This module and all its submodules are deprecated. """ - import gzip import os import typing diff --git a/other/davis_putnam_logemann_loveland.py b/other/davis_putnam_logemann_loveland.py index f5fb103ba528..436577eb5b5d 100644 --- a/other/davis_putnam_logemann_loveland.py +++ b/other/davis_putnam_logemann_loveland.py @@ -8,6 +8,7 @@ For more information about the algorithm: https://en.wikipedia.org/wiki/DPLL_algorithm """ + from __future__ import annotations import random diff --git a/other/fischer_yates_shuffle.py b/other/fischer_yates_shuffle.py index fa2f4dce9db0..37e11479a4c9 100644 --- a/other/fischer_yates_shuffle.py +++ b/other/fischer_yates_shuffle.py @@ -5,6 +5,7 @@ For more details visit wikipedia/Fischer-Yates-Shuffle. """ + import random from typing import Any diff --git a/other/gauss_easter.py b/other/gauss_easter.py index 4447d4ab86af..d1c525593f79 100644 --- a/other/gauss_easter.py +++ b/other/gauss_easter.py @@ -1,6 +1,7 @@ """ https://en.wikipedia.org/wiki/Computus#Gauss'_Easter_algorithm """ + import math from datetime import datetime, timedelta diff --git a/other/majority_vote_algorithm.py b/other/majority_vote_algorithm.py index ab8b386dd2e5..8d3b56707d06 100644 --- a/other/majority_vote_algorithm.py +++ b/other/majority_vote_algorithm.py @@ -4,6 +4,7 @@ We have to solve in O(n) time and O(1) Space. URL : https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_majority_vote_algorithm """ + from collections import Counter diff --git a/other/quine.py b/other/quine.py index 500a351d38dc..08e885bc1ce7 100644 --- a/other/quine.py +++ b/other/quine.py @@ -8,4 +8,5 @@ More info on: https://en.wikipedia.org/wiki/Quine_(computing) """ + print((lambda quine: quine % quine)("print((lambda quine: quine %% quine)(%r))")) diff --git a/other/word_search.py b/other/word_search.py index a4796e220c7c..9e8acadbd9a4 100644 --- a/other/word_search.py +++ b/other/word_search.py @@ -5,7 +5,6 @@ @ https://en.wikipedia.org/wiki/Word_search """ - from random import choice, randint, shuffle # The words to display on the word search - diff --git a/physics/archimedes_principle_of_buoyant_force.py b/physics/archimedes_principle_of_buoyant_force.py index 5f569837220f..71043e0e1111 100644 --- a/physics/archimedes_principle_of_buoyant_force.py +++ b/physics/archimedes_principle_of_buoyant_force.py @@ -8,7 +8,6 @@ https://en.wikipedia.org/wiki/Archimedes%27_principle """ - # Acceleration Constant on Earth (unit m/s^2) g = 9.80665 # Also available in scipy.constants.g diff --git a/physics/center_of_mass.py b/physics/center_of_mass.py index bd9ba2480584..59c3b807f401 100644 --- a/physics/center_of_mass.py +++ b/physics/center_of_mass.py @@ -24,6 +24,7 @@ Reference: https://en.wikipedia.org/wiki/Center_of_mass """ + from collections import namedtuple Particle = namedtuple("Particle", "x y z mass") # noqa: PYI024 diff --git a/physics/in_static_equilibrium.py b/physics/in_static_equilibrium.py index d56299f60858..e3c2f9d07aed 100644 --- a/physics/in_static_equilibrium.py +++ b/physics/in_static_equilibrium.py @@ -1,6 +1,7 @@ """ Checks if a system of forces is in static equilibrium. """ + from __future__ import annotations from numpy import array, cos, cross, float64, radians, sin diff --git a/physics/n_body_simulation.py b/physics/n_body_simulation.py index ec008784ba62..4d555716199a 100644 --- a/physics/n_body_simulation.py +++ b/physics/n_body_simulation.py @@ -11,7 +11,6 @@ (See also http://www.shodor.org/refdesk/Resources/Algorithms/EulersMethod/ ) """ - from __future__ import annotations import random diff --git a/physics/rms_speed_of_molecule.py b/physics/rms_speed_of_molecule.py index 478cee01c7fd..fb23eb8a21cf 100644 --- a/physics/rms_speed_of_molecule.py +++ b/physics/rms_speed_of_molecule.py @@ -20,7 +20,6 @@ alternative method. """ - UNIVERSAL_GAS_CONSTANT = 8.3144598 diff --git a/project_euler/problem_002/sol4.py b/project_euler/problem_002/sol4.py index 70b7d6a80a1d..3a2e4fce341c 100644 --- a/project_euler/problem_002/sol4.py +++ b/project_euler/problem_002/sol4.py @@ -14,6 +14,7 @@ References: - https://en.wikipedia.org/wiki/Fibonacci_number """ + import math from decimal import Decimal, getcontext diff --git a/project_euler/problem_003/sol1.py b/project_euler/problem_003/sol1.py index a7d01bb041ba..d1c0e61cf1a6 100644 --- a/project_euler/problem_003/sol1.py +++ b/project_euler/problem_003/sol1.py @@ -10,6 +10,7 @@ References: - https://en.wikipedia.org/wiki/Prime_number#Unique_factorization """ + import math diff --git a/project_euler/problem_006/sol3.py b/project_euler/problem_006/sol3.py index 529f233c9f8e..16445258c2b7 100644 --- a/project_euler/problem_006/sol3.py +++ b/project_euler/problem_006/sol3.py @@ -15,6 +15,7 @@ Find the difference between the sum of the squares of the first one hundred natural numbers and the square of the sum. """ + import math diff --git a/project_euler/problem_007/sol2.py b/project_euler/problem_007/sol2.py index 75d351889ea8..fd99453c1100 100644 --- a/project_euler/problem_007/sol2.py +++ b/project_euler/problem_007/sol2.py @@ -11,6 +11,7 @@ References: - https://en.wikipedia.org/wiki/Prime_number """ + import math diff --git a/project_euler/problem_007/sol3.py b/project_euler/problem_007/sol3.py index 774260db99a0..39db51a93427 100644 --- a/project_euler/problem_007/sol3.py +++ b/project_euler/problem_007/sol3.py @@ -11,6 +11,7 @@ References: - https://en.wikipedia.org/wiki/Prime_number """ + import itertools import math diff --git a/project_euler/problem_008/sol2.py b/project_euler/problem_008/sol2.py index 889c3a3143c2..f83cb1db30b6 100644 --- a/project_euler/problem_008/sol2.py +++ b/project_euler/problem_008/sol2.py @@ -30,6 +30,7 @@ Find the thirteen adjacent digits in the 1000-digit number that have the greatest product. What is the value of this product? """ + from functools import reduce N = ( diff --git a/project_euler/problem_008/sol3.py b/project_euler/problem_008/sol3.py index c6081aa05e2c..bf3bcb05b7e9 100644 --- a/project_euler/problem_008/sol3.py +++ b/project_euler/problem_008/sol3.py @@ -30,6 +30,7 @@ Find the thirteen adjacent digits in the 1000-digit number that have the greatest product. What is the value of this product? """ + import sys N = ( diff --git a/project_euler/problem_010/sol2.py b/project_euler/problem_010/sol2.py index 245cca1d1720..1a1fc0f33cb3 100644 --- a/project_euler/problem_010/sol2.py +++ b/project_euler/problem_010/sol2.py @@ -10,6 +10,7 @@ References: - https://en.wikipedia.org/wiki/Prime_number """ + import math from collections.abc import Iterator from itertools import takewhile diff --git a/project_euler/problem_013/sol1.py b/project_euler/problem_013/sol1.py index 7a414a9379e0..87d0e0a60e9b 100644 --- a/project_euler/problem_013/sol1.py +++ b/project_euler/problem_013/sol1.py @@ -5,6 +5,7 @@ Work out the first ten digits of the sum of the following one-hundred 50-digit numbers. """ + import os diff --git a/project_euler/problem_014/sol2.py b/project_euler/problem_014/sol2.py index 2448e652ce5b..797b0f9886fe 100644 --- a/project_euler/problem_014/sol2.py +++ b/project_euler/problem_014/sol2.py @@ -25,6 +25,7 @@ Which starting number, under one million, produces the longest chain? """ + from __future__ import annotations COLLATZ_SEQUENCE_LENGTHS = {1: 1} diff --git a/project_euler/problem_015/sol1.py b/project_euler/problem_015/sol1.py index fb2020d6179f..fd9014a406f6 100644 --- a/project_euler/problem_015/sol1.py +++ b/project_euler/problem_015/sol1.py @@ -5,6 +5,7 @@ the right and down, there are exactly 6 routes to the bottom right corner. How many such routes are there through a 20×20 grid? """ + from math import factorial diff --git a/project_euler/problem_018/solution.py b/project_euler/problem_018/solution.py index 70306148bb9e..cbe8743be15f 100644 --- a/project_euler/problem_018/solution.py +++ b/project_euler/problem_018/solution.py @@ -27,6 +27,7 @@ 63 66 04 68 89 53 67 30 73 16 69 87 40 31 04 62 98 27 23 09 70 98 73 93 38 53 60 04 23 """ + import os diff --git a/project_euler/problem_020/sol2.py b/project_euler/problem_020/sol2.py index 676e96e7836a..a1d56ade7708 100644 --- a/project_euler/problem_020/sol2.py +++ b/project_euler/problem_020/sol2.py @@ -8,6 +8,7 @@ Find the sum of the digits in the number 100! """ + from math import factorial diff --git a/project_euler/problem_020/sol3.py b/project_euler/problem_020/sol3.py index 4f28ac5fcfde..1886e05463f4 100644 --- a/project_euler/problem_020/sol3.py +++ b/project_euler/problem_020/sol3.py @@ -8,6 +8,7 @@ Find the sum of the digits in the number 100! """ + from math import factorial diff --git a/project_euler/problem_021/sol1.py b/project_euler/problem_021/sol1.py index 353510ae8f94..f6dbfa8864db 100644 --- a/project_euler/problem_021/sol1.py +++ b/project_euler/problem_021/sol1.py @@ -13,6 +13,7 @@ Evaluate the sum of all the amicable numbers under 10000. """ + from math import sqrt diff --git a/project_euler/problem_022/sol1.py b/project_euler/problem_022/sol1.py index 982906245e87..b6386186e7df 100644 --- a/project_euler/problem_022/sol1.py +++ b/project_euler/problem_022/sol1.py @@ -14,6 +14,7 @@ What is the total of all the name scores in the file? """ + import os diff --git a/project_euler/problem_022/sol2.py b/project_euler/problem_022/sol2.py index 5ae41c84686e..f7092ea1cd12 100644 --- a/project_euler/problem_022/sol2.py +++ b/project_euler/problem_022/sol2.py @@ -14,6 +14,7 @@ What is the total of all the name scores in the file? """ + import os diff --git a/project_euler/problem_024/sol1.py b/project_euler/problem_024/sol1.py index 1c6378b38260..3fb1bd4ec582 100644 --- a/project_euler/problem_024/sol1.py +++ b/project_euler/problem_024/sol1.py @@ -9,6 +9,7 @@ What is the millionth lexicographic permutation of the digits 0, 1, 2, 3, 4, 5, 6, 7, 8 and 9? """ + from itertools import permutations diff --git a/project_euler/problem_025/sol2.py b/project_euler/problem_025/sol2.py index 6f49e89fb465..9e950b355f7a 100644 --- a/project_euler/problem_025/sol2.py +++ b/project_euler/problem_025/sol2.py @@ -23,6 +23,7 @@ What is the index of the first term in the Fibonacci sequence to contain 1000 digits? """ + from collections.abc import Generator diff --git a/project_euler/problem_030/sol1.py b/project_euler/problem_030/sol1.py index 2c6b4e4e85d5..7d83e314523f 100644 --- a/project_euler/problem_030/sol1.py +++ b/project_euler/problem_030/sol1.py @@ -1,4 +1,4 @@ -""" Problem Statement (Digit Fifth Powers): https://projecteuler.net/problem=30 +"""Problem Statement (Digit Fifth Powers): https://projecteuler.net/problem=30 Surprisingly there are only three numbers that can be written as the sum of fourth powers of their digits: @@ -21,7 +21,6 @@ and hence a number between 1000 and 1000000 """ - DIGITS_FIFTH_POWER = {str(digit): digit**5 for digit in range(10)} diff --git a/project_euler/problem_032/sol32.py b/project_euler/problem_032/sol32.py index c4d11e86c877..a402b5584061 100644 --- a/project_euler/problem_032/sol32.py +++ b/project_euler/problem_032/sol32.py @@ -12,6 +12,7 @@ HINT: Some products can be obtained in more than one way so be sure to only include it once in your sum. """ + import itertools diff --git a/project_euler/problem_033/sol1.py b/project_euler/problem_033/sol1.py index 32be424b6a7b..187fd61bde6c 100644 --- a/project_euler/problem_033/sol1.py +++ b/project_euler/problem_033/sol1.py @@ -14,6 +14,7 @@ If the product of these four fractions is given in its lowest common terms, find the value of the denominator. """ + from __future__ import annotations from fractions import Fraction diff --git a/project_euler/problem_035/sol1.py b/project_euler/problem_035/sol1.py index 644c992ed8a5..cf9f6821d798 100644 --- a/project_euler/problem_035/sol1.py +++ b/project_euler/problem_035/sol1.py @@ -15,6 +15,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 __future__ import annotations sieve = [True] * 1000001 diff --git a/project_euler/problem_036/sol1.py b/project_euler/problem_036/sol1.py index 1d27356ec51e..3865b2a39ea9 100644 --- a/project_euler/problem_036/sol1.py +++ b/project_euler/problem_036/sol1.py @@ -14,6 +14,7 @@ (Please note that the palindromic number, in either base, may not include leading zeros.) """ + from __future__ import annotations diff --git a/project_euler/problem_038/sol1.py b/project_euler/problem_038/sol1.py index e4a6d09f8f7d..5bef273ea2a9 100644 --- a/project_euler/problem_038/sol1.py +++ b/project_euler/problem_038/sol1.py @@ -37,6 +37,7 @@ => 100 <= a < 334, candidate = a * 10^6 + 2a * 10^3 + 3a = 1002003 * a """ + from __future__ import annotations diff --git a/project_euler/problem_041/sol1.py b/project_euler/problem_041/sol1.py index 2ef0120684c3..0c37f5469a6c 100644 --- a/project_euler/problem_041/sol1.py +++ b/project_euler/problem_041/sol1.py @@ -10,6 +10,7 @@ So we will check only 7 digit pandigital numbers to obtain the largest possible pandigital prime. """ + from __future__ import annotations import math diff --git a/project_euler/problem_042/solution42.py b/project_euler/problem_042/solution42.py index f8a54e40eaab..f678bcdef710 100644 --- a/project_euler/problem_042/solution42.py +++ b/project_euler/problem_042/solution42.py @@ -13,6 +13,7 @@ containing nearly two-thousand common English words, how many are triangle words? """ + import os # Precomputes a list of the 100 first triangular numbers diff --git a/project_euler/problem_043/sol1.py b/project_euler/problem_043/sol1.py index c533f40da9c9..f3a2c71edc4e 100644 --- a/project_euler/problem_043/sol1.py +++ b/project_euler/problem_043/sol1.py @@ -18,7 +18,6 @@ Find the sum of all 0 to 9 pandigital numbers with this property. """ - from itertools import permutations diff --git a/project_euler/problem_050/sol1.py b/project_euler/problem_050/sol1.py index fc6e6f2b9a5d..0a5f861f0ef0 100644 --- a/project_euler/problem_050/sol1.py +++ b/project_euler/problem_050/sol1.py @@ -15,6 +15,7 @@ Which prime, below one-million, can be written as the sum of the most consecutive primes? """ + from __future__ import annotations diff --git a/project_euler/problem_051/sol1.py b/project_euler/problem_051/sol1.py index 921704bc4455..dc740c8b947d 100644 --- a/project_euler/problem_051/sol1.py +++ b/project_euler/problem_051/sol1.py @@ -15,6 +15,7 @@ Find the smallest prime which, by replacing part of the number (not necessarily adjacent digits) with the same digit, is part of an eight prime value family. """ + from __future__ import annotations from collections import Counter diff --git a/project_euler/problem_053/sol1.py b/project_euler/problem_053/sol1.py index 0692bbe0ebb8..a32b73c545d6 100644 --- a/project_euler/problem_053/sol1.py +++ b/project_euler/problem_053/sol1.py @@ -16,6 +16,7 @@ How many, not necessarily distinct, values of nCr, for 1 ≤ n ≤ 100, are greater than one-million? """ + from math import factorial diff --git a/project_euler/problem_054/sol1.py b/project_euler/problem_054/sol1.py index 86dfa5edd2f5..66aa3a0826f5 100644 --- a/project_euler/problem_054/sol1.py +++ b/project_euler/problem_054/sol1.py @@ -40,6 +40,7 @@ https://www.codewars.com/kata/ranking-poker-hands https://www.codewars.com/kata/sortable-poker-hands """ + from __future__ import annotations import os diff --git a/project_euler/problem_058/sol1.py b/project_euler/problem_058/sol1.py index 6a991c58b6b8..1d2f406eafdb 100644 --- a/project_euler/problem_058/sol1.py +++ b/project_euler/problem_058/sol1.py @@ -33,6 +33,7 @@ count of current primes. """ + import math diff --git a/project_euler/problem_059/sol1.py b/project_euler/problem_059/sol1.py index b795dd243b08..65bfd3f0b0fb 100644 --- a/project_euler/problem_059/sol1.py +++ b/project_euler/problem_059/sol1.py @@ -25,6 +25,7 @@ must contain common English words, decrypt the message and find the sum of the ASCII values in the original text. """ + from __future__ import annotations import string diff --git a/project_euler/problem_067/sol1.py b/project_euler/problem_067/sol1.py index 2b41fedc6784..171ff8c268f6 100644 --- a/project_euler/problem_067/sol1.py +++ b/project_euler/problem_067/sol1.py @@ -11,6 +11,7 @@ 'Save Link/Target As...'), a 15K text file containing a triangle with one-hundred rows. """ + import os diff --git a/project_euler/problem_067/sol2.py b/project_euler/problem_067/sol2.py index 2e88a57170a8..4fb093d49956 100644 --- a/project_euler/problem_067/sol2.py +++ b/project_euler/problem_067/sol2.py @@ -11,6 +11,7 @@ 'Save Link/Target As...'), a 15K text file containing a triangle with one-hundred rows. """ + import os diff --git a/project_euler/problem_070/sol1.py b/project_euler/problem_070/sol1.py index f1114a280a31..9874b7418559 100644 --- a/project_euler/problem_070/sol1.py +++ b/project_euler/problem_070/sol1.py @@ -28,6 +28,7 @@ Finding totients https://en.wikipedia.org/wiki/Euler's_totient_function#Euler's_product_formula """ + from __future__ import annotations import numpy as np diff --git a/project_euler/problem_074/sol1.py b/project_euler/problem_074/sol1.py index a257d4d94fa8..91440b3fd02b 100644 --- a/project_euler/problem_074/sol1.py +++ b/project_euler/problem_074/sol1.py @@ -27,7 +27,6 @@ non-repeating terms? """ - DIGIT_FACTORIALS = { "0": 1, "1": 1, diff --git a/project_euler/problem_074/sol2.py b/project_euler/problem_074/sol2.py index b54bc023e387..52a996bfa51d 100644 --- a/project_euler/problem_074/sol2.py +++ b/project_euler/problem_074/sol2.py @@ -33,6 +33,7 @@ is greater then the desired one. After generating each chain, the length is checked and the counter increases. """ + from math import factorial DIGIT_FACTORIAL: dict[str, int] = {str(digit): factorial(digit) for digit in range(10)} diff --git a/project_euler/problem_077/sol1.py b/project_euler/problem_077/sol1.py index 6098ea9e50a6..e8f4e979a625 100644 --- a/project_euler/problem_077/sol1.py +++ b/project_euler/problem_077/sol1.py @@ -12,6 +12,7 @@ What is the first value which can be written as the sum of primes in over five thousand different ways? """ + from __future__ import annotations from functools import lru_cache diff --git a/project_euler/problem_079/sol1.py b/project_euler/problem_079/sol1.py index d34adcd243b0..74392e9bd094 100644 --- a/project_euler/problem_079/sol1.py +++ b/project_euler/problem_079/sol1.py @@ -13,6 +13,7 @@ Given that the three characters are always asked for in order, analyse the file so as to determine the shortest possible secret passcode of unknown length. """ + import itertools from pathlib import Path diff --git a/project_euler/problem_080/sol1.py b/project_euler/problem_080/sol1.py index 916998bdd8ad..8cfcbd41b588 100644 --- a/project_euler/problem_080/sol1.py +++ b/project_euler/problem_080/sol1.py @@ -6,6 +6,7 @@ square roots. Time: 5 October 2020, 18:30 """ + import decimal diff --git a/project_euler/problem_081/sol1.py b/project_euler/problem_081/sol1.py index aef6106b54df..293027bddd0e 100644 --- a/project_euler/problem_081/sol1.py +++ b/project_euler/problem_081/sol1.py @@ -13,6 +13,7 @@ and down in matrix.txt (https://projecteuler.net/project/resources/p081_matrix.txt), a 31K text file containing an 80 by 80 matrix. """ + import os diff --git a/project_euler/problem_085/sol1.py b/project_euler/problem_085/sol1.py index d0f29796498c..d0b361ee750d 100644 --- a/project_euler/problem_085/sol1.py +++ b/project_euler/problem_085/sol1.py @@ -44,6 +44,7 @@ Reference: https://en.wikipedia.org/wiki/Triangular_number https://en.wikipedia.org/wiki/Quadratic_formula """ + from __future__ import annotations from math import ceil, floor, sqrt diff --git a/project_euler/problem_086/sol1.py b/project_euler/problem_086/sol1.py index 064af215c049..cbd2b648e0ac 100644 --- a/project_euler/problem_086/sol1.py +++ b/project_euler/problem_086/sol1.py @@ -66,7 +66,6 @@ """ - from math import sqrt diff --git a/project_euler/problem_091/sol1.py b/project_euler/problem_091/sol1.py index 6c9aa3fa6c70..7db98fca0049 100644 --- a/project_euler/problem_091/sol1.py +++ b/project_euler/problem_091/sol1.py @@ -11,7 +11,6 @@ Given that 0 ≤ x1, y1, x2, y2 ≤ 50, how many right triangles can be formed? """ - from itertools import combinations, product diff --git a/project_euler/problem_101/sol1.py b/project_euler/problem_101/sol1.py index d5c503af796a..2d209333cf31 100644 --- a/project_euler/problem_101/sol1.py +++ b/project_euler/problem_101/sol1.py @@ -41,6 +41,7 @@ Find the sum of FITs for the BOPs. """ + from __future__ import annotations from collections.abc import Callable diff --git a/project_euler/problem_102/sol1.py b/project_euler/problem_102/sol1.py index 4f6e6361e3e8..85fe5eac1e22 100644 --- a/project_euler/problem_102/sol1.py +++ b/project_euler/problem_102/sol1.py @@ -18,6 +18,7 @@ NOTE: The first two examples in the file represent the triangles in the example given above. """ + from __future__ import annotations from pathlib import Path diff --git a/project_euler/problem_107/sol1.py b/project_euler/problem_107/sol1.py index 4659eac24bd3..3fe75909e2ea 100644 --- a/project_euler/problem_107/sol1.py +++ b/project_euler/problem_107/sol1.py @@ -27,6 +27,7 @@ We use Prim's algorithm to find a Minimum Spanning Tree. Reference: https://en.wikipedia.org/wiki/Prim%27s_algorithm """ + from __future__ import annotations import os diff --git a/project_euler/problem_123/sol1.py b/project_euler/problem_123/sol1.py index f74cdd999401..7239e13a51e9 100644 --- a/project_euler/problem_123/sol1.py +++ b/project_euler/problem_123/sol1.py @@ -37,6 +37,7 @@ r = 2pn when n is odd r = 2 when n is even. """ + from __future__ import annotations from collections.abc import Generator diff --git a/project_euler/problem_144/sol1.py b/project_euler/problem_144/sol1.py index b5f103b64ff5..bc16bf985f41 100644 --- a/project_euler/problem_144/sol1.py +++ b/project_euler/problem_144/sol1.py @@ -29,7 +29,6 @@ How many times does the beam hit the internal surface of the white cell before exiting? """ - from math import isclose, sqrt diff --git a/project_euler/problem_145/sol1.py b/project_euler/problem_145/sol1.py index 71b851178fdb..ce4438289722 100644 --- a/project_euler/problem_145/sol1.py +++ b/project_euler/problem_145/sol1.py @@ -13,6 +13,7 @@ How many reversible numbers are there below one-billion (10^9)? """ + EVEN_DIGITS = [0, 2, 4, 6, 8] ODD_DIGITS = [1, 3, 5, 7, 9] diff --git a/project_euler/problem_173/sol1.py b/project_euler/problem_173/sol1.py index 5416e25462cc..9235d00e1752 100644 --- a/project_euler/problem_173/sol1.py +++ b/project_euler/problem_173/sol1.py @@ -11,7 +11,6 @@ Using up to one million tiles how many different square laminae can be formed? """ - from math import ceil, sqrt diff --git a/project_euler/problem_180/sol1.py b/project_euler/problem_180/sol1.py index 12e34dcaa76b..72baed42b99e 100644 --- a/project_euler/problem_180/sol1.py +++ b/project_euler/problem_180/sol1.py @@ -44,6 +44,7 @@ Reference: https://en.wikipedia.org/wiki/Fermat%27s_Last_Theorem """ + from __future__ import annotations from fractions import Fraction diff --git a/project_euler/problem_191/sol1.py b/project_euler/problem_191/sol1.py index 6bff9d54eeca..efb2a5d086ad 100644 --- a/project_euler/problem_191/sol1.py +++ b/project_euler/problem_191/sol1.py @@ -25,7 +25,6 @@ https://projecteuler.net/problem=191 """ - cache: dict[tuple[int, int, int], int] = {} diff --git a/project_euler/problem_203/sol1.py b/project_euler/problem_203/sol1.py index da9436246a7c..8ad089ec09aa 100644 --- a/project_euler/problem_203/sol1.py +++ b/project_euler/problem_203/sol1.py @@ -27,6 +27,7 @@ References: - https://en.wikipedia.org/wiki/Pascal%27s_triangle """ + from __future__ import annotations diff --git a/project_euler/problem_551/sol1.py b/project_euler/problem_551/sol1.py index 2cd75efbb68d..100e9d41dd31 100644 --- a/project_euler/problem_551/sol1.py +++ b/project_euler/problem_551/sol1.py @@ -12,7 +12,6 @@ Find a(10^15) """ - ks = range(2, 20 + 1) base = [10**k for k in range(ks[-1] + 1)] memo: dict[int, dict[int, list[list[int]]]] = {} diff --git a/scheduling/highest_response_ratio_next.py b/scheduling/highest_response_ratio_next.py index 057bd64cc729..112c2a85220f 100644 --- a/scheduling/highest_response_ratio_next.py +++ b/scheduling/highest_response_ratio_next.py @@ -4,6 +4,7 @@ to mitigate the problem of process starvation. https://en.wikipedia.org/wiki/Highest_response_ratio_next """ + from statistics import mean import numpy as np diff --git a/scheduling/job_sequence_with_deadline.py b/scheduling/job_sequence_with_deadline.py index fccb49cd88e8..ee1fdbd0e55c 100644 --- a/scheduling/job_sequence_with_deadline.py +++ b/scheduling/job_sequence_with_deadline.py @@ -13,6 +13,7 @@ Time Complexity - O(n log n) https://medium.com/@nihardudhat2000/job-sequencing-with-deadline-17ddbb5890b5 """ + from dataclasses import dataclass from operator import attrgetter diff --git a/scheduling/non_preemptive_shortest_job_first.py b/scheduling/non_preemptive_shortest_job_first.py index 69c974b0044d..cb7ffd3abd9c 100644 --- a/scheduling/non_preemptive_shortest_job_first.py +++ b/scheduling/non_preemptive_shortest_job_first.py @@ -5,7 +5,6 @@ https://en.wikipedia.org/wiki/Shortest_job_next """ - from __future__ import annotations from statistics import mean diff --git a/scheduling/round_robin.py b/scheduling/round_robin.py index e8d54dd9a553..5f6c7f341baa 100644 --- a/scheduling/round_robin.py +++ b/scheduling/round_robin.py @@ -3,6 +3,7 @@ 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 diff --git a/scheduling/shortest_job_first.py b/scheduling/shortest_job_first.py index 871de8207308..cfd0417ea62d 100644 --- a/scheduling/shortest_job_first.py +++ b/scheduling/shortest_job_first.py @@ -3,6 +3,7 @@ Please note arrival time and burst Please use spaces to separate times entered. """ + from __future__ import annotations import pandas as pd diff --git a/searches/binary_search.py b/searches/binary_search.py index 586be39c9a0d..2e66b672d5b4 100644 --- a/searches/binary_search.py +++ b/searches/binary_search.py @@ -9,6 +9,7 @@ For manual testing run: python3 binary_search.py """ + from __future__ import annotations import bisect diff --git a/searches/binary_tree_traversal.py b/searches/binary_tree_traversal.py index 6fb841af4294..4897ef17299c 100644 --- a/searches/binary_tree_traversal.py +++ b/searches/binary_tree_traversal.py @@ -1,6 +1,7 @@ """ This is pure Python implementation of tree traversal algorithms """ + from __future__ import annotations import queue diff --git a/searches/fibonacci_search.py b/searches/fibonacci_search.py index 55fc05d39eeb..ec3dfa7f30f6 100644 --- a/searches/fibonacci_search.py +++ b/searches/fibonacci_search.py @@ -10,6 +10,7 @@ For manual testing run: python3 fibonacci_search.py """ + from functools import lru_cache diff --git a/searches/jump_search.py b/searches/jump_search.py index 3bc3c37809a1..e72d85e8a868 100644 --- a/searches/jump_search.py +++ b/searches/jump_search.py @@ -14,8 +14,7 @@ class Comparable(Protocol): - def __lt__(self, other: Any, /) -> bool: - ... + def __lt__(self, other: Any, /) -> bool: ... T = TypeVar("T", bound=Comparable) diff --git a/searches/quick_select.py b/searches/quick_select.py index 5ede8c4dd07f..c8282e1fa5fc 100644 --- a/searches/quick_select.py +++ b/searches/quick_select.py @@ -4,6 +4,7 @@ sorted, even if it is not already sorted https://en.wikipedia.org/wiki/Quickselect """ + import random diff --git a/searches/simple_binary_search.py b/searches/simple_binary_search.py index ff043d7369af..00e83ff9e4a3 100644 --- a/searches/simple_binary_search.py +++ b/searches/simple_binary_search.py @@ -7,6 +7,7 @@ For manual testing run: python3 simple_binary_search.py """ + from __future__ import annotations diff --git a/searches/tabu_search.py b/searches/tabu_search.py index d998ddc55976..fd482a81224c 100644 --- a/searches/tabu_search.py +++ b/searches/tabu_search.py @@ -24,6 +24,7 @@ -s size_of_tabu_search e.g. python tabu_search.py -f tabudata2.txt -i 4 -s 3 """ + import argparse import copy diff --git a/searches/ternary_search.py b/searches/ternary_search.py index cb36e72faac6..8dcd6b5bde2e 100644 --- a/searches/ternary_search.py +++ b/searches/ternary_search.py @@ -6,6 +6,7 @@ Time Complexity : O(log3 N) Space Complexity : O(1) """ + from __future__ import annotations # This is the precision for this function which can be altered. diff --git a/sorts/bitonic_sort.py b/sorts/bitonic_sort.py index b65f877a45e3..600f8139603a 100644 --- a/sorts/bitonic_sort.py +++ b/sorts/bitonic_sort.py @@ -3,6 +3,7 @@ Note that this program works only when size of input is a power of 2. """ + from __future__ import annotations diff --git a/sorts/bucket_sort.py b/sorts/bucket_sort.py index c016e9e26e73..1c1320a58a7d 100644 --- a/sorts/bucket_sort.py +++ b/sorts/bucket_sort.py @@ -27,6 +27,7 @@ Source: https://en.wikipedia.org/wiki/Bucket_sort """ + from __future__ import annotations diff --git a/sorts/dutch_national_flag_sort.py b/sorts/dutch_national_flag_sort.py index 758e3a887b84..b4f1665cea00 100644 --- a/sorts/dutch_national_flag_sort.py +++ b/sorts/dutch_national_flag_sort.py @@ -23,7 +23,6 @@ python dnf_sort.py """ - # Python program to sort a sequence containing only 0, 1 and 2 in a single pass. red = 0 # The first color of the flag. white = 1 # The second color of the flag. diff --git a/sorts/insertion_sort.py b/sorts/insertion_sort.py index f11ddac349a0..46b263d84a33 100644 --- a/sorts/insertion_sort.py +++ b/sorts/insertion_sort.py @@ -18,8 +18,7 @@ class Comparable(Protocol): - def __lt__(self, other: Any, /) -> bool: - ... + def __lt__(self, other: Any, /) -> bool: ... T = TypeVar("T", bound=Comparable) diff --git a/sorts/intro_sort.py b/sorts/intro_sort.py index 5a5741dc8375..1184b381b05d 100644 --- a/sorts/intro_sort.py +++ b/sorts/intro_sort.py @@ -3,6 +3,7 @@ if the size of the list is under 16, use insertion sort https://en.wikipedia.org/wiki/Introsort """ + import math diff --git a/sorts/msd_radix_sort.py b/sorts/msd_radix_sort.py index 03f84c75b9d8..6aba4263663a 100644 --- a/sorts/msd_radix_sort.py +++ b/sorts/msd_radix_sort.py @@ -4,6 +4,7 @@ them. https://en.wikipedia.org/wiki/Radix_sort """ + from __future__ import annotations diff --git a/sorts/odd_even_transposition_parallel.py b/sorts/odd_even_transposition_parallel.py index b8ab46df1e59..9d2bcdbd7576 100644 --- a/sorts/odd_even_transposition_parallel.py +++ b/sorts/odd_even_transposition_parallel.py @@ -10,6 +10,7 @@ They are synchronized with locks and message passing but other forms of synchronization could be used. """ + from multiprocessing import Lock, Pipe, Process # lock used to ensure that two processes do not access a pipe at the same time diff --git a/sorts/pigeon_sort.py b/sorts/pigeon_sort.py index 3e6d4c09c46f..fdfa692f4680 100644 --- a/sorts/pigeon_sort.py +++ b/sorts/pigeon_sort.py @@ -1,14 +1,15 @@ """ - This is an implementation of Pigeon Hole Sort. - For doctests run following command: +This is an implementation of Pigeon Hole Sort. +For doctests run following command: - python3 -m doctest -v pigeon_sort.py - or - python -m doctest -v pigeon_sort.py +python3 -m doctest -v pigeon_sort.py +or +python -m doctest -v pigeon_sort.py - For manual testing run: - python pigeon_sort.py +For manual testing run: +python pigeon_sort.py """ + from __future__ import annotations diff --git a/sorts/quick_sort.py b/sorts/quick_sort.py index 6b95fc144426..374d52e75c81 100644 --- a/sorts/quick_sort.py +++ b/sorts/quick_sort.py @@ -7,6 +7,7 @@ For manual testing run: python3 quick_sort.py """ + from __future__ import annotations from random import randrange diff --git a/sorts/radix_sort.py b/sorts/radix_sort.py index 832b6162f349..1dbf5fbd1365 100644 --- a/sorts/radix_sort.py +++ b/sorts/radix_sort.py @@ -3,6 +3,7 @@ Source: https://en.wikipedia.org/wiki/Radix_sort """ + from __future__ import annotations RADIX = 10 diff --git a/sorts/recursive_insertion_sort.py b/sorts/recursive_insertion_sort.py index 297dbe9457e6..93465350bee2 100644 --- a/sorts/recursive_insertion_sort.py +++ b/sorts/recursive_insertion_sort.py @@ -1,6 +1,7 @@ """ A recursive implementation of the insertion sort algorithm """ + from __future__ import annotations diff --git a/sorts/slowsort.py b/sorts/slowsort.py index a5f4e873ebb2..394e6eed50b1 100644 --- a/sorts/slowsort.py +++ b/sorts/slowsort.py @@ -8,6 +8,7 @@ Source: https://en.wikipedia.org/wiki/Slowsort """ + from __future__ import annotations diff --git a/sorts/tree_sort.py b/sorts/tree_sort.py index dc95856f44c8..056864957d4d 100644 --- a/sorts/tree_sort.py +++ b/sorts/tree_sort.py @@ -3,6 +3,7 @@ Build a Binary Search Tree and then iterate thru it to get a sorted list. """ + from __future__ import annotations from collections.abc import Iterator diff --git a/strings/boyer_moore_search.py b/strings/boyer_moore_search.py index 117305d32fd3..9615d2fd659b 100644 --- a/strings/boyer_moore_search.py +++ b/strings/boyer_moore_search.py @@ -17,6 +17,7 @@ n=length of main string m=length of pattern string """ + from __future__ import annotations diff --git a/strings/check_anagrams.py b/strings/check_anagrams.py index 9dcdffcfb921..d747368b2373 100644 --- a/strings/check_anagrams.py +++ b/strings/check_anagrams.py @@ -1,6 +1,7 @@ """ wiki: https://en.wikipedia.org/wiki/Anagram """ + from collections import defaultdict diff --git a/strings/top_k_frequent_words.py b/strings/top_k_frequent_words.py index f3d1e0cd5ca7..40fa7fc85cd1 100644 --- a/strings/top_k_frequent_words.py +++ b/strings/top_k_frequent_words.py @@ -13,7 +13,6 @@ def top_k_frequent_words(words, k_value): return [x[0] for x in Counter(words).most_common(k_value)] """ - from collections import Counter from functools import total_ordering diff --git a/web_programming/co2_emission.py b/web_programming/co2_emission.py index 97927e7ef541..88a426cb976d 100644 --- a/web_programming/co2_emission.py +++ b/web_programming/co2_emission.py @@ -1,6 +1,7 @@ """ Get CO2 emission data from the UK CarbonIntensity API """ + from datetime import date import requests diff --git a/web_programming/emails_from_url.py b/web_programming/emails_from_url.py index 074ef878c0d7..6b4bacfe7d5a 100644 --- a/web_programming/emails_from_url.py +++ b/web_programming/emails_from_url.py @@ -1,4 +1,5 @@ """Get the site emails from URL.""" + from __future__ import annotations __author__ = "Muhammad Umer Farooq" diff --git a/web_programming/fetch_github_info.py b/web_programming/fetch_github_info.py index aa4e1d7b1963..7a4985b68841 100644 --- a/web_programming/fetch_github_info.py +++ b/web_programming/fetch_github_info.py @@ -17,6 +17,7 @@ #!/usr/bin/env bash export USER_TOKEN="" """ + from __future__ import annotations import os diff --git a/web_programming/fetch_jobs.py b/web_programming/fetch_jobs.py index 5af90a0bb239..49abd3c88eec 100644 --- a/web_programming/fetch_jobs.py +++ b/web_programming/fetch_jobs.py @@ -1,6 +1,7 @@ """ Scraping jobs given job title and location from indeed website """ + from __future__ import annotations from collections.abc import Generator diff --git a/web_programming/get_amazon_product_data.py b/web_programming/get_amazon_product_data.py index a16175688667..c2f2ac5ab291 100644 --- a/web_programming/get_amazon_product_data.py +++ b/web_programming/get_amazon_product_data.py @@ -4,7 +4,6 @@ information will include title, URL, price, ratings, and the discount available. """ - from itertools import zip_longest import requests diff --git a/web_programming/recaptcha_verification.py b/web_programming/recaptcha_verification.py index 47c6c42f2ad0..b03afb28ec53 100644 --- a/web_programming/recaptcha_verification.py +++ b/web_programming/recaptcha_verification.py @@ -31,6 +31,7 @@ Below a Django function for the views.py file contains a login form for demonstrating recaptcha verification. """ + import requests try: diff --git a/web_programming/search_books_by_isbn.py b/web_programming/search_books_by_isbn.py index d5d4cfe92f20..07429e9a9678 100644 --- a/web_programming/search_books_by_isbn.py +++ b/web_programming/search_books_by_isbn.py @@ -3,6 +3,7 @@ ISBN: https://en.wikipedia.org/wiki/International_Standard_Book_Number """ + from json import JSONDecodeError # Workaround for requests.exceptions.JSONDecodeError import requests From 435309a61aa70303133306c9fe06a3df118c9a5c Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 18 Mar 2024 20:46:32 +0100 Subject: [PATCH 578/757] [pre-commit.ci] pre-commit autoupdate (#11325) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.3.2 → v0.3.3](https://github.com/astral-sh/ruff-pre-commit/compare/v0.3.2...v0.3.3) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a17c4c323c30..c4b30f29a5b5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.3.2 + rev: v0.3.3 hooks: - id: ruff - id: ruff-format From 8faf823e83a1b7a036e2f2569c0c185924c05307 Mon Sep 17 00:00:00 2001 From: Margaret <62753112+meg-1@users.noreply.github.com> Date: Wed, 20 Mar 2024 15:33:40 +0200 Subject: [PATCH 579/757] adding a proper fractions algorithm (#11224) * adding a proper fractions algorithm * Implementing suggestions in maths/numerical_analysis/proper_fractions.py Co-authored-by: Christian Clauss * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Implementing suggestions to proper_fractions.py * Fixing ruff errors in proper_fractions.py * Apply suggestions from code review * ruff check --output-format=github . * Update maths/numerical_analysis/proper_fractions.py * Update proper_fractions.py --------- Co-authored-by: Christian Clauss Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .github/workflows/ruff.yml | 2 +- maths/numerical_analysis/proper_fractions.py | 40 ++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 maths/numerical_analysis/proper_fractions.py diff --git a/.github/workflows/ruff.yml b/.github/workflows/ruff.yml index 9ebabed3600a..d354eba672ae 100644 --- a/.github/workflows/ruff.yml +++ b/.github/workflows/ruff.yml @@ -13,4 +13,4 @@ jobs: steps: - uses: actions/checkout@v4 - run: pip install --user ruff - - run: ruff --output-format=github . + - run: ruff check --output-format=github . diff --git a/maths/numerical_analysis/proper_fractions.py b/maths/numerical_analysis/proper_fractions.py new file mode 100644 index 000000000000..774ce9a24876 --- /dev/null +++ b/maths/numerical_analysis/proper_fractions.py @@ -0,0 +1,40 @@ +from math import gcd + + +def proper_fractions(denominator: int) -> list[str]: + """ + this algorithm returns a list of proper fractions, in the + range between 0 and 1, which can be formed with the given denominator + https://en.wikipedia.org/wiki/Fraction#Proper_and_improper_fractions + + >>> proper_fractions(10) + ['1/10', '3/10', '7/10', '9/10'] + >>> proper_fractions(5) + ['1/5', '2/5', '3/5', '4/5'] + >>> proper_fractions(-15) + Traceback (most recent call last): + ... + ValueError: The Denominator Cannot be less than 0 + >>> proper_fractions(0) + [] + >>> proper_fractions(1.2) + Traceback (most recent call last): + ... + ValueError: The Denominator must be an integer + """ + + if denominator < 0: + raise ValueError("The Denominator Cannot be less than 0") + elif isinstance(denominator, float): + raise ValueError("The Denominator must be an integer") + return [ + f"{numerator}/{denominator}" + for numerator in range(1, denominator) + if gcd(numerator, denominator) == 1 + ] + + +if __name__ == "__main__": + from doctest import testmod + + testmod() From a936e94704b09841784358a4ac002401f3faceed Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Wed, 20 Mar 2024 17:00:17 +0300 Subject: [PATCH 580/757] Enable ruff ARG001 rule (#11321) * Enable ruff ARG001 rule * Fix dynamic_programming/combination_sum_iv.py * Fix machine_learning/frequent_pattern_growth.py * Fix other/davis_putnam_logemann_loveland.py * Fix other/password.py * Fix * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix physics/n_body_simulation.py * Fix project_euler/problem_145/sol1.py * Fix project_euler/problem_174/sol1.py * Fix scheduling/highest_response_ratio_next.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix * Fix * Fix scheduling/job_sequencing_with_deadline.py * Fix scheduling/job_sequencing_with_deadline.py * Fix * Fix --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- dynamic_programming/combination_sum_iv.py | 11 +++++------ machine_learning/frequent_pattern_growth.py | 4 ++-- other/davis_putnam_logemann_loveland.py | 3 ++- other/password.py | 12 ------------ physics/n_body_simulation.py | 2 +- project_euler/problem_145/sol1.py | 2 +- project_euler/problem_174/sol1.py | 4 +++- pyproject.toml | 1 - scheduling/highest_response_ratio_next.py | 5 ++++- scheduling/job_sequencing_with_deadline.py | 7 +++---- web_programming/nasa_data.py | 2 +- 11 files changed, 22 insertions(+), 31 deletions(-) diff --git a/dynamic_programming/combination_sum_iv.py b/dynamic_programming/combination_sum_iv.py index b2aeb0824f64..4526729b70b7 100644 --- a/dynamic_programming/combination_sum_iv.py +++ b/dynamic_programming/combination_sum_iv.py @@ -22,12 +22,12 @@ """ -def combination_sum_iv(n: int, array: list[int], target: int) -> int: +def combination_sum_iv(array: list[int], target: int) -> int: """ Function checks the all possible combinations, and returns the count of possible combination in exponential Time Complexity. - >>> combination_sum_iv(3, [1,2,5], 5) + >>> combination_sum_iv([1,2,5], 5) 9 """ @@ -41,13 +41,13 @@ def count_of_possible_combinations(target: int) -> int: return count_of_possible_combinations(target) -def combination_sum_iv_dp_array(n: int, array: list[int], target: int) -> int: +def combination_sum_iv_dp_array(array: list[int], target: int) -> int: """ Function checks the all possible combinations, and returns the count of possible combination in O(N^2) Time Complexity as we are using Dynamic programming array here. - >>> combination_sum_iv_dp_array(3, [1,2,5], 5) + >>> combination_sum_iv_dp_array([1,2,5], 5) 9 """ @@ -96,7 +96,6 @@ def combination_sum_iv_bottom_up(n: int, array: list[int], target: int) -> int: import doctest doctest.testmod() - n = 3 target = 5 array = [1, 2, 5] - print(combination_sum_iv(n, array, target)) + print(combination_sum_iv(array, target)) diff --git a/machine_learning/frequent_pattern_growth.py b/machine_learning/frequent_pattern_growth.py index 6b9870f5e1d2..947f8692f298 100644 --- a/machine_learning/frequent_pattern_growth.py +++ b/machine_learning/frequent_pattern_growth.py @@ -240,7 +240,7 @@ def ascend_tree(leaf_node: TreeNode, prefix_path: list[str]) -> None: ascend_tree(leaf_node.parent, prefix_path) -def find_prefix_path(base_pat: frozenset, tree_node: TreeNode | None) -> dict: +def find_prefix_path(base_pat: frozenset, tree_node: TreeNode | None) -> dict: # noqa: ARG001 """ Find the conditional pattern base for a given base pattern. @@ -277,7 +277,7 @@ def find_prefix_path(base_pat: frozenset, tree_node: TreeNode | None) -> dict: def mine_tree( - in_tree: TreeNode, + in_tree: TreeNode, # noqa: ARG001 header_table: dict, min_sup: int, pre_fix: set, diff --git a/other/davis_putnam_logemann_loveland.py b/other/davis_putnam_logemann_loveland.py index 436577eb5b5d..5c6e2d9ffd5e 100644 --- a/other/davis_putnam_logemann_loveland.py +++ b/other/davis_putnam_logemann_loveland.py @@ -227,7 +227,8 @@ def find_pure_symbols( def find_unit_clauses( - clauses: list[Clause], model: dict[str, bool | None] + clauses: list[Clause], + model: dict[str, bool | None], # noqa: ARG001 ) -> tuple[list[str], dict[str, bool | None]]: """ Returns the unit symbols and their values to satisfy clause. diff --git a/other/password.py b/other/password.py index 1ce0d52316e6..dff1316c049c 100644 --- a/other/password.py +++ b/other/password.py @@ -51,18 +51,6 @@ def random(chars_incl: str, i: int) -> str: return "".join(secrets.choice(chars_incl) for _ in range(i)) -def random_number(chars_incl, i): - pass # Put your code here... - - -def random_letters(chars_incl, i): - pass # Put your code here... - - -def random_characters(chars_incl, i): - pass # Put your code here... - - def is_strong_password(password: str, min_length: int = 8) -> bool: """ This will check whether a given password is strong or not. The password must be at diff --git a/physics/n_body_simulation.py b/physics/n_body_simulation.py index 4d555716199a..9bfb6b3c6864 100644 --- a/physics/n_body_simulation.py +++ b/physics/n_body_simulation.py @@ -239,7 +239,7 @@ def plot( ax.add_patch(patch) # Function called at each step of the animation - def update(frame: int) -> list[plt.Circle]: + def update(frame: int) -> list[plt.Circle]: # noqa: ARG001 update_step(body_system, DELTA_TIME, patches) return patches diff --git a/project_euler/problem_145/sol1.py b/project_euler/problem_145/sol1.py index ce4438289722..583bb03a0a90 100644 --- a/project_euler/problem_145/sol1.py +++ b/project_euler/problem_145/sol1.py @@ -110,7 +110,7 @@ def reversible_numbers( if (length - 1) % 4 == 0: return 0 - return slow_reversible_numbers(length, 0, [0] * length, length) + return slow_reversible_numbers(remaining_length, remainder, digits, length) def solution(max_power: int = 9) -> int: diff --git a/project_euler/problem_174/sol1.py b/project_euler/problem_174/sol1.py index cbc0df5a9d65..33c1b158adbb 100644 --- a/project_euler/problem_174/sol1.py +++ b/project_euler/problem_174/sol1.py @@ -26,6 +26,8 @@ def solution(t_limit: int = 1000000, n_limit: int = 10) -> int: Return the sum of N(n) for 1 <= n <= n_limit. >>> solution(1000,5) + 222 + >>> solution(1000,10) 249 >>> solution(10000,10) 2383 @@ -45,7 +47,7 @@ def solution(t_limit: int = 1000000, n_limit: int = 10) -> int: for hole_width in range(hole_width_lower_bound, outer_width - 1, 2): count[outer_width * outer_width - hole_width * hole_width] += 1 - return sum(1 for n in count.values() if 1 <= n <= 10) + return sum(1 for n in count.values() if 1 <= n <= n_limit) if __name__ == "__main__": diff --git a/pyproject.toml b/pyproject.toml index 2e7da519da8b..a69ab7aa6437 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,5 @@ [tool.ruff] lint.ignore = [ # `ruff rule S101` for a description of that rule - "ARG001", # Unused function argument `amount` -- FIX ME? "B904", # Within an `except` clause, raise exceptions with `raise ... from err` -- FIX ME "B905", # `zip()` without an explicit `strict=` parameter -- FIX ME "DTZ001", # The use of `datetime.datetime()` without `tzinfo` argument is not allowed -- FIX ME diff --git a/scheduling/highest_response_ratio_next.py b/scheduling/highest_response_ratio_next.py index 112c2a85220f..b549835616bf 100644 --- a/scheduling/highest_response_ratio_next.py +++ b/scheduling/highest_response_ratio_next.py @@ -75,7 +75,10 @@ def calculate_turn_around_time( def calculate_waiting_time( - process_name: list, turn_around_time: list, burst_time: list, no_of_process: int + process_name: list, # noqa: ARG001 + turn_around_time: list, + burst_time: list, + no_of_process: int, ) -> list: """ Calculate the waiting time of each processes. diff --git a/scheduling/job_sequencing_with_deadline.py b/scheduling/job_sequencing_with_deadline.py index 7b23c0b3575f..13946948492f 100644 --- a/scheduling/job_sequencing_with_deadline.py +++ b/scheduling/job_sequencing_with_deadline.py @@ -1,9 +1,8 @@ -def job_sequencing_with_deadlines(num_jobs: int, jobs: list) -> list: +def job_sequencing_with_deadlines(jobs: list) -> list: """ Function to find the maximum profit by doing jobs in a given time frame Args: - num_jobs [int]: Number of jobs jobs [list]: A list of tuples of (job_id, deadline, profit) Returns: @@ -11,10 +10,10 @@ def job_sequencing_with_deadlines(num_jobs: int, jobs: list) -> list: in a given time frame Examples: - >>> job_sequencing_with_deadlines(4, + >>> job_sequencing_with_deadlines( ... [(1, 4, 20), (2, 1, 10), (3, 1, 40), (4, 1, 30)]) [2, 60] - >>> job_sequencing_with_deadlines(5, + >>> job_sequencing_with_deadlines( ... [(1, 2, 100), (2, 1, 19), (3, 2, 27), (4, 1, 25), (5, 1, 15)]) [2, 127] """ diff --git a/web_programming/nasa_data.py b/web_programming/nasa_data.py index c0a2c4fdd1a7..81125e0a4f05 100644 --- a/web_programming/nasa_data.py +++ b/web_programming/nasa_data.py @@ -3,7 +3,7 @@ import requests -def get_apod_data(api_key: str, download: bool = False, path: str = ".") -> dict: +def get_apod_data(api_key: str) -> dict: """ Get the APOD(Astronomical Picture of the day) data Get your API Key from: https://api.nasa.gov/ From 481c071e8423ed3b17ddff96b905da3d27d4f7b4 Mon Sep 17 00:00:00 2001 From: Mehdi Oudghiri <144174136+PAxitoo@users.noreply.github.com> Date: Wed, 20 Mar 2024 15:07:55 +0100 Subject: [PATCH 581/757] add vicsek to fractals (#11306) Co-authored-by: BastosLaG --- fractals/vicsek.py | 76 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 fractals/vicsek.py diff --git a/fractals/vicsek.py b/fractals/vicsek.py new file mode 100644 index 000000000000..290fe95b79b4 --- /dev/null +++ b/fractals/vicsek.py @@ -0,0 +1,76 @@ +"""Authors Bastien Capiaux & Mehdi Oudghiri + +The Vicsek fractal algorithm is a recursive algorithm that creates a +pattern known as the Vicsek fractal or the Vicsek square. +It is based on the concept of self-similarity, where the pattern at each +level of recursion resembles the overall pattern. +The algorithm involves dividing a square into 9 equal smaller squares, +removing the center square, and then repeating this process on the remaining 8 squares. +This results in a pattern that exhibits self-similarity and has a +square-shaped outline with smaller squares within it. + +Source: https://en.wikipedia.org/wiki/Vicsek_fractal +""" + +import turtle + + +def draw_cross(x: float, y: float, length: float): + """ + Draw a cross at the specified position and with the specified length. + """ + turtle.up() + turtle.goto(x - length / 2, y - length / 6) + turtle.down() + turtle.seth(0) + turtle.begin_fill() + for _ in range(4): + turtle.fd(length / 3) + turtle.right(90) + turtle.fd(length / 3) + turtle.left(90) + turtle.fd(length / 3) + turtle.left(90) + turtle.end_fill() + + +def draw_fractal_recursive(x: float, y: float, length: float, depth: float): + """ + Recursively draw the Vicsek fractal at the specified position, with the + specified length and depth. + """ + if depth == 0: + draw_cross(x, y, length) + return + + draw_fractal_recursive(x, y, length / 3, depth - 1) + draw_fractal_recursive(x + length / 3, y, length / 3, depth - 1) + draw_fractal_recursive(x - length / 3, y, length / 3, depth - 1) + draw_fractal_recursive(x, y + length / 3, length / 3, depth - 1) + draw_fractal_recursive(x, y - length / 3, length / 3, depth - 1) + + +def set_color(rgb: str): + turtle.color(rgb) + + +def draw_vicsek_fractal(x: float, y: float, length: float, depth: float, color="blue"): + """ + Draw the Vicsek fractal at the specified position, with the specified + length and depth. + """ + turtle.speed(0) + turtle.hideturtle() + set_color(color) + draw_fractal_recursive(x, y, length, depth) + turtle.Screen().update() + + +def main(): + draw_vicsek_fractal(0, 0, 800, 4) + + turtle.done() + + +if __name__ == "__main__": + main() From 102e9a31b673e5444678fd55640a0038b6a16a9d Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Mon, 25 Mar 2024 10:43:24 +0300 Subject: [PATCH 582/757] Enable ruff DTZ001 rule (#11326) * updating DIRECTORY.md * Enable ruff DTZ001 rule * Fix other/gauss_easter.py * Fix * Fix * Fix * Fix * Fix * Fix --------- Co-authored-by: MaximSmolskiy --- DIRECTORY.md | 2 ++ other/gauss_easter.py | 16 ++++++++-------- pyproject.toml | 1 - 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index 2f828aa512a9..01667c9feee8 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -419,6 +419,7 @@ * [Koch Snowflake](fractals/koch_snowflake.py) * [Mandelbrot](fractals/mandelbrot.py) * [Sierpinski Triangle](fractals/sierpinski_triangle.py) + * [Vicsek](fractals/vicsek.py) ## Fuzzy Logic * [Fuzzy Operations](fuzzy_logic/fuzzy_operations.py) @@ -678,6 +679,7 @@ * [Newton Forward Interpolation](maths/numerical_analysis/newton_forward_interpolation.py) * [Newton Raphson](maths/numerical_analysis/newton_raphson.py) * [Numerical Integration](maths/numerical_analysis/numerical_integration.py) + * [Proper Fractions](maths/numerical_analysis/proper_fractions.py) * [Runge Kutta](maths/numerical_analysis/runge_kutta.py) * [Runge Kutta Fehlberg 45](maths/numerical_analysis/runge_kutta_fehlberg_45.py) * [Runge Kutta Gills](maths/numerical_analysis/runge_kutta_gills.py) diff --git a/other/gauss_easter.py b/other/gauss_easter.py index d1c525593f79..7ccea7f5bbf0 100644 --- a/other/gauss_easter.py +++ b/other/gauss_easter.py @@ -3,7 +3,7 @@ """ import math -from datetime import datetime, timedelta +from datetime import UTC, datetime, timedelta def gauss_easter(year: int) -> datetime: @@ -11,16 +11,16 @@ def gauss_easter(year: int) -> datetime: Calculation Gregorian easter date for given year >>> gauss_easter(2007) - datetime.datetime(2007, 4, 8, 0, 0) + datetime.datetime(2007, 4, 8, 0, 0, tzinfo=datetime.timezone.utc) >>> gauss_easter(2008) - datetime.datetime(2008, 3, 23, 0, 0) + datetime.datetime(2008, 3, 23, 0, 0, tzinfo=datetime.timezone.utc) >>> gauss_easter(2020) - datetime.datetime(2020, 4, 12, 0, 0) + datetime.datetime(2020, 4, 12, 0, 0, tzinfo=datetime.timezone.utc) >>> gauss_easter(2021) - datetime.datetime(2021, 4, 4, 0, 0) + datetime.datetime(2021, 4, 4, 0, 0, tzinfo=datetime.timezone.utc) """ metonic_cycle = year % 19 julian_leap_year = year % 4 @@ -45,11 +45,11 @@ def gauss_easter(year: int) -> datetime: ) % 7 if days_to_add == 29 and days_from_phm_to_sunday == 6: - return datetime(year, 4, 19) + return datetime(year, 4, 19, tzinfo=UTC) elif days_to_add == 28 and days_from_phm_to_sunday == 6: - return datetime(year, 4, 18) + return datetime(year, 4, 18, tzinfo=UTC) else: - return datetime(year, 3, 22) + timedelta( + return datetime(year, 3, 22, tzinfo=UTC) + timedelta( days=int(days_to_add + days_from_phm_to_sunday) ) diff --git a/pyproject.toml b/pyproject.toml index a69ab7aa6437..09093433a47a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,7 +2,6 @@ lint.ignore = [ # `ruff rule S101` for a description of that rule "B904", # Within an `except` clause, raise exceptions with `raise ... from err` -- FIX ME "B905", # `zip()` without an explicit `strict=` parameter -- FIX ME - "DTZ001", # The use of `datetime.datetime()` without `tzinfo` argument is not allowed -- FIX ME "DTZ005", # The use of `datetime.datetime.now()` without `tzinfo` argument is not allowed -- FIX ME "E741", # Ambiguous variable name 'l' -- FIX ME "EM101", # Exception must not use a string literal, assign to variable first From ead54314f26615769ce8b055b25e25f9dbbb1f83 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 25 Mar 2024 20:21:21 +0100 Subject: [PATCH 583/757] [pre-commit.ci] pre-commit autoupdate (#11328) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.3.3 → v0.3.4](https://github.com/astral-sh/ruff-pre-commit/compare/v0.3.3...v0.3.4) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c4b30f29a5b5..8b101207d5ff 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.3.3 + rev: v0.3.4 hooks: - id: ruff - id: ruff-format From b5cb1fba0debb5df7e5aea6bb069c6e3f130dba5 Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Mon, 25 Mar 2024 23:54:11 +0300 Subject: [PATCH 584/757] Enable ruff DTZ005 rule (#11327) * Enable ruff DTZ005 rule * Fix other/gauss_easter.py * Fix * Fix web_programming/instagram_pic.py * Fix web_programming/instagram_video.py * Apply suggestions from code review * Update instagram_pic.py * datetime.now(tz=UTC).astimezone() * .astimezone() * Fix --------- Co-authored-by: Christian Clauss --- other/gauss_easter.py | 4 ++-- pyproject.toml | 1 - web_programming/instagram_pic.py | 4 ++-- web_programming/instagram_video.py | 4 ++-- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/other/gauss_easter.py b/other/gauss_easter.py index 7ccea7f5bbf0..8c8c37c92796 100644 --- a/other/gauss_easter.py +++ b/other/gauss_easter.py @@ -55,6 +55,6 @@ def gauss_easter(year: int) -> datetime: if __name__ == "__main__": - for year in (1994, 2000, 2010, 2021, 2023): - tense = "will be" if year > datetime.now().year else "was" + for year in (1994, 2000, 2010, 2021, 2023, 2032, 2100): + tense = "will be" if year > datetime.now(tz=UTC).year else "was" print(f"Easter in {year} {tense} {gauss_easter(year)}") diff --git a/pyproject.toml b/pyproject.toml index 09093433a47a..5187491e5ee7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,7 +2,6 @@ lint.ignore = [ # `ruff rule S101` for a description of that rule "B904", # Within an `except` clause, raise exceptions with `raise ... from err` -- FIX ME "B905", # `zip()` without an explicit `strict=` parameter -- FIX ME - "DTZ005", # The use of `datetime.datetime.now()` without `tzinfo` argument is not allowed -- FIX ME "E741", # Ambiguous variable name 'l' -- FIX ME "EM101", # Exception must not use a string literal, assign to variable first "EXE001", # Shebang is present but file is not executable" -- FIX ME diff --git a/web_programming/instagram_pic.py b/web_programming/instagram_pic.py index 2630c8659232..2d987c1766dc 100644 --- a/web_programming/instagram_pic.py +++ b/web_programming/instagram_pic.py @@ -1,4 +1,4 @@ -from datetime import datetime +from datetime import UTC, datetime import requests from bs4 import BeautifulSoup @@ -36,7 +36,7 @@ def download_image(url: str) -> str: if not image_data: return f"Failed to download the image from {image_url}." - file_name = f"{datetime.now():%Y-%m-%d_%H:%M:%S}.jpg" + file_name = f"{datetime.now(tz=UTC).astimezone():%Y-%m-%d_%H:%M:%S}.jpg" with open(file_name, "wb") as out_file: out_file.write(image_data) return f"Image downloaded and saved in the file {file_name}" diff --git a/web_programming/instagram_video.py b/web_programming/instagram_video.py index 243cece1a50e..1f1b0e297034 100644 --- a/web_programming/instagram_video.py +++ b/web_programming/instagram_video.py @@ -1,4 +1,4 @@ -from datetime import datetime +from datetime import UTC, datetime import requests @@ -11,7 +11,7 @@ def download_video(url: str) -> bytes: if __name__ == "__main__": url = input("Enter Video/IGTV url: ").strip() - file_name = f"{datetime.now():%Y-%m-%d_%H:%M:%S}.mp4" + file_name = f"{datetime.now(tz=UTC).astimezone():%Y-%m-%d_%H:%M:%S}.mp4" with open(file_name, "wb") as fp: fp.write(download_video(url)) print(f"Done. Video saved to disk as {file_name}.") From 19fd435042a3191f6a5787a6eaf58e9c47920845 Mon Sep 17 00:00:00 2001 From: MrBubb1es <63935943+MrBubb1es@users.noreply.github.com> Date: Thu, 28 Mar 2024 12:19:51 -0500 Subject: [PATCH 585/757] Improved doctests for some functions (#11334) --- .../binary_tree/binary_tree_traversals.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/data_structures/binary_tree/binary_tree_traversals.py b/data_structures/binary_tree/binary_tree_traversals.py index 2b33cdca4fed..49c208335b2c 100644 --- a/data_structures/binary_tree/binary_tree_traversals.py +++ b/data_structures/binary_tree/binary_tree_traversals.py @@ -97,6 +97,8 @@ def level_order(root: Node | None) -> Generator[int, None, None]: """ Returns a list of nodes value from a whole binary tree in Level Order Traverse. Level Order traverse: Visit nodes of the tree level-by-level. + >>> list(level_order(make_tree())) + [1, 2, 3, 4, 5] """ if root is None: @@ -120,6 +122,10 @@ def get_nodes_from_left_to_right( """ Returns a list of nodes value from a particular level: Left to right direction of the binary tree. + >>> list(get_nodes_from_left_to_right(make_tree(), 1)) + [1] + >>> list(get_nodes_from_left_to_right(make_tree(), 2)) + [2, 3] """ def populate_output(root: Node | None, level: int) -> Generator[int, None, None]: @@ -140,10 +146,14 @@ def get_nodes_from_right_to_left( """ Returns a list of nodes value from a particular level: Right to left direction of the binary tree. + >>> list(get_nodes_from_right_to_left(make_tree(), 1)) + [1] + >>> list(get_nodes_from_right_to_left(make_tree(), 2)) + [3, 2] """ def populate_output(root: Node | None, level: int) -> Generator[int, None, None]: - if root is None: + if not root: return if level == 1: yield root.data @@ -158,6 +168,8 @@ def zigzag(root: Node | None) -> Generator[int, None, None]: """ ZigZag traverse: Returns a list of nodes value from left to right and right to left, alternatively. + >>> list(zigzag(make_tree())) + [1, 3, 2, 4, 5] """ if root is None: return From 516a3028d1f6b6e7e11ae4501fdaee50a0965464 Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Thu, 28 Mar 2024 20:25:41 +0300 Subject: [PATCH 586/757] Enable ruff PLR5501 rule (#11332) * Enable ruff PLR5501 rule * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- backtracking/crossword_puzzle_solver.py | 5 +- cellular_automata/game_of_life.py | 5 +- ciphers/decrypt_caesar_with_chi_squared.py | 21 +++--- data_structures/binary_tree/avl_tree.py | 10 +-- .../binary_tree/binary_search_tree.py | 9 ++- .../binary_search_tree_recursive.py | 22 +++---- data_structures/binary_tree/red_black_tree.py | 66 +++++++++---------- data_structures/binary_tree/treap.py | 29 ++++---- data_structures/heap/max_heap.py | 7 +- .../stacks/infix_to_prefix_conversion.py | 13 ++-- data_structures/trie/radix_tree.py | 45 +++++++------ divide_and_conquer/convex_hull.py | 15 ++--- graphs/graph_list.py | 46 ++++++------- graphs/minimum_spanning_tree_prims.py | 7 +- graphs/multi_heuristic_astar.py | 33 +++++----- machine_learning/forecasting/run.py | 7 +- maths/largest_of_very_large_numbers.py | 9 ++- maths/pollard_rho.py | 13 ++-- matrix/cramers_rule_2x2.py | 15 ++--- project_euler/problem_019/sol1.py | 7 +- pyproject.toml | 1 - searches/hill_climbing.py | 7 +- searches/interpolation_search.py | 35 +++++----- strings/min_cost_string_conversion.py | 23 ++++--- 24 files changed, 211 insertions(+), 239 deletions(-) diff --git a/backtracking/crossword_puzzle_solver.py b/backtracking/crossword_puzzle_solver.py index b9c01c4efea9..e702c7e52153 100644 --- a/backtracking/crossword_puzzle_solver.py +++ b/backtracking/crossword_puzzle_solver.py @@ -28,9 +28,8 @@ def is_valid( if vertical: if row + i >= len(puzzle) or puzzle[row + i][col] != "": return False - else: - if col + i >= len(puzzle[0]) or puzzle[row][col + i] != "": - return False + elif col + i >= len(puzzle[0]) or puzzle[row][col + i] != "": + return False return True diff --git a/cellular_automata/game_of_life.py b/cellular_automata/game_of_life.py index 67e647d6475b..76276b272d65 100644 --- a/cellular_automata/game_of_life.py +++ b/cellular_automata/game_of_life.py @@ -101,9 +101,8 @@ def __judge_point(pt: bool, neighbours: list[list[bool]]) -> bool: state = True elif alive > 3: state = False - else: - if alive == 3: - state = True + elif alive == 3: + state = True return state diff --git a/ciphers/decrypt_caesar_with_chi_squared.py b/ciphers/decrypt_caesar_with_chi_squared.py index 6c36860207cd..10832203e531 100644 --- a/ciphers/decrypt_caesar_with_chi_squared.py +++ b/ciphers/decrypt_caesar_with_chi_squared.py @@ -206,20 +206,19 @@ def decrypt_caesar_with_chi_squared( # Add the margin of error to the total chi squared statistic chi_squared_statistic += chi_letter_value - else: - if letter.lower() in frequencies: - # Get the amount of times the letter occurs in the message - occurrences = decrypted_with_shift.count(letter) + elif letter.lower() in frequencies: + # Get the amount of times the letter occurs in the message + occurrences = decrypted_with_shift.count(letter) - # Get the excepcted amount of times the letter should appear based - # on letter frequencies - expected = frequencies[letter] * occurrences + # Get the excepcted amount of times the letter should appear based + # on letter frequencies + expected = frequencies[letter] * occurrences - # Complete the chi squared statistic formula - chi_letter_value = ((occurrences - expected) ** 2) / expected + # Complete the chi squared statistic formula + chi_letter_value = ((occurrences - expected) ** 2) / expected - # Add the margin of error to the total chi squared statistic - chi_squared_statistic += chi_letter_value + # Add the margin of error to the total chi squared statistic + chi_squared_statistic += chi_letter_value # Add the data to the chi_squared_statistic_values dictionary chi_squared_statistic_values[shift] = ( diff --git a/data_structures/binary_tree/avl_tree.py b/data_structures/binary_tree/avl_tree.py index 041ed7e36d16..9fca7237404c 100644 --- a/data_structures/binary_tree/avl_tree.py +++ b/data_structures/binary_tree/avl_tree.py @@ -215,11 +215,11 @@ def del_node(root: MyNode, data: Any) -> MyNode | None: return root else: root.set_left(del_node(left_child, data)) - else: # root.get_data() < data - if right_child is None: - return root - else: - root.set_right(del_node(right_child, data)) + # root.get_data() < data + elif right_child is None: + return root + else: + root.set_right(del_node(right_child, data)) if get_height(right_child) - get_height(left_child) == 2: assert right_child is not None diff --git a/data_structures/binary_tree/binary_search_tree.py b/data_structures/binary_tree/binary_search_tree.py index 08a60a12065d..090e3e25fe6d 100644 --- a/data_structures/binary_tree/binary_search_tree.py +++ b/data_structures/binary_tree/binary_search_tree.py @@ -185,12 +185,11 @@ def __insert(self, value) -> None: break else: parent_node = parent_node.left + elif parent_node.right is None: + parent_node.right = new_node + break else: - if parent_node.right is None: - parent_node.right = new_node - break - else: - parent_node = parent_node.right + parent_node = parent_node.right new_node.parent = parent_node def insert(self, *values) -> Self: diff --git a/data_structures/binary_tree/binary_search_tree_recursive.py b/data_structures/binary_tree/binary_search_tree_recursive.py index 6af1b053f42c..d94ac5253360 100644 --- a/data_structures/binary_tree/binary_search_tree_recursive.py +++ b/data_structures/binary_tree/binary_search_tree_recursive.py @@ -74,14 +74,13 @@ def put(self, label: int) -> None: def _put(self, node: Node | None, label: int, parent: Node | None = None) -> Node: if node is None: node = Node(label, parent) + elif label < node.label: + node.left = self._put(node.left, label, node) + elif label > node.label: + node.right = self._put(node.right, label, node) else: - if label < node.label: - node.left = self._put(node.left, label, node) - elif label > node.label: - node.right = self._put(node.right, label, node) - else: - msg = f"Node with label {label} already exists" - raise ValueError(msg) + msg = f"Node with label {label} already exists" + raise ValueError(msg) return node @@ -106,11 +105,10 @@ def _search(self, node: Node | None, label: int) -> Node: if node is None: msg = f"Node with label {label} does not exist" raise ValueError(msg) - else: - if label < node.label: - node = self._search(node.left, label) - elif label > node.label: - node = self._search(node.right, label) + elif label < node.label: + node = self._search(node.left, label) + elif label > node.label: + node = self._search(node.right, label) return node diff --git a/data_structures/binary_tree/red_black_tree.py b/data_structures/binary_tree/red_black_tree.py index 3b5845cd957b..bdd808c828e0 100644 --- a/data_structures/binary_tree/red_black_tree.py +++ b/data_structures/binary_tree/red_black_tree.py @@ -107,12 +107,11 @@ def insert(self, label: int) -> RedBlackTree: else: self.left = RedBlackTree(label, 1, self) self.left._insert_repair() + elif self.right: + self.right.insert(label) else: - if self.right: - self.right.insert(label) - else: - self.right = RedBlackTree(label, 1, self) - self.right._insert_repair() + self.right = RedBlackTree(label, 1, self) + self.right._insert_repair() return self.parent or self def _insert_repair(self) -> None: @@ -178,36 +177,34 @@ def remove(self, label: int) -> RedBlackTree: # noqa: PLR0912 self.parent.left = None else: self.parent.right = None - else: - # The node is black - if child is None: - # This node and its child are black - if self.parent is None: - # The tree is now empty - return RedBlackTree(None) - else: - self._remove_repair() - if self.is_left(): - self.parent.left = None - else: - self.parent.right = None - self.parent = None + # The node is black + elif child is None: + # This node and its child are black + if self.parent is None: + # The tree is now empty + return RedBlackTree(None) else: - # This node is black and its child is red - # Move the child node here and make it black - self.label = child.label - self.left = child.left - self.right = child.right - if self.left: - self.left.parent = self - if self.right: - self.right.parent = self + self._remove_repair() + if self.is_left(): + self.parent.left = None + else: + self.parent.right = None + self.parent = None + else: + # This node is black and its child is red + # Move the child node here and make it black + self.label = child.label + self.left = child.left + self.right = child.right + if self.left: + self.left.parent = self + if self.right: + self.right.parent = self elif self.label is not None and self.label > label: if self.left: self.left.remove(label) - else: - if self.right: - self.right.remove(label) + elif self.right: + self.right.remove(label) return self.parent or self def _remove_repair(self) -> None: @@ -369,11 +366,10 @@ def search(self, label: int) -> RedBlackTree | None: return None else: return self.right.search(label) + elif self.left is None: + return None else: - if self.left is None: - return None - else: - return self.left.search(label) + return self.left.search(label) def floor(self, label: int) -> int | None: """Returns the largest element in this tree which is at most label. diff --git a/data_structures/binary_tree/treap.py b/data_structures/binary_tree/treap.py index a53ac566ed54..e7ddf931b83a 100644 --- a/data_structures/binary_tree/treap.py +++ b/data_structures/binary_tree/treap.py @@ -43,22 +43,21 @@ def split(root: Node | None, value: int) -> tuple[Node | None, Node | None]: return None, None elif root.value is None: return None, None + elif value < root.value: + """ + Right tree's root will be current node. + Now we split(with the same value) current node's left son + Left tree: left part of that split + Right tree's left son: right part of that split + """ + left, root.left = split(root.left, value) + return left, root else: - if value < root.value: - """ - Right tree's root will be current node. - Now we split(with the same value) current node's left son - Left tree: left part of that split - Right tree's left son: right part of that split - """ - left, root.left = split(root.left, value) - return left, root - else: - """ - Just symmetric to previous case - """ - root.right, right = split(root.right, value) - return root, right + """ + Just symmetric to previous case + """ + root.right, right = split(root.right, value) + return root, right def merge(left: Node | None, right: Node | None) -> Node | None: diff --git a/data_structures/heap/max_heap.py b/data_structures/heap/max_heap.py index fbc8eed09226..5a9f9cf88433 100644 --- a/data_structures/heap/max_heap.py +++ b/data_structures/heap/max_heap.py @@ -40,11 +40,10 @@ def __swap_down(self, i: int) -> None: while self.__size >= 2 * i: if 2 * i + 1 > self.__size: bigger_child = 2 * i + elif self.__heap[2 * i] > self.__heap[2 * i + 1]: + bigger_child = 2 * i else: - if self.__heap[2 * i] > self.__heap[2 * i + 1]: - bigger_child = 2 * i - else: - bigger_child = 2 * i + 1 + bigger_child = 2 * i + 1 temporary = self.__heap[i] if self.__heap[i] < self.__heap[bigger_child]: self.__heap[i] = self.__heap[bigger_child] diff --git a/data_structures/stacks/infix_to_prefix_conversion.py b/data_structures/stacks/infix_to_prefix_conversion.py index beff421c0cfa..878473b93c19 100644 --- a/data_structures/stacks/infix_to_prefix_conversion.py +++ b/data_structures/stacks/infix_to_prefix_conversion.py @@ -95,13 +95,12 @@ def infix_2_postfix(infix: str) -> str: while stack[-1] != "(": post_fix.append(stack.pop()) # Pop stack & add the content to Postfix stack.pop() - else: - if len(stack) == 0: - stack.append(x) # If stack is empty, push x to stack - else: # while priority of x is not > priority of element in the stack - while stack and stack[-1] != "(" and priority[x] <= priority[stack[-1]]: - post_fix.append(stack.pop()) # pop stack & add to Postfix - stack.append(x) # push x to stack + elif len(stack) == 0: + stack.append(x) # If stack is empty, push x to stack + else: # while priority of x is not > priority of element in the stack + while stack and stack[-1] != "(" and priority[x] <= priority[stack[-1]]: + post_fix.append(stack.pop()) # pop stack & add to Postfix + stack.append(x) # push x to stack print( x.center(8), diff --git a/data_structures/trie/radix_tree.py b/data_structures/trie/radix_tree.py index fadc50cb49a7..caf566a6ce30 100644 --- a/data_structures/trie/radix_tree.py +++ b/data_structures/trie/radix_tree.py @@ -153,31 +153,30 @@ def delete(self, word: str) -> bool: # We have word remaining so we check the next node elif remaining_word != "": return incoming_node.delete(remaining_word) + # If it is not a leaf, we don't have to delete + elif not incoming_node.is_leaf: + return False else: - # If it is not a leaf, we don't have to delete - if not incoming_node.is_leaf: - return False + # We delete the nodes if no edges go from it + if len(incoming_node.nodes) == 0: + del self.nodes[word[0]] + # We merge the current node with its only child + if len(self.nodes) == 1 and not self.is_leaf: + merging_node = next(iter(self.nodes.values())) + self.is_leaf = merging_node.is_leaf + self.prefix += merging_node.prefix + self.nodes = merging_node.nodes + # If there is more than 1 edge, we just mark it as non-leaf + elif len(incoming_node.nodes) > 1: + incoming_node.is_leaf = False + # If there is 1 edge, we merge it with its child else: - # We delete the nodes if no edges go from it - if len(incoming_node.nodes) == 0: - del self.nodes[word[0]] - # We merge the current node with its only child - if len(self.nodes) == 1 and not self.is_leaf: - merging_node = next(iter(self.nodes.values())) - self.is_leaf = merging_node.is_leaf - self.prefix += merging_node.prefix - self.nodes = merging_node.nodes - # If there is more than 1 edge, we just mark it as non-leaf - elif len(incoming_node.nodes) > 1: - incoming_node.is_leaf = False - # If there is 1 edge, we merge it with its child - else: - merging_node = next(iter(incoming_node.nodes.values())) - incoming_node.is_leaf = merging_node.is_leaf - incoming_node.prefix += merging_node.prefix - incoming_node.nodes = merging_node.nodes - - return True + merging_node = next(iter(incoming_node.nodes.values())) + incoming_node.is_leaf = merging_node.is_leaf + incoming_node.prefix += merging_node.prefix + incoming_node.nodes = merging_node.nodes + + return True def print_tree(self, height: int = 0) -> None: """Print the tree diff --git a/divide_and_conquer/convex_hull.py b/divide_and_conquer/convex_hull.py index a5d8b713bdbc..93f6daf1f88c 100644 --- a/divide_and_conquer/convex_hull.py +++ b/divide_and_conquer/convex_hull.py @@ -274,14 +274,13 @@ def convex_hull_bf(points: list[Point]) -> list[Point]: points_left_of_ij = True elif det_k < 0: points_right_of_ij = True - else: - # point[i], point[j], point[k] all lie on a straight line - # if point[k] is to the left of point[i] or it's to the - # right of point[j], then point[i], point[j] cannot be - # part of the convex hull of A - if points[k] < points[i] or points[k] > points[j]: - ij_part_of_convex_hull = False - break + # point[i], point[j], point[k] all lie on a straight line + # if point[k] is to the left of point[i] or it's to the + # right of point[j], then point[i], point[j] cannot be + # part of the convex hull of A + elif points[k] < points[i] or points[k] > points[j]: + ij_part_of_convex_hull = False + break if points_left_of_ij and points_right_of_ij: ij_part_of_convex_hull = False diff --git a/graphs/graph_list.py b/graphs/graph_list.py index e871f3b8a9d6..6563cbb76132 100644 --- a/graphs/graph_list.py +++ b/graphs/graph_list.py @@ -120,29 +120,29 @@ def add_edge( else: self.adj_list[source_vertex] = [destination_vertex] self.adj_list[destination_vertex] = [source_vertex] - else: # For directed graphs - # if both source vertex and destination vertex are present in adjacency - # list, add destination vertex to source vertex list of adjacent vertices. - if source_vertex in self.adj_list and destination_vertex in self.adj_list: - self.adj_list[source_vertex].append(destination_vertex) - # if only source vertex is present in adjacency list, add destination - # vertex to source vertex list of adjacent vertices and create a new vertex - # with destination vertex as key, which has no adjacent vertex - elif source_vertex in self.adj_list: - self.adj_list[source_vertex].append(destination_vertex) - self.adj_list[destination_vertex] = [] - # if only destination vertex is present in adjacency list, create a new - # vertex with source vertex as key and assign a list containing destination - # vertex as first adjacent vertex - elif destination_vertex in self.adj_list: - self.adj_list[source_vertex] = [destination_vertex] - # if both source vertex and destination vertex are not present in adjacency - # list, create a new vertex with source vertex as key and a list containing - # destination vertex as it's first adjacent vertex. Then create a new vertex - # with destination vertex as key, which has no adjacent vertex - else: - self.adj_list[source_vertex] = [destination_vertex] - self.adj_list[destination_vertex] = [] + # For directed graphs + # if both source vertex and destination vertex are present in adjacency + # list, add destination vertex to source vertex list of adjacent vertices. + elif source_vertex in self.adj_list and destination_vertex in self.adj_list: + self.adj_list[source_vertex].append(destination_vertex) + # if only source vertex is present in adjacency list, add destination + # vertex to source vertex list of adjacent vertices and create a new vertex + # with destination vertex as key, which has no adjacent vertex + elif source_vertex in self.adj_list: + self.adj_list[source_vertex].append(destination_vertex) + self.adj_list[destination_vertex] = [] + # if only destination vertex is present in adjacency list, create a new + # vertex with source vertex as key and assign a list containing destination + # vertex as first adjacent vertex + elif destination_vertex in self.adj_list: + self.adj_list[source_vertex] = [destination_vertex] + # if both source vertex and destination vertex are not present in adjacency + # list, create a new vertex with source vertex as key and a list containing + # destination vertex as it's first adjacent vertex. Then create a new vertex + # with destination vertex as key, which has no adjacent vertex + else: + self.adj_list[source_vertex] = [destination_vertex] + self.adj_list[destination_vertex] = [] return self diff --git a/graphs/minimum_spanning_tree_prims.py b/graphs/minimum_spanning_tree_prims.py index 5a08ec57ff4d..90c9f4c91e86 100644 --- a/graphs/minimum_spanning_tree_prims.py +++ b/graphs/minimum_spanning_tree_prims.py @@ -18,11 +18,10 @@ def top_to_bottom(self, heap, start, size, positions): else: if 2 * start + 2 >= size: smallest_child = 2 * start + 1 + elif heap[2 * start + 1] < heap[2 * start + 2]: + smallest_child = 2 * start + 1 else: - if heap[2 * start + 1] < heap[2 * start + 2]: - smallest_child = 2 * start + 1 - else: - smallest_child = 2 * start + 2 + smallest_child = 2 * start + 2 if heap[smallest_child] < heap[start]: temp, temp1 = heap[smallest_child], positions[smallest_child] heap[smallest_child], positions[smallest_child] = ( diff --git a/graphs/multi_heuristic_astar.py b/graphs/multi_heuristic_astar.py index 0a18ede6ed41..6af9a187a4e9 100644 --- a/graphs/multi_heuristic_astar.py +++ b/graphs/multi_heuristic_astar.py @@ -270,24 +270,23 @@ def multi_a_star(start: TPos, goal: TPos, n_heuristic: int): back_pointer, ) close_list_inad.append(get_s) + elif g_function[goal] <= open_list[0].minkey(): + if g_function[goal] < float("inf"): + do_something(back_pointer, goal, start) else: - if g_function[goal] <= open_list[0].minkey(): - if g_function[goal] < float("inf"): - do_something(back_pointer, goal, start) - else: - get_s = open_list[0].top_show() - visited.add(get_s) - expand_state( - get_s, - 0, - visited, - g_function, - close_list_anchor, - close_list_inad, - open_list, - back_pointer, - ) - close_list_anchor.append(get_s) + get_s = open_list[0].top_show() + visited.add(get_s) + expand_state( + get_s, + 0, + visited, + g_function, + close_list_anchor, + close_list_inad, + open_list, + back_pointer, + ) + close_list_anchor.append(get_s) print("No path found to goal") print() for i in range(n - 1, -1, -1): diff --git a/machine_learning/forecasting/run.py b/machine_learning/forecasting/run.py index 64e719daacc2..dbb86caf8568 100644 --- a/machine_learning/forecasting/run.py +++ b/machine_learning/forecasting/run.py @@ -113,11 +113,10 @@ def data_safety_checker(list_vote: list, actual_result: float) -> bool: for i in list_vote: if i > actual_result: safe = not_safe + 1 + elif abs(abs(i) - abs(actual_result)) <= 0.1: + safe += 1 else: - if abs(abs(i) - abs(actual_result)) <= 0.1: - safe += 1 - else: - not_safe += 1 + not_safe += 1 return safe > not_safe diff --git a/maths/largest_of_very_large_numbers.py b/maths/largest_of_very_large_numbers.py index eb5c121fd262..edee50371e02 100644 --- a/maths/largest_of_very_large_numbers.py +++ b/maths/largest_of_very_large_numbers.py @@ -20,11 +20,10 @@ def res(x, y): if 0 not in (x, y): # We use the relation x^y = y*log10(x), where 10 is the base. return y * math.log10(x) - else: - if x == 0: # 0 raised to any number is 0 - return 0 - elif y == 0: - return 1 # any number raised to 0 is 1 + elif x == 0: # 0 raised to any number is 0 + return 0 + elif y == 0: + return 1 # any number raised to 0 is 1 raise AssertionError("This should never happen") diff --git a/maths/pollard_rho.py b/maths/pollard_rho.py index 5082f54f71a8..e8bc89cef6c5 100644 --- a/maths/pollard_rho.py +++ b/maths/pollard_rho.py @@ -94,14 +94,13 @@ def rand_fn(value: int, step: int, modulus: int) -> int: if divisor == 1: # No common divisor yet, just keep searching. continue + # We found a common divisor! + elif divisor == num: + # Unfortunately, the divisor is ``num`` itself and is useless. + break else: - # We found a common divisor! - if divisor == num: - # Unfortunately, the divisor is ``num`` itself and is useless. - break - else: - # The divisor is a nontrivial factor of ``num``! - return divisor + # The divisor is a nontrivial factor of ``num``! + return divisor # If we made it here, then this attempt failed. # We need to pick a new starting seed for the tortoise and hare diff --git a/matrix/cramers_rule_2x2.py b/matrix/cramers_rule_2x2.py index 4f52dbe646ad..081035bec002 100644 --- a/matrix/cramers_rule_2x2.py +++ b/matrix/cramers_rule_2x2.py @@ -73,12 +73,11 @@ def cramers_rule_2x2(equation1: list[int], equation2: list[int]) -> tuple[float, raise ValueError("Infinite solutions. (Consistent system)") else: raise ValueError("No solution. (Inconsistent system)") + elif determinant_x == determinant_y == 0: + # Trivial solution (Inconsistent system) + return (0.0, 0.0) else: - if determinant_x == determinant_y == 0: - # Trivial solution (Inconsistent system) - return (0.0, 0.0) - else: - x = determinant_x / determinant - y = determinant_y / determinant - # Non-Trivial Solution (Consistent system) - return (x, y) + x = determinant_x / determinant + y = determinant_y / determinant + # Non-Trivial Solution (Consistent system) + return (x, y) diff --git a/project_euler/problem_019/sol1.py b/project_euler/problem_019/sol1.py index 0e38137d4f01..656f104c390d 100644 --- a/project_euler/problem_019/sol1.py +++ b/project_euler/problem_019/sol1.py @@ -46,10 +46,9 @@ def solution(): elif day > 29 and month == 2: month += 1 day = day - 29 - else: - if day > days_per_month[month - 1]: - month += 1 - day = day - days_per_month[month - 2] + elif day > days_per_month[month - 1]: + month += 1 + day = day - days_per_month[month - 2] if month > 12: year += 1 diff --git a/pyproject.toml b/pyproject.toml index 5187491e5ee7..290a6b7599be 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,7 +12,6 @@ lint.ignore = [ # `ruff rule S101` for a description of that rule "NPY002", # Replace legacy `np.random.choice` call with `np.random.Generator` -- FIX ME "PGH003", # Use specific rule codes when ignoring type issues -- FIX ME "PLC1901", # `{}` can be simplified to `{}` as an empty string is falsey - "PLR5501", # Consider using `elif` instead of `else` -- FIX ME "PLW0120", # `else` clause on loop without a `break` statement -- FIX ME "PLW060", # Using global for `{name}` but no assignment is done -- DO NOT FIX "PLW2901", # PLW2901: Redefined loop variable -- FIX ME diff --git a/searches/hill_climbing.py b/searches/hill_climbing.py index 83a3b8b74e27..689b7e5cca8f 100644 --- a/searches/hill_climbing.py +++ b/searches/hill_climbing.py @@ -137,11 +137,10 @@ def hill_climbing( if change > max_change and change > 0: max_change = change next_state = neighbor - else: # finding min + elif change < min_change and change < 0: # finding min # to direction with greatest descent - if change < min_change and change < 0: - min_change = change - next_state = neighbor + min_change = change + next_state = neighbor if next_state is not None: # we found at least one neighbor which improved the current state current_state = next_state diff --git a/searches/interpolation_search.py b/searches/interpolation_search.py index 49194c2600a0..0591788aa40b 100644 --- a/searches/interpolation_search.py +++ b/searches/interpolation_search.py @@ -33,18 +33,16 @@ def interpolation_search(sorted_collection, item): current_item = sorted_collection[point] if current_item == item: return point + elif point < left: + right = left + left = point + elif point > right: + left = right + right = point + elif item < current_item: + right = point - 1 else: - if point < left: - right = left - left = point - elif point > right: - left = right - right = point - else: - if item < current_item: - right = point - 1 - else: - left = point + 1 + left = point + 1 return None @@ -79,15 +77,14 @@ def interpolation_search_by_recursion(sorted_collection, item, left, right): return interpolation_search_by_recursion(sorted_collection, item, point, left) elif point > right: return interpolation_search_by_recursion(sorted_collection, item, right, left) + elif sorted_collection[point] > item: + return interpolation_search_by_recursion( + sorted_collection, item, left, point - 1 + ) else: - if sorted_collection[point] > item: - return interpolation_search_by_recursion( - sorted_collection, item, left, point - 1 - ) - else: - return interpolation_search_by_recursion( - sorted_collection, item, point + 1, right - ) + return interpolation_search_by_recursion( + sorted_collection, item, point + 1, right + ) def __assert_sorted(collection): diff --git a/strings/min_cost_string_conversion.py b/strings/min_cost_string_conversion.py index 0fad0b88c370..d147a9d7954c 100644 --- a/strings/min_cost_string_conversion.py +++ b/strings/min_cost_string_conversion.py @@ -60,19 +60,18 @@ def compute_transform_tables( def assemble_transformation(ops: list[list[str]], i: int, j: int) -> list[str]: if i == 0 and j == 0: return [] + elif ops[i][j][0] in {"C", "R"}: + seq = assemble_transformation(ops, i - 1, j - 1) + seq.append(ops[i][j]) + return seq + elif ops[i][j][0] == "D": + seq = assemble_transformation(ops, i - 1, j) + seq.append(ops[i][j]) + return seq else: - if ops[i][j][0] in {"C", "R"}: - seq = assemble_transformation(ops, i - 1, j - 1) - seq.append(ops[i][j]) - return seq - elif ops[i][j][0] == "D": - seq = assemble_transformation(ops, i - 1, j) - seq.append(ops[i][j]) - return seq - else: - seq = assemble_transformation(ops, i, j - 1) - seq.append(ops[i][j]) - return seq + seq = assemble_transformation(ops, i, j - 1) + seq.append(ops[i][j]) + return seq if __name__ == "__main__": From da47d5c88ccf18e27c5b8f10830376031ad1792a Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Thu, 28 Mar 2024 20:26:41 +0300 Subject: [PATCH 587/757] Enable ruff N999 rule (#11331) * Enable ruff N999 rule * updating DIRECTORY.md --------- Co-authored-by: MaximSmolskiy --- DIRECTORY.md | 6 +++--- ...(nlogn).py => longest_increasing_subsequence_o_nlogn.py} | 0 ...)_graph.py => directed_and_undirected_weighted_graph.py} | 0 ...eural_network.py => two_hidden_layers_neural_network.py} | 0 pyproject.toml | 1 - 5 files changed, 3 insertions(+), 4 deletions(-) rename dynamic_programming/{longest_increasing_subsequence_o(nlogn).py => longest_increasing_subsequence_o_nlogn.py} (100%) rename graphs/{directed_and_undirected_(weighted)_graph.py => directed_and_undirected_weighted_graph.py} (100%) rename neural_network/{2_hidden_layers_neural_network.py => two_hidden_layers_neural_network.py} (100%) diff --git a/DIRECTORY.md b/DIRECTORY.md index 01667c9feee8..f6d6cb463faa 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -351,7 +351,7 @@ * [Longest Common Subsequence](dynamic_programming/longest_common_subsequence.py) * [Longest Common Substring](dynamic_programming/longest_common_substring.py) * [Longest Increasing Subsequence](dynamic_programming/longest_increasing_subsequence.py) - * [Longest Increasing Subsequence O(Nlogn)](dynamic_programming/longest_increasing_subsequence_o(nlogn).py) + * [Longest Increasing Subsequence O Nlogn](dynamic_programming/longest_increasing_subsequence_o_nlogn.py) * [Longest Palindromic Subsequence](dynamic_programming/longest_palindromic_subsequence.py) * [Matrix Chain Multiplication](dynamic_programming/matrix_chain_multiplication.py) * [Matrix Chain Order](dynamic_programming/matrix_chain_order.py) @@ -465,7 +465,7 @@ * [Dijkstra Alternate](graphs/dijkstra_alternate.py) * [Dijkstra Binary Grid](graphs/dijkstra_binary_grid.py) * [Dinic](graphs/dinic.py) - * [Directed And Undirected (Weighted) Graph](graphs/directed_and_undirected_(weighted)_graph.py) + * [Directed And Undirected Weighted Graph](graphs/directed_and_undirected_weighted_graph.py) * [Edmonds Karp Multiple Source And Sink](graphs/edmonds_karp_multiple_source_and_sink.py) * [Eulerian Path And Circuit For Undirected Graph](graphs/eulerian_path_and_circuit_for_undirected_graph.py) * [Even Tree](graphs/even_tree.py) @@ -792,7 +792,6 @@ * [Minimum Cut](networking_flow/minimum_cut.py) ## Neural Network - * [2 Hidden Layers Neural Network](neural_network/2_hidden_layers_neural_network.py) * Activation Functions * [Binary Step](neural_network/activation_functions/binary_step.py) * [Exponential Linear Unit](neural_network/activation_functions/exponential_linear_unit.py) @@ -809,6 +808,7 @@ * [Convolution Neural Network](neural_network/convolution_neural_network.py) * [Input Data](neural_network/input_data.py) * [Simple Neural Network](neural_network/simple_neural_network.py) + * [Two Hidden Layers Neural Network](neural_network/two_hidden_layers_neural_network.py) ## Other * [Activity Selection](other/activity_selection.py) diff --git a/dynamic_programming/longest_increasing_subsequence_o(nlogn).py b/dynamic_programming/longest_increasing_subsequence_o_nlogn.py similarity index 100% rename from dynamic_programming/longest_increasing_subsequence_o(nlogn).py rename to dynamic_programming/longest_increasing_subsequence_o_nlogn.py diff --git a/graphs/directed_and_undirected_(weighted)_graph.py b/graphs/directed_and_undirected_weighted_graph.py similarity index 100% rename from graphs/directed_and_undirected_(weighted)_graph.py rename to graphs/directed_and_undirected_weighted_graph.py diff --git a/neural_network/2_hidden_layers_neural_network.py b/neural_network/two_hidden_layers_neural_network.py similarity index 100% rename from neural_network/2_hidden_layers_neural_network.py rename to neural_network/two_hidden_layers_neural_network.py diff --git a/pyproject.toml b/pyproject.toml index 290a6b7599be..5b2eb07b4555 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,6 @@ lint.ignore = [ # `ruff rule S101` for a description of that rule "G004", # Logging statement uses f-string "ICN001", # `matplotlib.pyplot` should be imported as `plt` -- FIX ME "INP001", # File `x/y/z.py` is part of an implicit namespace package. Add an `__init__.py`. -- FIX ME - "N999", # Invalid module name -- FIX ME "NPY002", # Replace legacy `np.random.choice` call with `np.random.Generator` -- FIX ME "PGH003", # Use specific rule codes when ignoring type issues -- FIX ME "PLC1901", # `{}` can be simplified to `{}` as an empty string is falsey From efb7463cde48305cfebb8a547273c93edbdaaee5 Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Thu, 28 Mar 2024 20:28:54 +0300 Subject: [PATCH 588/757] Enable ruff PLW0120 rule (#11330) Co-authored-by: Christian Clauss --- pyproject.toml | 1 - searches/fibonacci_search.py | 3 +-- searches/ternary_search.py | 3 +-- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 5b2eb07b4555..b9f3115df92a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,7 +11,6 @@ lint.ignore = [ # `ruff rule S101` for a description of that rule "NPY002", # Replace legacy `np.random.choice` call with `np.random.Generator` -- FIX ME "PGH003", # Use specific rule codes when ignoring type issues -- FIX ME "PLC1901", # `{}` can be simplified to `{}` as an empty string is falsey - "PLW0120", # `else` clause on loop without a `break` statement -- FIX ME "PLW060", # Using global for `{name}` but no assignment is done -- DO NOT FIX "PLW2901", # PLW2901: Redefined loop variable -- FIX ME "PT011", # `pytest.raises(Exception)` is too broad, set the `match` parameter or use a more specific exception diff --git a/searches/fibonacci_search.py b/searches/fibonacci_search.py index ec3dfa7f30f6..7b2252a68be2 100644 --- a/searches/fibonacci_search.py +++ b/searches/fibonacci_search.py @@ -123,8 +123,7 @@ def fibonacci_search(arr: list, val: int) -> int: elif val > item_k_1: offset += fibonacci(fibb_k - 1) fibb_k -= 2 - else: - return -1 + return -1 if __name__ == "__main__": diff --git a/searches/ternary_search.py b/searches/ternary_search.py index 8dcd6b5bde2e..73e4b1ddc68b 100644 --- a/searches/ternary_search.py +++ b/searches/ternary_search.py @@ -106,8 +106,7 @@ def ite_ternary_search(array: list[int], target: int) -> int: else: left = one_third + 1 right = two_third - 1 - else: - return -1 + return -1 def rec_ternary_search(left: int, right: int, array: list[int], target: int) -> int: From f2246ce7fd539d94fd9299bd2fe42469dafab03f Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Thu, 28 Mar 2024 21:03:23 +0300 Subject: [PATCH 589/757] Enable ruff ICN001 rule (#11329) * Enable ruff ICN001 rule * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- ciphers/hill_cipher.py | 38 ++++----- fractals/julia_sets.py | 54 ++++++------ fractals/koch_snowflake.py | 34 ++++---- graphics/bezier_curve.py | 2 +- machine_learning/gradient_descent.py | 4 +- neural_network/input_data.py | 32 +++---- .../two_hidden_layers_neural_network.py | 84 +++++++++---------- pyproject.toml | 1 - 8 files changed, 121 insertions(+), 128 deletions(-) diff --git a/ciphers/hill_cipher.py b/ciphers/hill_cipher.py index ea337a72dc04..33b2529f017b 100644 --- a/ciphers/hill_cipher.py +++ b/ciphers/hill_cipher.py @@ -38,7 +38,7 @@ import string -import numpy +import numpy as np from maths.greatest_common_divisor import greatest_common_divisor @@ -49,11 +49,11 @@ class HillCipher: # i.e. a total of 36 characters # take x and return x % len(key_string) - modulus = numpy.vectorize(lambda x: x % 36) + modulus = np.vectorize(lambda x: x % 36) - to_int = numpy.vectorize(round) + to_int = np.vectorize(round) - def __init__(self, encrypt_key: numpy.ndarray) -> None: + def __init__(self, encrypt_key: np.ndarray) -> None: """ encrypt_key is an NxN numpy array """ @@ -63,7 +63,7 @@ def __init__(self, encrypt_key: numpy.ndarray) -> None: def replace_letters(self, letter: str) -> int: """ - >>> hill_cipher = HillCipher(numpy.array([[2, 5], [1, 6]])) + >>> hill_cipher = HillCipher(np.array([[2, 5], [1, 6]])) >>> hill_cipher.replace_letters('T') 19 >>> hill_cipher.replace_letters('0') @@ -73,7 +73,7 @@ def replace_letters(self, letter: str) -> int: def replace_digits(self, num: int) -> str: """ - >>> hill_cipher = HillCipher(numpy.array([[2, 5], [1, 6]])) + >>> hill_cipher = HillCipher(np.array([[2, 5], [1, 6]])) >>> hill_cipher.replace_digits(19) 'T' >>> hill_cipher.replace_digits(26) @@ -83,10 +83,10 @@ def replace_digits(self, num: int) -> str: def check_determinant(self) -> None: """ - >>> hill_cipher = HillCipher(numpy.array([[2, 5], [1, 6]])) + >>> hill_cipher = HillCipher(np.array([[2, 5], [1, 6]])) >>> hill_cipher.check_determinant() """ - det = round(numpy.linalg.det(self.encrypt_key)) + det = round(np.linalg.det(self.encrypt_key)) if det < 0: det = det % len(self.key_string) @@ -101,7 +101,7 @@ def check_determinant(self) -> None: def process_text(self, text: str) -> str: """ - >>> hill_cipher = HillCipher(numpy.array([[2, 5], [1, 6]])) + >>> hill_cipher = HillCipher(np.array([[2, 5], [1, 6]])) >>> hill_cipher.process_text('Testing Hill Cipher') 'TESTINGHILLCIPHERR' >>> hill_cipher.process_text('hello') @@ -117,7 +117,7 @@ def process_text(self, text: str) -> str: def encrypt(self, text: str) -> str: """ - >>> hill_cipher = HillCipher(numpy.array([[2, 5], [1, 6]])) + >>> hill_cipher = HillCipher(np.array([[2, 5], [1, 6]])) >>> hill_cipher.encrypt('testing hill cipher') 'WHXYJOLM9C6XT085LL' >>> hill_cipher.encrypt('hello') @@ -129,7 +129,7 @@ def encrypt(self, text: str) -> str: for i in range(0, len(text) - self.break_key + 1, self.break_key): batch = text[i : i + self.break_key] vec = [self.replace_letters(char) for char in batch] - batch_vec = numpy.array([vec]).T + batch_vec = np.array([vec]).T batch_encrypted = self.modulus(self.encrypt_key.dot(batch_vec)).T.tolist()[ 0 ] @@ -140,14 +140,14 @@ def encrypt(self, text: str) -> str: return encrypted - def make_decrypt_key(self) -> numpy.ndarray: + def make_decrypt_key(self) -> np.ndarray: """ - >>> hill_cipher = HillCipher(numpy.array([[2, 5], [1, 6]])) + >>> hill_cipher = HillCipher(np.array([[2, 5], [1, 6]])) >>> hill_cipher.make_decrypt_key() array([[ 6, 25], [ 5, 26]]) """ - det = round(numpy.linalg.det(self.encrypt_key)) + det = round(np.linalg.det(self.encrypt_key)) if det < 0: det = det % len(self.key_string) @@ -158,16 +158,14 @@ def make_decrypt_key(self) -> numpy.ndarray: break inv_key = ( - det_inv - * numpy.linalg.det(self.encrypt_key) - * numpy.linalg.inv(self.encrypt_key) + det_inv * np.linalg.det(self.encrypt_key) * np.linalg.inv(self.encrypt_key) ) return self.to_int(self.modulus(inv_key)) def decrypt(self, text: str) -> str: """ - >>> hill_cipher = HillCipher(numpy.array([[2, 5], [1, 6]])) + >>> hill_cipher = HillCipher(np.array([[2, 5], [1, 6]])) >>> hill_cipher.decrypt('WHXYJOLM9C6XT085LL') 'TESTINGHILLCIPHERR' >>> hill_cipher.decrypt('85FF00') @@ -180,7 +178,7 @@ def decrypt(self, text: str) -> str: for i in range(0, len(text) - self.break_key + 1, self.break_key): batch = text[i : i + self.break_key] vec = [self.replace_letters(char) for char in batch] - batch_vec = numpy.array([vec]).T + batch_vec = np.array([vec]).T batch_decrypted = self.modulus(decrypt_key.dot(batch_vec)).T.tolist()[0] decrypted_batch = "".join( self.replace_digits(num) for num in batch_decrypted @@ -199,7 +197,7 @@ def main() -> None: row = [int(x) for x in input().split()] hill_matrix.append(row) - hc = HillCipher(numpy.array(hill_matrix)) + hc = HillCipher(np.array(hill_matrix)) print("Would you like to encrypt or decrypt some text? (1 or 2)") option = input("\n1. Encrypt\n2. Decrypt\n") diff --git a/fractals/julia_sets.py b/fractals/julia_sets.py index 482e1eddfecc..1eef4573ba19 100644 --- a/fractals/julia_sets.py +++ b/fractals/julia_sets.py @@ -25,8 +25,8 @@ from collections.abc import Callable from typing import Any -import numpy -from matplotlib import pyplot +import matplotlib.pyplot as plt +import numpy as np c_cauliflower = 0.25 + 0.0j c_polynomial_1 = -0.4 + 0.6j @@ -37,22 +37,20 @@ nb_pixels = 666 -def eval_exponential(c_parameter: complex, z_values: numpy.ndarray) -> numpy.ndarray: +def eval_exponential(c_parameter: complex, z_values: np.ndarray) -> np.ndarray: """ Evaluate $e^z + c$. >>> eval_exponential(0, 0) 1.0 - >>> abs(eval_exponential(1, numpy.pi*1.j)) < 1e-15 + >>> abs(eval_exponential(1, np.pi*1.j)) < 1e-15 True >>> abs(eval_exponential(1.j, 0)-1-1.j) < 1e-15 True """ - return numpy.exp(z_values) + c_parameter + return np.exp(z_values) + c_parameter -def eval_quadratic_polynomial( - c_parameter: complex, z_values: numpy.ndarray -) -> numpy.ndarray: +def eval_quadratic_polynomial(c_parameter: complex, z_values: np.ndarray) -> np.ndarray: """ >>> eval_quadratic_polynomial(0, 2) 4 @@ -66,7 +64,7 @@ def eval_quadratic_polynomial( return z_values * z_values + c_parameter -def prepare_grid(window_size: float, nb_pixels: int) -> numpy.ndarray: +def prepare_grid(window_size: float, nb_pixels: int) -> np.ndarray: """ Create a grid of complex values of size nb_pixels*nb_pixels with real and imaginary parts ranging from -window_size to window_size (inclusive). @@ -77,20 +75,20 @@ def prepare_grid(window_size: float, nb_pixels: int) -> numpy.ndarray: [ 0.-1.j, 0.+0.j, 0.+1.j], [ 1.-1.j, 1.+0.j, 1.+1.j]]) """ - x = numpy.linspace(-window_size, window_size, nb_pixels) + x = np.linspace(-window_size, window_size, nb_pixels) x = x.reshape((nb_pixels, 1)) - y = numpy.linspace(-window_size, window_size, nb_pixels) + y = np.linspace(-window_size, window_size, nb_pixels) y = y.reshape((1, nb_pixels)) return x + 1.0j * y def iterate_function( - eval_function: Callable[[Any, numpy.ndarray], numpy.ndarray], + eval_function: Callable[[Any, np.ndarray], np.ndarray], function_params: Any, nb_iterations: int, - z_0: numpy.ndarray, + z_0: np.ndarray, infinity: float | None = None, -) -> numpy.ndarray: +) -> np.ndarray: """ Iterate the function "eval_function" exactly nb_iterations times. The first argument of the function is a parameter which is contained in @@ -98,22 +96,22 @@ def iterate_function( values to iterate from. This function returns the final iterates. - >>> iterate_function(eval_quadratic_polynomial, 0, 3, numpy.array([0,1,2])).shape + >>> iterate_function(eval_quadratic_polynomial, 0, 3, np.array([0,1,2])).shape (3,) - >>> numpy.round(iterate_function(eval_quadratic_polynomial, + >>> np.round(iterate_function(eval_quadratic_polynomial, ... 0, ... 3, - ... numpy.array([0,1,2]))[0]) + ... np.array([0,1,2]))[0]) 0j - >>> numpy.round(iterate_function(eval_quadratic_polynomial, + >>> np.round(iterate_function(eval_quadratic_polynomial, ... 0, ... 3, - ... numpy.array([0,1,2]))[1]) + ... np.array([0,1,2]))[1]) (1+0j) - >>> numpy.round(iterate_function(eval_quadratic_polynomial, + >>> np.round(iterate_function(eval_quadratic_polynomial, ... 0, ... 3, - ... numpy.array([0,1,2]))[2]) + ... np.array([0,1,2]))[2]) (256+0j) """ @@ -121,8 +119,8 @@ def iterate_function( for _ in range(nb_iterations): z_n = eval_function(function_params, z_n) if infinity is not None: - numpy.nan_to_num(z_n, copy=False, nan=infinity) - z_n[abs(z_n) == numpy.inf] = infinity + np.nan_to_num(z_n, copy=False, nan=infinity) + z_n[abs(z_n) == np.inf] = infinity return z_n @@ -130,21 +128,21 @@ def show_results( function_label: str, function_params: Any, escape_radius: float, - z_final: numpy.ndarray, + z_final: np.ndarray, ) -> None: """ Plots of whether the absolute value of z_final is greater than the value of escape_radius. Adds the function_label and function_params to the title. - >>> show_results('80', 0, 1, numpy.array([[0,1,.5],[.4,2,1.1],[.2,1,1.3]])) + >>> show_results('80', 0, 1, np.array([[0,1,.5],[.4,2,1.1],[.2,1,1.3]])) """ abs_z_final = (abs(z_final)).transpose() abs_z_final[:, :] = abs_z_final[::-1, :] - pyplot.matshow(abs_z_final < escape_radius) - pyplot.title(f"Julia set of ${function_label}$, $c={function_params}$") - pyplot.show() + plt.matshow(abs_z_final < escape_radius) + plt.title(f"Julia set of ${function_label}$, $c={function_params}$") + plt.show() def ignore_overflow_warnings() -> None: diff --git a/fractals/koch_snowflake.py b/fractals/koch_snowflake.py index 30cd4b39c7c1..724b78f41a69 100644 --- a/fractals/koch_snowflake.py +++ b/fractals/koch_snowflake.py @@ -22,25 +22,25 @@ from __future__ import annotations -import matplotlib.pyplot as plt # type: ignore -import numpy +import matplotlib.pyplot as plt +import numpy as np # initial triangle of Koch snowflake -VECTOR_1 = numpy.array([0, 0]) -VECTOR_2 = numpy.array([0.5, 0.8660254]) -VECTOR_3 = numpy.array([1, 0]) +VECTOR_1 = np.array([0, 0]) +VECTOR_2 = np.array([0.5, 0.8660254]) +VECTOR_3 = np.array([1, 0]) INITIAL_VECTORS = [VECTOR_1, VECTOR_2, VECTOR_3, VECTOR_1] # uncomment for simple Koch curve instead of Koch snowflake # INITIAL_VECTORS = [VECTOR_1, VECTOR_3] -def iterate(initial_vectors: list[numpy.ndarray], steps: int) -> list[numpy.ndarray]: +def iterate(initial_vectors: list[np.ndarray], steps: int) -> list[np.ndarray]: """ Go through the number of iterations determined by the argument "steps". Be careful with high values (above 5) since the time to calculate increases exponentially. - >>> iterate([numpy.array([0, 0]), numpy.array([1, 0])], 1) + >>> iterate([np.array([0, 0]), np.array([1, 0])], 1) [array([0, 0]), array([0.33333333, 0. ]), array([0.5 , \ 0.28867513]), array([0.66666667, 0. ]), array([1, 0])] """ @@ -50,13 +50,13 @@ def iterate(initial_vectors: list[numpy.ndarray], steps: int) -> list[numpy.ndar return vectors -def iteration_step(vectors: list[numpy.ndarray]) -> list[numpy.ndarray]: +def iteration_step(vectors: list[np.ndarray]) -> list[np.ndarray]: """ Loops through each pair of adjacent vectors. Each line between two adjacent vectors is divided into 4 segments by adding 3 additional vectors in-between the original two vectors. The vector in the middle is constructed through a 60 degree rotation so it is bent outwards. - >>> iteration_step([numpy.array([0, 0]), numpy.array([1, 0])]) + >>> iteration_step([np.array([0, 0]), np.array([1, 0])]) [array([0, 0]), array([0.33333333, 0. ]), array([0.5 , \ 0.28867513]), array([0.66666667, 0. ]), array([1, 0])] """ @@ -74,22 +74,22 @@ def iteration_step(vectors: list[numpy.ndarray]) -> list[numpy.ndarray]: return new_vectors -def rotate(vector: numpy.ndarray, angle_in_degrees: float) -> numpy.ndarray: +def rotate(vector: np.ndarray, angle_in_degrees: float) -> np.ndarray: """ Standard rotation of a 2D vector with a rotation matrix (see https://en.wikipedia.org/wiki/Rotation_matrix ) - >>> rotate(numpy.array([1, 0]), 60) + >>> rotate(np.array([1, 0]), 60) array([0.5 , 0.8660254]) - >>> rotate(numpy.array([1, 0]), 90) + >>> rotate(np.array([1, 0]), 90) array([6.123234e-17, 1.000000e+00]) """ - theta = numpy.radians(angle_in_degrees) - c, s = numpy.cos(theta), numpy.sin(theta) - rotation_matrix = numpy.array(((c, -s), (s, c))) - return numpy.dot(rotation_matrix, vector) + theta = np.radians(angle_in_degrees) + c, s = np.cos(theta), np.sin(theta) + rotation_matrix = np.array(((c, -s), (s, c))) + return np.dot(rotation_matrix, vector) -def plot(vectors: list[numpy.ndarray]) -> None: +def plot(vectors: list[np.ndarray]) -> None: """ Utility function to plot the vectors using matplotlib.pyplot No doctest was implemented since this function does not have a return value diff --git a/graphics/bezier_curve.py b/graphics/bezier_curve.py index 7c22329ad8b4..6eeb89da6bdf 100644 --- a/graphics/bezier_curve.py +++ b/graphics/bezier_curve.py @@ -78,7 +78,7 @@ def plot_curve(self, step_size: float = 0.01): step_size: defines the step(s) at which to evaluate the Bezier curve. The smaller the step size, the finer the curve produced. """ - from matplotlib import pyplot as plt # type: ignore + 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 diff --git a/machine_learning/gradient_descent.py b/machine_learning/gradient_descent.py index db38b3c95b52..95463faf5635 100644 --- a/machine_learning/gradient_descent.py +++ b/machine_learning/gradient_descent.py @@ -3,7 +3,7 @@ function. """ -import numpy +import numpy as np # List of input, output pairs train_data = ( @@ -116,7 +116,7 @@ def run_gradient_descent(): temp_parameter_vector[i] = ( parameter_vector[i] - LEARNING_RATE * cost_derivative ) - if numpy.allclose( + if np.allclose( parameter_vector, temp_parameter_vector, atol=absolute_error_limit, diff --git a/neural_network/input_data.py b/neural_network/input_data.py index f7ae86b48e65..9d4195487dbb 100644 --- a/neural_network/input_data.py +++ b/neural_network/input_data.py @@ -22,7 +22,7 @@ import typing import urllib -import numpy +import numpy as np from tensorflow.python.framework import dtypes, random_seed from tensorflow.python.platform import gfile from tensorflow.python.util.deprecation import deprecated @@ -39,8 +39,8 @@ class _Datasets(typing.NamedTuple): def _read32(bytestream): - dt = numpy.dtype(numpy.uint32).newbyteorder(">") - return numpy.frombuffer(bytestream.read(4), dtype=dt)[0] + dt = np.dtype(np.uint32).newbyteorder(">") + return np.frombuffer(bytestream.read(4), dtype=dt)[0] @deprecated(None, "Please use tf.data to implement this functionality.") @@ -68,7 +68,7 @@ def _extract_images(f): rows = _read32(bytestream) cols = _read32(bytestream) buf = bytestream.read(rows * cols * num_images) - data = numpy.frombuffer(buf, dtype=numpy.uint8) + data = np.frombuffer(buf, dtype=np.uint8) data = data.reshape(num_images, rows, cols, 1) return data @@ -77,8 +77,8 @@ def _extract_images(f): def _dense_to_one_hot(labels_dense, num_classes): """Convert class labels from scalars to one-hot vectors.""" num_labels = labels_dense.shape[0] - index_offset = numpy.arange(num_labels) * num_classes - labels_one_hot = numpy.zeros((num_labels, num_classes)) + index_offset = np.arange(num_labels) * num_classes + labels_one_hot = np.zeros((num_labels, num_classes)) labels_one_hot.flat[index_offset + labels_dense.ravel()] = 1 return labels_one_hot @@ -107,7 +107,7 @@ def _extract_labels(f, one_hot=False, num_classes=10): ) num_items = _read32(bytestream) buf = bytestream.read(num_items) - labels = numpy.frombuffer(buf, dtype=numpy.uint8) + labels = np.frombuffer(buf, dtype=np.uint8) if one_hot: return _dense_to_one_hot(labels, num_classes) return labels @@ -153,7 +153,7 @@ def __init__( """ seed1, seed2 = random_seed.get_seed(seed) # If op level seed is not set, use whatever graph level seed is returned - numpy.random.seed(seed1 if seed is None else seed2) + np.random.seed(seed1 if seed is None else seed2) dtype = dtypes.as_dtype(dtype).base_dtype if dtype not in (dtypes.uint8, dtypes.float32): raise TypeError("Invalid image dtype %r, expected uint8 or float32" % dtype) @@ -175,8 +175,8 @@ def __init__( ) if dtype == dtypes.float32: # Convert from [0, 255] -> [0.0, 1.0]. - images = images.astype(numpy.float32) - images = numpy.multiply(images, 1.0 / 255.0) + images = images.astype(np.float32) + images = np.multiply(images, 1.0 / 255.0) self._images = images self._labels = labels self._epochs_completed = 0 @@ -210,8 +210,8 @@ def next_batch(self, batch_size, fake_data=False, shuffle=True): start = self._index_in_epoch # Shuffle for the first epoch if self._epochs_completed == 0 and start == 0 and shuffle: - perm0 = numpy.arange(self._num_examples) - numpy.random.shuffle(perm0) + perm0 = np.arange(self._num_examples) + np.random.shuffle(perm0) self._images = self.images[perm0] self._labels = self.labels[perm0] # Go to the next epoch @@ -224,8 +224,8 @@ def next_batch(self, batch_size, fake_data=False, shuffle=True): labels_rest_part = self._labels[start : self._num_examples] # Shuffle the data if shuffle: - perm = numpy.arange(self._num_examples) - numpy.random.shuffle(perm) + perm = np.arange(self._num_examples) + np.random.shuffle(perm) self._images = self.images[perm] self._labels = self.labels[perm] # Start next epoch @@ -235,8 +235,8 @@ def next_batch(self, batch_size, fake_data=False, shuffle=True): images_new_part = self._images[start:end] labels_new_part = self._labels[start:end] return ( - numpy.concatenate((images_rest_part, images_new_part), axis=0), - numpy.concatenate((labels_rest_part, labels_new_part), axis=0), + np.concatenate((images_rest_part, images_new_part), axis=0), + np.concatenate((labels_rest_part, labels_new_part), axis=0), ) else: self._index_in_epoch += batch_size diff --git a/neural_network/two_hidden_layers_neural_network.py b/neural_network/two_hidden_layers_neural_network.py index 7b374a93d039..dea7e2342d9f 100644 --- a/neural_network/two_hidden_layers_neural_network.py +++ b/neural_network/two_hidden_layers_neural_network.py @@ -5,11 +5,11 @@ - https://en.wikipedia.org/wiki/Feedforward_neural_network (Feedforward) """ -import numpy +import numpy as np class TwoHiddenLayerNeuralNetwork: - def __init__(self, input_array: numpy.ndarray, output_array: numpy.ndarray) -> None: + def __init__(self, input_array: np.ndarray, output_array: np.ndarray) -> None: """ This function initializes the TwoHiddenLayerNeuralNetwork class with random weights for every layer and initializes predicted output with zeroes. @@ -28,30 +28,28 @@ def __init__(self, input_array: numpy.ndarray, output_array: numpy.ndarray) -> N # Random initial weights are assigned. # self.input_array.shape[1] is used to represent number of nodes in input layer. # First hidden layer consists of 4 nodes. - self.input_layer_and_first_hidden_layer_weights = numpy.random.rand( + self.input_layer_and_first_hidden_layer_weights = np.random.rand( self.input_array.shape[1], 4 ) # Random initial values for the first hidden layer. # First hidden layer has 4 nodes. # Second hidden layer has 3 nodes. - self.first_hidden_layer_and_second_hidden_layer_weights = numpy.random.rand( - 4, 3 - ) + self.first_hidden_layer_and_second_hidden_layer_weights = np.random.rand(4, 3) # Random initial values for the second hidden layer. # Second hidden layer has 3 nodes. # Output layer has 1 node. - self.second_hidden_layer_and_output_layer_weights = numpy.random.rand(3, 1) + self.second_hidden_layer_and_output_layer_weights = np.random.rand(3, 1) # Real output values provided. self.output_array = output_array # Predicted output values by the neural network. # Predicted_output array initially consists of zeroes. - self.predicted_output = numpy.zeros(output_array.shape) + self.predicted_output = np.zeros(output_array.shape) - def feedforward(self) -> numpy.ndarray: + def feedforward(self) -> np.ndarray: """ The information moves in only one direction i.e. forward from the input nodes, through the two hidden nodes and to the output nodes. @@ -60,24 +58,24 @@ def feedforward(self) -> numpy.ndarray: Return layer_between_second_hidden_layer_and_output (i.e the last layer of the neural network). - >>> input_val = numpy.array(([0, 0, 0], [0, 0, 0], [0, 0, 0]), dtype=float) - >>> output_val = numpy.array(([0], [0], [0]), dtype=float) + >>> input_val = np.array(([0, 0, 0], [0, 0, 0], [0, 0, 0]), dtype=float) + >>> output_val = np.array(([0], [0], [0]), dtype=float) >>> nn = TwoHiddenLayerNeuralNetwork(input_val, output_val) >>> res = nn.feedforward() - >>> array_sum = numpy.sum(res) - >>> numpy.isnan(array_sum) + >>> array_sum = np.sum(res) + >>> np.isnan(array_sum) False """ # Layer_between_input_and_first_hidden_layer is the layer connecting the # input nodes with the first hidden layer nodes. self.layer_between_input_and_first_hidden_layer = sigmoid( - numpy.dot(self.input_array, self.input_layer_and_first_hidden_layer_weights) + np.dot(self.input_array, self.input_layer_and_first_hidden_layer_weights) ) # layer_between_first_hidden_layer_and_second_hidden_layer is the layer # connecting the first hidden set of nodes with the second hidden set of nodes. self.layer_between_first_hidden_layer_and_second_hidden_layer = sigmoid( - numpy.dot( + np.dot( self.layer_between_input_and_first_hidden_layer, self.first_hidden_layer_and_second_hidden_layer_weights, ) @@ -86,7 +84,7 @@ def feedforward(self) -> numpy.ndarray: # layer_between_second_hidden_layer_and_output is the layer connecting # second hidden layer with the output node. self.layer_between_second_hidden_layer_and_output = sigmoid( - numpy.dot( + np.dot( self.layer_between_first_hidden_layer_and_second_hidden_layer, self.second_hidden_layer_and_output_layer_weights, ) @@ -100,8 +98,8 @@ def back_propagation(self) -> None: error rate obtained in the previous epoch (i.e., iteration). Updation is done using derivative of sogmoid activation function. - >>> input_val = numpy.array(([0, 0, 0], [0, 0, 0], [0, 0, 0]), dtype=float) - >>> output_val = numpy.array(([0], [0], [0]), dtype=float) + >>> input_val = np.array(([0, 0, 0], [0, 0, 0], [0, 0, 0]), dtype=float) + >>> output_val = np.array(([0], [0], [0]), dtype=float) >>> nn = TwoHiddenLayerNeuralNetwork(input_val, output_val) >>> res = nn.feedforward() >>> nn.back_propagation() @@ -110,15 +108,15 @@ def back_propagation(self) -> None: False """ - updated_second_hidden_layer_and_output_layer_weights = numpy.dot( + updated_second_hidden_layer_and_output_layer_weights = np.dot( self.layer_between_first_hidden_layer_and_second_hidden_layer.T, 2 * (self.output_array - self.predicted_output) * sigmoid_derivative(self.predicted_output), ) - updated_first_hidden_layer_and_second_hidden_layer_weights = numpy.dot( + updated_first_hidden_layer_and_second_hidden_layer_weights = np.dot( self.layer_between_input_and_first_hidden_layer.T, - numpy.dot( + np.dot( 2 * (self.output_array - self.predicted_output) * sigmoid_derivative(self.predicted_output), @@ -128,10 +126,10 @@ def back_propagation(self) -> None: self.layer_between_first_hidden_layer_and_second_hidden_layer ), ) - updated_input_layer_and_first_hidden_layer_weights = numpy.dot( + updated_input_layer_and_first_hidden_layer_weights = np.dot( self.input_array.T, - numpy.dot( - numpy.dot( + np.dot( + np.dot( 2 * (self.output_array - self.predicted_output) * sigmoid_derivative(self.predicted_output), @@ -155,7 +153,7 @@ def back_propagation(self) -> None: updated_second_hidden_layer_and_output_layer_weights ) - def train(self, output: numpy.ndarray, iterations: int, give_loss: bool) -> None: + def train(self, output: np.ndarray, iterations: int, give_loss: bool) -> None: """ Performs the feedforwarding and back propagation process for the given number of iterations. @@ -166,8 +164,8 @@ def train(self, output: numpy.ndarray, iterations: int, give_loss: bool) -> None give_loss : boolean value, If True then prints loss for each iteration, If False then nothing is printed - >>> input_val = numpy.array(([0, 0, 0], [0, 1, 0], [0, 0, 1]), dtype=float) - >>> output_val = numpy.array(([0], [1], [1]), dtype=float) + >>> input_val = np.array(([0, 0, 0], [0, 1, 0], [0, 0, 1]), dtype=float) + >>> output_val = np.array(([0], [1], [1]), dtype=float) >>> nn = TwoHiddenLayerNeuralNetwork(input_val, output_val) >>> first_iteration_weights = nn.feedforward() >>> nn.back_propagation() @@ -179,10 +177,10 @@ def train(self, output: numpy.ndarray, iterations: int, give_loss: bool) -> None self.output = self.feedforward() self.back_propagation() if give_loss: - loss = numpy.mean(numpy.square(output - self.feedforward())) + loss = np.mean(np.square(output - self.feedforward())) print(f"Iteration {iteration} Loss: {loss}") - def predict(self, input_arr: numpy.ndarray) -> int: + def predict(self, input_arr: np.ndarray) -> int: """ Predict's the output for the given input values using the trained neural network. @@ -192,8 +190,8 @@ def predict(self, input_arr: numpy.ndarray) -> int: than the threshold value else returns 0, as the real output values are in binary. - >>> input_val = numpy.array(([0, 0, 0], [0, 1, 0], [0, 0, 1]), dtype=float) - >>> output_val = numpy.array(([0], [1], [1]), dtype=float) + >>> input_val = np.array(([0, 0, 0], [0, 1, 0], [0, 0, 1]), dtype=float) + >>> output_val = np.array(([0], [1], [1]), dtype=float) >>> nn = TwoHiddenLayerNeuralNetwork(input_val, output_val) >>> nn.train(output_val, 1000, False) >>> nn.predict([0, 1, 0]) in (0, 1) @@ -204,18 +202,18 @@ def predict(self, input_arr: numpy.ndarray) -> int: self.array = input_arr self.layer_between_input_and_first_hidden_layer = sigmoid( - numpy.dot(self.array, self.input_layer_and_first_hidden_layer_weights) + np.dot(self.array, self.input_layer_and_first_hidden_layer_weights) ) self.layer_between_first_hidden_layer_and_second_hidden_layer = sigmoid( - numpy.dot( + np.dot( self.layer_between_input_and_first_hidden_layer, self.first_hidden_layer_and_second_hidden_layer_weights, ) ) self.layer_between_second_hidden_layer_and_output = sigmoid( - numpy.dot( + np.dot( self.layer_between_first_hidden_layer_and_second_hidden_layer, self.second_hidden_layer_and_output_layer_weights, ) @@ -224,26 +222,26 @@ def predict(self, input_arr: numpy.ndarray) -> int: return int((self.layer_between_second_hidden_layer_and_output > 0.6)[0]) -def sigmoid(value: numpy.ndarray) -> numpy.ndarray: +def sigmoid(value: np.ndarray) -> np.ndarray: """ Applies sigmoid activation function. return normalized values - >>> sigmoid(numpy.array(([1, 0, 2], [1, 0, 0]), dtype=numpy.float64)) + >>> sigmoid(np.array(([1, 0, 2], [1, 0, 0]), dtype=np.float64)) array([[0.73105858, 0.5 , 0.88079708], [0.73105858, 0.5 , 0.5 ]]) """ - return 1 / (1 + numpy.exp(-value)) + return 1 / (1 + np.exp(-value)) -def sigmoid_derivative(value: numpy.ndarray) -> numpy.ndarray: +def sigmoid_derivative(value: np.ndarray) -> np.ndarray: """ Provides the derivative value of the sigmoid function. returns derivative of the sigmoid value - >>> sigmoid_derivative(numpy.array(([1, 0, 2], [1, 0, 0]), dtype=numpy.float64)) + >>> sigmoid_derivative(np.array(([1, 0, 2], [1, 0, 0]), dtype=np.float64)) array([[ 0., 0., -2.], [ 0., 0., 0.]]) """ @@ -264,7 +262,7 @@ def example() -> int: True """ # Input values. - test_input = numpy.array( + test_input = np.array( ( [0, 0, 0], [0, 0, 1], @@ -275,11 +273,11 @@ def example() -> int: [1, 1, 0], [1, 1, 1], ), - dtype=numpy.float64, + dtype=np.float64, ) # True output values for the given input values. - output = numpy.array(([0], [1], [1], [0], [1], [0], [0], [1]), dtype=numpy.float64) + output = np.array(([0], [1], [1], [0], [1], [0], [0], [1]), dtype=np.float64) # Calling neural network class. neural_network = TwoHiddenLayerNeuralNetwork( @@ -290,7 +288,7 @@ def example() -> int: # Set give_loss to True if you want to see loss in every iteration. neural_network.train(output=output, iterations=10, give_loss=False) - return neural_network.predict(numpy.array(([1, 1, 1]), dtype=numpy.float64)) + return neural_network.predict(np.array(([1, 1, 1]), dtype=np.float64)) if __name__ == "__main__": diff --git a/pyproject.toml b/pyproject.toml index b9f3115df92a..22da7cb777b5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,6 @@ lint.ignore = [ # `ruff rule S101` for a description of that rule "EM101", # Exception must not use a string literal, assign to variable first "EXE001", # Shebang is present but file is not executable" -- FIX ME "G004", # Logging statement uses f-string - "ICN001", # `matplotlib.pyplot` should be imported as `plt` -- FIX ME "INP001", # File `x/y/z.py` is part of an implicit namespace package. Add an `__init__.py`. -- FIX ME "NPY002", # Replace legacy `np.random.choice` call with `np.random.Generator` -- FIX ME "PGH003", # Use specific rule codes when ignoring type issues -- FIX ME From c328b000ecdd4ad08d029999144e7ec702022390 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 1 Apr 2024 21:35:37 +0200 Subject: [PATCH 590/757] [pre-commit.ci] pre-commit autoupdate (#11339) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.3.4 → v0.3.5](https://github.com/astral-sh/ruff-pre-commit/compare/v0.3.4...v0.3.5) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8b101207d5ff..e6b1b0442c04 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.3.4 + rev: v0.3.5 hooks: - id: ruff - id: ruff-format From 39daaf8248b37404f69e8459d0378d77b59c6c0f Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Mon, 1 Apr 2024 22:36:41 +0300 Subject: [PATCH 591/757] Enable ruff RUF100 rule (#11337) --- audio_filters/butterworth_filter.py | 14 +++++++------- data_structures/binary_tree/basic_binary_tree.py | 2 +- .../binary_tree/non_recursive_segment_tree.py | 2 +- data_structures/binary_tree/red_black_tree.py | 2 +- data_structures/binary_tree/segment_tree.py | 6 +++--- data_structures/heap/min_heap.py | 2 +- dynamic_programming/longest_common_subsequence.py | 2 +- .../longest_increasing_subsequence_o_nlogn.py | 4 ++-- graphs/articulation_points.py | 2 +- graphs/dinic.py | 2 +- other/sdes.py | 4 ++-- project_euler/problem_011/sol2.py | 2 +- pyproject.toml | 1 - strings/manacher.py | 2 +- 14 files changed, 23 insertions(+), 24 deletions(-) diff --git a/audio_filters/butterworth_filter.py b/audio_filters/butterworth_filter.py index 6449bc3f3dce..4e6ea1b18fb4 100644 --- a/audio_filters/butterworth_filter.py +++ b/audio_filters/butterworth_filter.py @@ -13,7 +13,7 @@ def make_lowpass( frequency: int, samplerate: int, - q_factor: float = 1 / sqrt(2), # noqa: B008 + q_factor: float = 1 / sqrt(2), ) -> IIRFilter: """ Creates a low-pass filter @@ -43,7 +43,7 @@ def make_lowpass( def make_highpass( frequency: int, samplerate: int, - q_factor: float = 1 / sqrt(2), # noqa: B008 + q_factor: float = 1 / sqrt(2), ) -> IIRFilter: """ Creates a high-pass filter @@ -73,7 +73,7 @@ def make_highpass( def make_bandpass( frequency: int, samplerate: int, - q_factor: float = 1 / sqrt(2), # noqa: B008 + q_factor: float = 1 / sqrt(2), ) -> IIRFilter: """ Creates a band-pass filter @@ -104,7 +104,7 @@ def make_bandpass( def make_allpass( frequency: int, samplerate: int, - q_factor: float = 1 / sqrt(2), # noqa: B008 + q_factor: float = 1 / sqrt(2), ) -> IIRFilter: """ Creates an all-pass filter @@ -132,7 +132,7 @@ def make_peak( frequency: int, samplerate: int, gain_db: float, - q_factor: float = 1 / sqrt(2), # noqa: B008 + q_factor: float = 1 / sqrt(2), ) -> IIRFilter: """ Creates a peak filter @@ -164,7 +164,7 @@ def make_lowshelf( frequency: int, samplerate: int, gain_db: float, - q_factor: float = 1 / sqrt(2), # noqa: B008 + q_factor: float = 1 / sqrt(2), ) -> IIRFilter: """ Creates a low-shelf filter @@ -201,7 +201,7 @@ def make_highshelf( frequency: int, samplerate: int, gain_db: float, - q_factor: float = 1 / sqrt(2), # noqa: B008 + q_factor: float = 1 / sqrt(2), ) -> IIRFilter: """ Creates a high-shelf filter diff --git a/data_structures/binary_tree/basic_binary_tree.py b/data_structures/binary_tree/basic_binary_tree.py index 0439413d95b5..9d4c1bdbb57a 100644 --- a/data_structures/binary_tree/basic_binary_tree.py +++ b/data_structures/binary_tree/basic_binary_tree.py @@ -85,7 +85,7 @@ def depth(self) -> int: """ return self._depth(self.root) - def _depth(self, node: Node | None) -> int: # noqa: UP007 + def _depth(self, node: Node | None) -> int: if not node: return 0 return 1 + max(self._depth(node.left), self._depth(node.right)) diff --git a/data_structures/binary_tree/non_recursive_segment_tree.py b/data_structures/binary_tree/non_recursive_segment_tree.py index 42c78a3a1be0..45c476701d79 100644 --- a/data_structures/binary_tree/non_recursive_segment_tree.py +++ b/data_structures/binary_tree/non_recursive_segment_tree.py @@ -87,7 +87,7 @@ def update(self, p: int, v: T) -> None: p = p // 2 self.st[p] = self.fn(self.st[p * 2], self.st[p * 2 + 1]) - def query(self, l: int, r: int) -> T | None: # noqa: E741 + def query(self, l: int, r: int) -> T | None: """ Get range query value in log(N) time :param l: left element index diff --git a/data_structures/binary_tree/red_black_tree.py b/data_structures/binary_tree/red_black_tree.py index bdd808c828e0..e68d8d1e3735 100644 --- a/data_structures/binary_tree/red_black_tree.py +++ b/data_structures/binary_tree/red_black_tree.py @@ -152,7 +152,7 @@ def _insert_repair(self) -> None: self.grandparent.color = 1 self.grandparent._insert_repair() - def remove(self, label: int) -> RedBlackTree: # noqa: PLR0912 + def remove(self, label: int) -> RedBlackTree: """Remove label from this tree.""" if self.label == label: if self.left and self.right: diff --git a/data_structures/binary_tree/segment_tree.py b/data_structures/binary_tree/segment_tree.py index 3b0b32946f6e..bb9c1ae2268b 100644 --- a/data_structures/binary_tree/segment_tree.py +++ b/data_structures/binary_tree/segment_tree.py @@ -35,7 +35,7 @@ def right(self, idx): """ return idx * 2 + 1 - def build(self, idx, l, r): # noqa: E741 + def build(self, idx, l, r): if l == r: self.st[idx] = self.A[l] else: @@ -56,7 +56,7 @@ def update(self, a, b, val): """ return self.update_recursive(1, 0, self.N - 1, a - 1, b - 1, val) - def update_recursive(self, idx, l, r, a, b, val): # noqa: E741 + def update_recursive(self, idx, l, r, a, b, val): """ update(1, 1, N, a, b, v) for update val v to [a,b] """ @@ -83,7 +83,7 @@ def query(self, a, b): """ return self.query_recursive(1, 0, self.N - 1, a - 1, b - 1) - def query_recursive(self, idx, l, r, a, b): # noqa: E741 + def query_recursive(self, idx, l, r, a, b): """ query(1, 1, N, a, b) for query max of [a,b] """ diff --git a/data_structures/heap/min_heap.py b/data_structures/heap/min_heap.py index ecb1876493b0..39f6d99e8a4c 100644 --- a/data_structures/heap/min_heap.py +++ b/data_structures/heap/min_heap.py @@ -66,7 +66,7 @@ def build_heap(self, array): # this is min-heapify method def sift_down(self, idx, array): while True: - l = self.get_left_child_idx(idx) # noqa: E741 + l = self.get_left_child_idx(idx) r = self.get_right_child_idx(idx) smallest = idx diff --git a/dynamic_programming/longest_common_subsequence.py b/dynamic_programming/longest_common_subsequence.py index 178b4169b213..22f50a166ae4 100644 --- a/dynamic_programming/longest_common_subsequence.py +++ b/dynamic_programming/longest_common_subsequence.py @@ -38,7 +38,7 @@ def longest_common_subsequence(x: str, y: str): n = len(y) # declaring the array for storing the dp values - l = [[0] * (n + 1) for _ in range(m + 1)] # noqa: E741 + l = [[0] * (n + 1) for _ in range(m + 1)] for i in range(1, m + 1): for j in range(1, n + 1): diff --git a/dynamic_programming/longest_increasing_subsequence_o_nlogn.py b/dynamic_programming/longest_increasing_subsequence_o_nlogn.py index 5e11d729f395..44e333e97779 100644 --- a/dynamic_programming/longest_increasing_subsequence_o_nlogn.py +++ b/dynamic_programming/longest_increasing_subsequence_o_nlogn.py @@ -7,13 +7,13 @@ from __future__ import annotations -def ceil_index(v, l, r, key): # noqa: E741 +def ceil_index(v, l, r, key): while r - l > 1: m = (l + r) // 2 if v[m] >= key: r = m else: - l = m # noqa: E741 + l = m return r diff --git a/graphs/articulation_points.py b/graphs/articulation_points.py index d28045282425..3fcaffd73725 100644 --- a/graphs/articulation_points.py +++ b/graphs/articulation_points.py @@ -1,5 +1,5 @@ # Finding Articulation Points in Undirected Graph -def compute_ap(l): # noqa: E741 +def compute_ap(l): n = len(l) out_edge_count = 0 low = [0] * n diff --git a/graphs/dinic.py b/graphs/dinic.py index aaf3a119525c..4f5e81236984 100644 --- a/graphs/dinic.py +++ b/graphs/dinic.py @@ -37,7 +37,7 @@ def depth_first_search(self, vertex, sink, flow): # Here we calculate the flow that reaches the sink def max_flow(self, source, sink): flow, self.q[0] = 0, source - for l in range(31): # noqa: E741 l = 30 maybe faster for random data + for l in range(31): # l = 30 maybe faster for random data while True: self.lvl, self.ptr = [0] * len(self.q), [0] * len(self.q) qi, qe, self.lvl[source] = 0, 1, 1 diff --git a/other/sdes.py b/other/sdes.py index 31105984b9bb..a69add3430c3 100644 --- a/other/sdes.py +++ b/other/sdes.py @@ -44,9 +44,9 @@ def function(expansion, s0, s1, key, message): right = message[4:] temp = apply_table(right, expansion) temp = xor(temp, key) - l = apply_sbox(s0, temp[:4]) # noqa: E741 + l = apply_sbox(s0, temp[:4]) r = apply_sbox(s1, temp[4:]) - l = "0" * (2 - len(l)) + l # noqa: E741 + l = "0" * (2 - len(l)) + l r = "0" * (2 - len(r)) + r temp = apply_table(l + r, p4_table) temp = xor(left, temp) diff --git a/project_euler/problem_011/sol2.py b/project_euler/problem_011/sol2.py index 9ea0db991aaf..2958305331a9 100644 --- a/project_euler/problem_011/sol2.py +++ b/project_euler/problem_011/sol2.py @@ -35,7 +35,7 @@ def solution(): 70600674 """ with open(os.path.dirname(__file__) + "/grid.txt") as f: - l = [] # noqa: E741 + l = [] for _ in range(20): l.append([int(x) for x in f.readline().split()]) diff --git a/pyproject.toml b/pyproject.toml index 22da7cb777b5..c8a8744abc83 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,7 +15,6 @@ lint.ignore = [ # `ruff rule S101` for a description of that rule "PT011", # `pytest.raises(Exception)` is too broad, set the `match` parameter or use a more specific exception "PT018", # Assertion should be broken down into multiple parts "RUF00", # Ambiguous unicode character and other rules - "RUF100", # Unused `noqa` directive -- FIX ME "S101", # Use of `assert` detected -- DO NOT FIX "S105", # Possible hardcoded password: 'password' "S113", # Probable use of requests call without timeout -- FIX ME diff --git a/strings/manacher.py b/strings/manacher.py index c58c7c19ec44..ca546e533acd 100644 --- a/strings/manacher.py +++ b/strings/manacher.py @@ -50,7 +50,7 @@ def palindromic_string(input_string: str) -> str: # does this string is ending after the previously explored end (that is r) ? # if yes the update the new r to the last index of this if j + k - 1 > r: - l = j - k + 1 # noqa: E741 + l = j - k + 1 r = j + k - 1 # update max_length and start position From f8a948914b928d9fd3c0e32c034bd90315caa389 Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Mon, 1 Apr 2024 22:39:31 +0300 Subject: [PATCH 592/757] Enable ruff NPY002 rule (#11336) --- linear_algebra/src/conjugate_gradient.py | 6 ++++-- machine_learning/decision_tree.py | 3 ++- machine_learning/k_means_clust.py | 6 +++--- machine_learning/sequential_minimum_optimization.py | 5 +++-- neural_network/back_propagation_neural_network.py | 8 +++++--- neural_network/convolution_neural_network.py | 13 +++++++------ neural_network/input_data.py | 6 +++--- neural_network/two_hidden_layers_neural_network.py | 9 +++++---- pyproject.toml | 1 - 9 files changed, 32 insertions(+), 25 deletions(-) diff --git a/linear_algebra/src/conjugate_gradient.py b/linear_algebra/src/conjugate_gradient.py index 4c0b58deb978..45da35813978 100644 --- a/linear_algebra/src/conjugate_gradient.py +++ b/linear_algebra/src/conjugate_gradient.py @@ -61,7 +61,8 @@ def _create_spd_matrix(dimension: int) -> Any: >>> _is_matrix_spd(spd_matrix) True """ - random_matrix = np.random.randn(dimension, dimension) + rng = np.random.default_rng() + random_matrix = rng.normal(size=(dimension, dimension)) spd_matrix = np.dot(random_matrix, random_matrix.T) assert _is_matrix_spd(spd_matrix) return spd_matrix @@ -157,7 +158,8 @@ def test_conjugate_gradient() -> None: # Create linear system with SPD matrix and known solution x_true. dimension = 3 spd_matrix = _create_spd_matrix(dimension) - x_true = np.random.randn(dimension, 1) + rng = np.random.default_rng() + x_true = rng.normal(size=(dimension, 1)) b = np.dot(spd_matrix, x_true) # Numpy solution. diff --git a/machine_learning/decision_tree.py b/machine_learning/decision_tree.py index 7f129919a3ce..e48905eeac6a 100644 --- a/machine_learning/decision_tree.py +++ b/machine_learning/decision_tree.py @@ -187,7 +187,8 @@ def main(): tree = DecisionTree(depth=10, min_leaf_size=10) tree.train(x, y) - test_cases = (np.random.rand(10) * 2) - 1 + rng = np.random.default_rng() + test_cases = (rng.random(10) * 2) - 1 predictions = np.array([tree.predict(x) for x in test_cases]) avg_error = np.mean((predictions - test_cases) ** 2) diff --git a/machine_learning/k_means_clust.py b/machine_learning/k_means_clust.py index 9f6646944458..a926362fc18b 100644 --- a/machine_learning/k_means_clust.py +++ b/machine_learning/k_means_clust.py @@ -55,12 +55,12 @@ def get_initial_centroids(data, k, seed=None): """Randomly choose k data points as initial centroids""" - if seed is not None: # useful for obtaining consistent results - np.random.seed(seed) + # useful for obtaining consistent results + rng = np.random.default_rng(seed) n = data.shape[0] # number of data points # Pick K indices from range [0, N). - rand_indices = np.random.randint(0, n, k) + rand_indices = rng.integers(0, n, k) # Keep centroids as dense format, as many entries will be nonzero due to averaging. # As long as at least one document in a cluster contains a word, diff --git a/machine_learning/sequential_minimum_optimization.py b/machine_learning/sequential_minimum_optimization.py index be16baca1a4c..408d59ab5d29 100644 --- a/machine_learning/sequential_minimum_optimization.py +++ b/machine_learning/sequential_minimum_optimization.py @@ -289,12 +289,13 @@ def _choose_a2(self, i1): if cmd is None: return - for i2 in np.roll(self.unbound, np.random.choice(self.length)): + rng = np.random.default_rng() + for i2 in np.roll(self.unbound, rng.choice(self.length)): cmd = yield i1, i2 if cmd is None: return - for i2 in np.roll(self._all_samples, np.random.choice(self.length)): + for i2 in np.roll(self._all_samples, rng.choice(self.length)): cmd = yield i1, i2 if cmd is None: return diff --git a/neural_network/back_propagation_neural_network.py b/neural_network/back_propagation_neural_network.py index 7e0bdbbe2857..6131a13e945e 100644 --- a/neural_network/back_propagation_neural_network.py +++ b/neural_network/back_propagation_neural_network.py @@ -51,8 +51,9 @@ def __init__( self.is_input_layer = is_input_layer def initializer(self, back_units): - self.weight = np.asmatrix(np.random.normal(0, 0.5, (self.units, back_units))) - self.bias = np.asmatrix(np.random.normal(0, 0.5, self.units)).T + rng = np.random.default_rng() + self.weight = np.asmatrix(rng.normal(0, 0.5, (self.units, back_units))) + self.bias = np.asmatrix(rng.normal(0, 0.5, self.units)).T if self.activation is None: self.activation = sigmoid @@ -174,7 +175,8 @@ def plot_loss(self): def example(): - x = np.random.randn(10, 10) + rng = np.random.default_rng() + x = rng.normal(size=(10, 10)) y = np.asarray( [ [0.8, 0.4], diff --git a/neural_network/convolution_neural_network.py b/neural_network/convolution_neural_network.py index 07cc456b7466..3c551924442d 100644 --- a/neural_network/convolution_neural_network.py +++ b/neural_network/convolution_neural_network.py @@ -41,15 +41,16 @@ def __init__( self.size_pooling1 = size_p1 self.rate_weight = rate_w self.rate_thre = rate_t + rng = np.random.default_rng() self.w_conv1 = [ - np.asmatrix(-1 * np.random.rand(self.conv1[0], self.conv1[0]) + 0.5) + np.asmatrix(-1 * rng.random((self.conv1[0], self.conv1[0])) + 0.5) for i in range(self.conv1[1]) ] - self.wkj = np.asmatrix(-1 * np.random.rand(self.num_bp3, self.num_bp2) + 0.5) - self.vji = np.asmatrix(-1 * np.random.rand(self.num_bp2, self.num_bp1) + 0.5) - self.thre_conv1 = -2 * np.random.rand(self.conv1[1]) + 1 - self.thre_bp2 = -2 * np.random.rand(self.num_bp2) + 1 - self.thre_bp3 = -2 * np.random.rand(self.num_bp3) + 1 + self.wkj = np.asmatrix(-1 * rng.random((self.num_bp3, self.num_bp2)) + 0.5) + self.vji = np.asmatrix(-1 * rng.random((self.num_bp2, self.num_bp1)) + 0.5) + self.thre_conv1 = -2 * rng.random(self.conv1[1]) + 1 + self.thre_bp2 = -2 * rng.random(self.num_bp2) + 1 + self.thre_bp3 = -2 * rng.random(self.num_bp3) + 1 def save_model(self, save_path): # save model dict with pickle diff --git a/neural_network/input_data.py b/neural_network/input_data.py index 9d4195487dbb..d189e3f9e0d9 100644 --- a/neural_network/input_data.py +++ b/neural_network/input_data.py @@ -153,7 +153,7 @@ def __init__( """ seed1, seed2 = random_seed.get_seed(seed) # If op level seed is not set, use whatever graph level seed is returned - np.random.seed(seed1 if seed is None else seed2) + self._rng = np.random.default_rng(seed1 if seed is None else seed2) dtype = dtypes.as_dtype(dtype).base_dtype if dtype not in (dtypes.uint8, dtypes.float32): raise TypeError("Invalid image dtype %r, expected uint8 or float32" % dtype) @@ -211,7 +211,7 @@ def next_batch(self, batch_size, fake_data=False, shuffle=True): # Shuffle for the first epoch if self._epochs_completed == 0 and start == 0 and shuffle: perm0 = np.arange(self._num_examples) - np.random.shuffle(perm0) + self._rng.shuffle(perm0) self._images = self.images[perm0] self._labels = self.labels[perm0] # Go to the next epoch @@ -225,7 +225,7 @@ def next_batch(self, batch_size, fake_data=False, shuffle=True): # Shuffle the data if shuffle: perm = np.arange(self._num_examples) - np.random.shuffle(perm) + self._rng.shuffle(perm) self._images = self.images[perm] self._labels = self.labels[perm] # Start next epoch diff --git a/neural_network/two_hidden_layers_neural_network.py b/neural_network/two_hidden_layers_neural_network.py index dea7e2342d9f..d488de590cc2 100644 --- a/neural_network/two_hidden_layers_neural_network.py +++ b/neural_network/two_hidden_layers_neural_network.py @@ -28,19 +28,20 @@ def __init__(self, input_array: np.ndarray, output_array: np.ndarray) -> None: # Random initial weights are assigned. # self.input_array.shape[1] is used to represent number of nodes in input layer. # First hidden layer consists of 4 nodes. - self.input_layer_and_first_hidden_layer_weights = np.random.rand( - self.input_array.shape[1], 4 + rng = np.random.default_rng() + self.input_layer_and_first_hidden_layer_weights = rng.random( + (self.input_array.shape[1], 4) ) # Random initial values for the first hidden layer. # First hidden layer has 4 nodes. # Second hidden layer has 3 nodes. - self.first_hidden_layer_and_second_hidden_layer_weights = np.random.rand(4, 3) + self.first_hidden_layer_and_second_hidden_layer_weights = rng.random((4, 3)) # Random initial values for the second hidden layer. # Second hidden layer has 3 nodes. # Output layer has 1 node. - self.second_hidden_layer_and_output_layer_weights = np.random.rand(3, 1) + self.second_hidden_layer_and_output_layer_weights = rng.random((3, 1)) # Real output values provided. self.output_array = output_array diff --git a/pyproject.toml b/pyproject.toml index c8a8744abc83..50cd38005f09 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,6 @@ lint.ignore = [ # `ruff rule S101` for a description of that rule "EXE001", # Shebang is present but file is not executable" -- FIX ME "G004", # Logging statement uses f-string "INP001", # File `x/y/z.py` is part of an implicit namespace package. Add an `__init__.py`. -- FIX ME - "NPY002", # Replace legacy `np.random.choice` call with `np.random.Generator` -- FIX ME "PGH003", # Use specific rule codes when ignoring type issues -- FIX ME "PLC1901", # `{}` can be simplified to `{}` as an empty string is falsey "PLW060", # Using global for `{name}` but no assignment is done -- DO NOT FIX From 93fb555e0a97096f62a122e73cfdc6f0579cefbe Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Tue, 2 Apr 2024 04:27:56 +0300 Subject: [PATCH 593/757] Enable ruff SIM102 rule (#11341) * Enable ruff SIM102 rule * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- data_structures/arrays/sudoku_solver.py | 7 +++---- .../stacks/balanced_parentheses.py | 7 ++++--- graphs/a_star.py | 20 ++++++++++++------- graphs/bi_directional_dijkstra.py | 8 +++++--- other/davis_putnam_logemann_loveland.py | 7 +++---- project_euler/problem_033/sol1.py | 10 +++++++--- project_euler/problem_037/sol1.py | 7 ++++--- project_euler/problem_107/sol1.py | 9 +++++---- project_euler/problem_207/sol1.py | 8 +++++--- pyproject.toml | 1 - scheduling/shortest_job_first.py | 13 +++++++----- scripts/validate_solutions.py | 11 ++++++---- web_programming/emails_from_url.py | 15 ++++++++------ 13 files changed, 73 insertions(+), 50 deletions(-) diff --git a/data_structures/arrays/sudoku_solver.py b/data_structures/arrays/sudoku_solver.py index c9dffcde2379..5c1cff06f9d4 100644 --- a/data_structures/arrays/sudoku_solver.py +++ b/data_structures/arrays/sudoku_solver.py @@ -92,10 +92,9 @@ def eliminate(values, s, d): dplaces = [s for s in u if d in values[s]] if len(dplaces) == 0: return False ## Contradiction: no place for this value - elif len(dplaces) == 1: - # d can only be in one place in unit; assign it there - if not assign(values, dplaces[0], d): - return False + # d can only be in one place in unit; assign it there + elif len(dplaces) == 1 and not assign(values, dplaces[0], d): + return False return values diff --git a/data_structures/stacks/balanced_parentheses.py b/data_structures/stacks/balanced_parentheses.py index 3c036c220e5c..928815bb2111 100644 --- a/data_structures/stacks/balanced_parentheses.py +++ b/data_structures/stacks/balanced_parentheses.py @@ -19,9 +19,10 @@ def balanced_parentheses(parentheses: str) -> bool: for bracket in parentheses: if bracket in bracket_pairs: stack.push(bracket) - elif bracket in (")", "]", "}"): - if stack.is_empty() or bracket_pairs[stack.pop()] != bracket: - return False + elif bracket in (")", "]", "}") and ( + stack.is_empty() or bracket_pairs[stack.pop()] != bracket + ): + return False return stack.is_empty() diff --git a/graphs/a_star.py b/graphs/a_star.py index 06da3b5cd863..1d7063ccc55a 100644 --- a/graphs/a_star.py +++ b/graphs/a_star.py @@ -75,13 +75,19 @@ def search( for i in range(len(DIRECTIONS)): # to try out different valid actions x2 = x + DIRECTIONS[i][0] y2 = y + DIRECTIONS[i][1] - if x2 >= 0 and x2 < len(grid) and y2 >= 0 and y2 < len(grid[0]): - if closed[x2][y2] == 0 and grid[x2][y2] == 0: - g2 = g + cost - f2 = g2 + heuristic[x2][y2] - cell.append([f2, g2, x2, y2]) - closed[x2][y2] = 1 - action[x2][y2] = i + if ( + x2 >= 0 + and x2 < len(grid) + and y2 >= 0 + and y2 < len(grid[0]) + and closed[x2][y2] == 0 + and grid[x2][y2] == 0 + ): + g2 = g + cost + f2 = g2 + heuristic[x2][y2] + cell.append([f2, g2, x2, y2]) + closed[x2][y2] = 1 + action[x2][y2] = i invpath = [] x = goal[0] y = goal[1] diff --git a/graphs/bi_directional_dijkstra.py b/graphs/bi_directional_dijkstra.py index 7b9eac6c8587..d2c4030b921b 100644 --- a/graphs/bi_directional_dijkstra.py +++ b/graphs/bi_directional_dijkstra.py @@ -36,9 +36,11 @@ def pass_and_relaxation( queue.put((new_cost_f, nxt)) cst_fwd[nxt] = new_cost_f parent[nxt] = v - if nxt in visited_backward: - if cst_fwd[v] + d + cst_bwd[nxt] < shortest_distance: - shortest_distance = cst_fwd[v] + d + cst_bwd[nxt] + if ( + nxt in visited_backward + and cst_fwd[v] + d + cst_bwd[nxt] < shortest_distance + ): + shortest_distance = cst_fwd[v] + d + cst_bwd[nxt] return shortest_distance diff --git a/other/davis_putnam_logemann_loveland.py b/other/davis_putnam_logemann_loveland.py index 5c6e2d9ffd5e..3a76f3dfef08 100644 --- a/other/davis_putnam_logemann_loveland.py +++ b/other/davis_putnam_logemann_loveland.py @@ -64,10 +64,9 @@ def assign(self, model: dict[str, bool | None]) -> None: value = model[symbol] else: continue - if value is not None: - # Complement assignment if literal is in complemented form - if literal.endswith("'"): - value = not value + # Complement assignment if literal is in complemented form + if value is not None and literal.endswith("'"): + value = not value self.literals[literal] = value def evaluate(self, model: dict[str, bool | None]) -> bool | None: diff --git a/project_euler/problem_033/sol1.py b/project_euler/problem_033/sol1.py index 187fd61bde6c..71790d34fbed 100644 --- a/project_euler/problem_033/sol1.py +++ b/project_euler/problem_033/sol1.py @@ -44,9 +44,13 @@ def fraction_list(digit_len: int) -> list[str]: last_digit = int("1" + "0" * digit_len) for num in range(den, last_digit): while den <= 99: - if (num != den) and (num % 10 == den // 10) and (den % 10 != 0): - if is_digit_cancelling(num, den): - solutions.append(f"{num}/{den}") + if ( + (num != den) + and (num % 10 == den // 10) + and (den % 10 != 0) + and is_digit_cancelling(num, den) + ): + solutions.append(f"{num}/{den}") den += 1 num += 1 den = 10 diff --git a/project_euler/problem_037/sol1.py b/project_euler/problem_037/sol1.py index ef7686cbcb96..9c09065f4bd0 100644 --- a/project_euler/problem_037/sol1.py +++ b/project_euler/problem_037/sol1.py @@ -85,9 +85,10 @@ def validate(n: int) -> bool: >>> validate(3797) True """ - if len(str(n)) > 3: - if not is_prime(int(str(n)[-3:])) or not is_prime(int(str(n)[:3])): - return False + if len(str(n)) > 3 and ( + not is_prime(int(str(n)[-3:])) or not is_prime(int(str(n)[:3])) + ): + return False return True diff --git a/project_euler/problem_107/sol1.py b/project_euler/problem_107/sol1.py index 3fe75909e2ea..79cdd937042e 100644 --- a/project_euler/problem_107/sol1.py +++ b/project_euler/problem_107/sol1.py @@ -81,10 +81,11 @@ def prims_algorithm(self) -> Graph: while len(subgraph.vertices) < len(self.vertices): min_weight = max(self.edges.values()) + 1 for edge, weight in self.edges.items(): - if (edge[0] in subgraph.vertices) ^ (edge[1] in subgraph.vertices): - if weight < min_weight: - min_edge = edge - min_weight = weight + if (edge[0] in subgraph.vertices) ^ ( + edge[1] in subgraph.vertices + ) and weight < min_weight: + min_edge = edge + min_weight = weight subgraph.add_edge(min_edge, min_weight) diff --git a/project_euler/problem_207/sol1.py b/project_euler/problem_207/sol1.py index 2b3591f51cfa..c83dc1d4aaef 100644 --- a/project_euler/problem_207/sol1.py +++ b/project_euler/problem_207/sol1.py @@ -88,9 +88,11 @@ def solution(max_proportion: float = 1 / 12345) -> int: total_partitions += 1 if check_partition_perfect(partition_candidate): perfect_partitions += 1 - if perfect_partitions > 0: - if perfect_partitions / total_partitions < max_proportion: - return int(partition_candidate) + if ( + perfect_partitions > 0 + and perfect_partitions / total_partitions < max_proportion + ): + return int(partition_candidate) integer += 1 diff --git a/pyproject.toml b/pyproject.toml index 50cd38005f09..e3cf42c92c54 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,7 +18,6 @@ lint.ignore = [ # `ruff rule S101` for a description of that rule "S105", # Possible hardcoded password: 'password' "S113", # Probable use of requests call without timeout -- FIX ME "S311", # Standard pseudo-random generators are not suitable for cryptographic purposes -- FIX ME - "SIM102", # Use a single `if` statement instead of nested `if` statements -- FIX ME "SLF001", # Private member accessed: `_Iterator` -- FIX ME "UP038", # Use `X | Y` in `{}` call instead of `(X, Y)` -- DO NOT FIX ] diff --git a/scheduling/shortest_job_first.py b/scheduling/shortest_job_first.py index cfd0417ea62d..6899ec87c591 100644 --- a/scheduling/shortest_job_first.py +++ b/scheduling/shortest_job_first.py @@ -37,11 +37,14 @@ def calculate_waitingtime( # Process until all processes are completed while complete != no_of_processes: for j in range(no_of_processes): - if arrival_time[j] <= increment_time and remaining_time[j] > 0: - if remaining_time[j] < minm: - minm = remaining_time[j] - short = j - check = True + if ( + arrival_time[j] <= increment_time + and remaining_time[j] > 0 + and remaining_time[j] < minm + ): + minm = remaining_time[j] + short = j + check = True if not check: increment_time += 1 diff --git a/scripts/validate_solutions.py b/scripts/validate_solutions.py index ca4af5261a8f..0afbdde315c7 100755 --- a/scripts/validate_solutions.py +++ b/scripts/validate_solutions.py @@ -71,10 +71,13 @@ def added_solution_file_path() -> list[pathlib.Path]: def collect_solution_file_paths() -> list[pathlib.Path]: - if os.environ.get("CI") and os.environ.get("GITHUB_EVENT_NAME") == "pull_request": - # Return only if there are any, otherwise default to all solutions - if filepaths := added_solution_file_path(): - return filepaths + # Return only if there are any, otherwise default to all solutions + if ( + os.environ.get("CI") + and os.environ.get("GITHUB_EVENT_NAME") == "pull_request" + and (filepaths := added_solution_file_path()) + ): + return filepaths return all_solution_file_paths() diff --git a/web_programming/emails_from_url.py b/web_programming/emails_from_url.py index 6b4bacfe7d5a..26c88e1b13a5 100644 --- a/web_programming/emails_from_url.py +++ b/web_programming/emails_from_url.py @@ -30,12 +30,15 @@ def handle_starttag(self, tag: str, attrs: list[tuple[str, str | None]]) -> None if tag == "a": # Check the list of defined attributes. for name, value in attrs: - # If href is defined, and not empty nor # print it. - if name == "href" and value != "#" and value != "": - # If not already in urls. - if value not in self.urls: - url = parse.urljoin(self.domain, value) - self.urls.append(url) + # If href is defined, not empty nor # print it and not already in urls. + if ( + name == "href" + and value != "#" + and value != "" + and value not in self.urls + ): + url = parse.urljoin(self.domain, value) + self.urls.append(url) # Get main domain name (example.com) From f8cdb3e9482ddca85cd1bffa96c038afc13f9c85 Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Tue, 2 Apr 2024 19:44:37 +0300 Subject: [PATCH 594/757] Enable ruff S105 rule (#11343) * Enable ruff S105 rule * Update web_programming/recaptcha_verification.py --------- Co-authored-by: Christian Clauss --- pyproject.toml | 1 - web_programming/recaptcha_verification.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index e3cf42c92c54..65a0754d678c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,7 +15,6 @@ lint.ignore = [ # `ruff rule S101` for a description of that rule "PT018", # Assertion should be broken down into multiple parts "RUF00", # Ambiguous unicode character and other rules "S101", # Use of `assert` detected -- DO NOT FIX - "S105", # Possible hardcoded password: 'password' "S113", # Probable use of requests call without timeout -- FIX ME "S311", # Standard pseudo-random generators are not suitable for cryptographic purposes -- FIX ME "SLF001", # Private member accessed: `_Iterator` -- FIX ME diff --git a/web_programming/recaptcha_verification.py b/web_programming/recaptcha_verification.py index b03afb28ec53..c9b691b28a8b 100644 --- a/web_programming/recaptcha_verification.py +++ b/web_programming/recaptcha_verification.py @@ -43,7 +43,7 @@ def login_using_recaptcha(request): # Enter your recaptcha secret key here - secret_key = "secretKey" + secret_key = "secretKey" # noqa: S105 url = "https://www.google.com/recaptcha/api/siteverify" # when method is not POST, direct user to login page From f437f922792b8c7e3fbb168a1ec6bfdf183a7304 Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Tue, 2 Apr 2024 22:13:56 +0300 Subject: [PATCH 595/757] Enable ruff INP001 rule (#11346) * Enable ruff INP001 rule * Fix * Fix * Fix * Fix * Fix --- data_structures/arrays/__init__.py | 0 data_structures/hashing/tests/__init__.py | 0 digital_image_processing/morphological_operations/__init__.py | 0 electronics/__init__.py | 0 electronics/circular_convolution.py | 3 +-- fractals/__init__.py | 0 geometry/__init__.py | 0 greedy_methods/__init__.py | 0 linear_algebra/src/gaussian_elimination_pivoting/__init__.py | 0 linear_programming/__init__.py | 0 maths/numerical_analysis/__init__.py | 0 maths/special_numbers/__init__.py | 0 neural_network/activation_functions/__init__.py | 0 neural_network/activation_functions/mish.py | 3 ++- pyproject.toml | 1 - 15 files changed, 3 insertions(+), 4 deletions(-) create mode 100644 data_structures/arrays/__init__.py create mode 100644 data_structures/hashing/tests/__init__.py create mode 100644 digital_image_processing/morphological_operations/__init__.py create mode 100644 electronics/__init__.py create mode 100644 fractals/__init__.py create mode 100644 geometry/__init__.py create mode 100644 greedy_methods/__init__.py create mode 100644 linear_algebra/src/gaussian_elimination_pivoting/__init__.py create mode 100644 linear_programming/__init__.py create mode 100644 maths/numerical_analysis/__init__.py create mode 100644 maths/special_numbers/__init__.py create mode 100644 neural_network/activation_functions/__init__.py diff --git a/data_structures/arrays/__init__.py b/data_structures/arrays/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/data_structures/hashing/tests/__init__.py b/data_structures/hashing/tests/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/digital_image_processing/morphological_operations/__init__.py b/digital_image_processing/morphological_operations/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/electronics/__init__.py b/electronics/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/electronics/circular_convolution.py b/electronics/circular_convolution.py index f2e35742e944..768f2ad941bc 100644 --- a/electronics/circular_convolution.py +++ b/electronics/circular_convolution.py @@ -37,8 +37,7 @@ def circular_convolution(self) -> list[float]: using matrix method Usage: - >>> import circular_convolution as cc - >>> convolution = cc.CircularConvolution() + >>> convolution = CircularConvolution() >>> convolution.circular_convolution() [10, 10, 6, 14] diff --git a/fractals/__init__.py b/fractals/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/geometry/__init__.py b/geometry/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/greedy_methods/__init__.py b/greedy_methods/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/linear_algebra/src/gaussian_elimination_pivoting/__init__.py b/linear_algebra/src/gaussian_elimination_pivoting/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/linear_programming/__init__.py b/linear_programming/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/maths/numerical_analysis/__init__.py b/maths/numerical_analysis/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/maths/special_numbers/__init__.py b/maths/special_numbers/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/neural_network/activation_functions/__init__.py b/neural_network/activation_functions/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/neural_network/activation_functions/mish.py b/neural_network/activation_functions/mish.py index e51655df8a3f..57a91413fe50 100644 --- a/neural_network/activation_functions/mish.py +++ b/neural_network/activation_functions/mish.py @@ -7,7 +7,8 @@ """ import numpy as np -from softplus import softplus + +from .softplus import softplus def mish(vector: np.ndarray) -> np.ndarray: diff --git a/pyproject.toml b/pyproject.toml index 65a0754d678c..9689cf2b37aa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,6 @@ lint.ignore = [ # `ruff rule S101` for a description of that rule "EM101", # Exception must not use a string literal, assign to variable first "EXE001", # Shebang is present but file is not executable" -- FIX ME "G004", # Logging statement uses f-string - "INP001", # File `x/y/z.py` is part of an implicit namespace package. Add an `__init__.py`. -- FIX ME "PGH003", # Use specific rule codes when ignoring type issues -- FIX ME "PLC1901", # `{}` can be simplified to `{}` as an empty string is falsey "PLW060", # Using global for `{name}` but no assignment is done -- DO NOT FIX From f5bbea3776a5038d0e428ce3c06c25086076e212 Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Tue, 2 Apr 2024 22:18:47 +0300 Subject: [PATCH 596/757] Enable ruff RUF005 rule (#11344) --- data_structures/binary_tree/binary_search_tree.py | 2 +- dynamic_programming/subset_generation.py | 2 +- maths/odd_sieve.py | 2 +- pyproject.toml | 5 ++++- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/data_structures/binary_tree/binary_search_tree.py b/data_structures/binary_tree/binary_search_tree.py index 090e3e25fe6d..32194ddc2043 100644 --- a/data_structures/binary_tree/binary_search_tree.py +++ b/data_structures/binary_tree/binary_search_tree.py @@ -336,7 +336,7 @@ def inorder(curr_node: Node | None) -> list[Node]: """ node_list = [] if curr_node is not None: - node_list = inorder(curr_node.left) + [curr_node] + inorder(curr_node.right) + node_list = [*inorder(curr_node.left), curr_node, *inorder(curr_node.right)] return node_list diff --git a/dynamic_programming/subset_generation.py b/dynamic_programming/subset_generation.py index 1be412b9374d..d490bca737ba 100644 --- a/dynamic_programming/subset_generation.py +++ b/dynamic_programming/subset_generation.py @@ -45,7 +45,7 @@ def subset_combinations(elements: list[int], n: int) -> list: for i in range(1, r + 1): for j in range(i, 0, -1): for prev_combination in dp[j - 1]: - dp[j].append(tuple(prev_combination) + (elements[i - 1],)) + dp[j].append((*prev_combination, elements[i - 1])) try: return sorted(dp[n]) diff --git a/maths/odd_sieve.py b/maths/odd_sieve.py index 60e92921a94c..06605ca54296 100644 --- a/maths/odd_sieve.py +++ b/maths/odd_sieve.py @@ -33,7 +33,7 @@ def odd_sieve(num: int) -> list[int]: 0, ceil((num - i_squared) / (i << 1)) ) - return [2] + list(compress(range(3, num, 2), sieve)) + return [2, *list(compress(range(3, num, 2), sieve))] if __name__ == "__main__": diff --git a/pyproject.toml b/pyproject.toml index 9689cf2b37aa..e1d7dc91b2b8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,7 +12,10 @@ lint.ignore = [ # `ruff rule S101` for a description of that rule "PLW2901", # PLW2901: Redefined loop variable -- FIX ME "PT011", # `pytest.raises(Exception)` is too broad, set the `match` parameter or use a more specific exception "PT018", # Assertion should be broken down into multiple parts - "RUF00", # Ambiguous unicode character and other rules + "RUF001", # String contains ambiguous {}. Did you mean {}? + "RUF002", # Docstring contains ambiguous {}. Did you mean {}? + "RUF003", # Comment contains ambiguous {}. Did you mean {}? + "RUF007", # Prefer itertools.pairwise() over zip() when iterating over successive pairs "S101", # Use of `assert` detected -- DO NOT FIX "S113", # Probable use of requests call without timeout -- FIX ME "S311", # Standard pseudo-random generators are not suitable for cryptographic purposes -- FIX ME From 53b2926704f3ad3ec2134a114be3a338e755e28a Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Tue, 2 Apr 2024 22:29:34 +0300 Subject: [PATCH 597/757] Enable ruff PGH003 rule (#11345) * Enable ruff PGH003 rule * Fix * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- compression/huffman.py | 4 ++-- data_structures/binary_tree/binary_search_tree.py | 4 ++-- data_structures/linked_list/rotate_to_the_right.py | 2 +- fractals/mandelbrot.py | 2 +- graphics/bezier_curve.py | 2 +- maths/entropy.py | 4 ++-- matrix/spiral_print.py | 4 +++- matrix/tests/test_matrix_operation.py | 2 +- project_euler/problem_092/sol1.py | 2 +- project_euler/problem_104/sol1.py | 2 +- pyproject.toml | 1 - scripts/validate_filenames.py | 2 +- scripts/validate_solutions.py | 6 +++--- web_programming/covid_stats_via_xpath.py | 2 +- 14 files changed, 20 insertions(+), 19 deletions(-) diff --git a/compression/huffman.py b/compression/huffman.py index 65e5c2f25385..44eda6c03180 100644 --- a/compression/huffman.py +++ b/compression/huffman.py @@ -40,7 +40,7 @@ def build_tree(letters: list[Letter]) -> Letter | TreeNode: Run through the list of Letters and build the min heap for the Huffman Tree. """ - response: list[Letter | TreeNode] = letters # type: ignore + response: list[Letter | TreeNode] = list(letters) while len(response) > 1: left = response.pop(0) right = response.pop(0) @@ -59,7 +59,7 @@ def traverse_tree(root: Letter | TreeNode, bitstring: str) -> list[Letter]: if isinstance(root, Letter): root.bitstring[root.letter] = bitstring return [root] - treenode: TreeNode = root # type: ignore + treenode: TreeNode = root letters = [] letters += traverse_tree(treenode.left, bitstring + "0") letters += traverse_tree(treenode.right, bitstring + "1") diff --git a/data_structures/binary_tree/binary_search_tree.py b/data_structures/binary_tree/binary_search_tree.py index 32194ddc2043..3f214d0113a4 100644 --- a/data_structures/binary_tree/binary_search_tree.py +++ b/data_structures/binary_tree/binary_search_tree.py @@ -294,9 +294,9 @@ def remove(self, value: int) -> None: predecessor = self.get_max( node.left ) # Gets the max value of the left branch - self.remove(predecessor.value) # type: ignore + self.remove(predecessor.value) # type: ignore[union-attr] node.value = ( - predecessor.value # type: ignore + predecessor.value # type: ignore[union-attr] ) # Assigns the value to the node to delete and keep tree structure def preorder_traverse(self, node: Node | None) -> Iterable: diff --git a/data_structures/linked_list/rotate_to_the_right.py b/data_structures/linked_list/rotate_to_the_right.py index 51b10481c0ce..6b1c54f4be4d 100644 --- a/data_structures/linked_list/rotate_to_the_right.py +++ b/data_structures/linked_list/rotate_to_the_right.py @@ -63,7 +63,7 @@ def insert_node(head: Node | None, data: int) -> Node: while temp_node.next_node: temp_node = temp_node.next_node - temp_node.next_node = new_node # type: ignore + temp_node.next_node = new_node return head diff --git a/fractals/mandelbrot.py b/fractals/mandelbrot.py index 5eb9af0aafe1..359d965a882d 100644 --- a/fractals/mandelbrot.py +++ b/fractals/mandelbrot.py @@ -17,7 +17,7 @@ import colorsys -from PIL import Image # type: ignore +from PIL import Image def get_distance(x: float, y: float, max_step: int) -> float: diff --git a/graphics/bezier_curve.py b/graphics/bezier_curve.py index 6eeb89da6bdf..9d906f179c92 100644 --- a/graphics/bezier_curve.py +++ b/graphics/bezier_curve.py @@ -2,7 +2,7 @@ # https://www.tutorialspoint.com/computer_graphics/computer_graphics_curves.htm from __future__ import annotations -from scipy.special import comb # type: ignore +from scipy.special import comb class BezierCurve: diff --git a/maths/entropy.py b/maths/entropy.py index 76fac4ee717d..39ec67bea038 100644 --- a/maths/entropy.py +++ b/maths/entropy.py @@ -96,8 +96,8 @@ def analyze_text(text: str) -> tuple[dict, dict]: The first dictionary stores the frequency of single character strings. The second dictionary stores the frequency of two character strings. """ - single_char_strings = Counter() # type: ignore - two_char_strings = Counter() # type: ignore + single_char_strings = Counter() # type: ignore[var-annotated] + two_char_strings = Counter() # type: ignore[var-annotated] single_char_strings[text[-1]] += 1 # first case when we have space at start. diff --git a/matrix/spiral_print.py b/matrix/spiral_print.py index 7ba0a275157b..c16dde69cb56 100644 --- a/matrix/spiral_print.py +++ b/matrix/spiral_print.py @@ -116,7 +116,9 @@ def spiral_traversal(matrix: list[list]) -> list[int]: [1, 2, 3, 4, 8, 12, 11, 10, 9, 5, 6, 7] + spiral_traversal([]) """ if matrix: - return list(matrix.pop(0)) + spiral_traversal(list(zip(*matrix))[::-1]) # type: ignore + return list(matrix.pop(0)) + spiral_traversal( + [list(row) for row in zip(*matrix)][::-1] + ) else: return [] diff --git a/matrix/tests/test_matrix_operation.py b/matrix/tests/test_matrix_operation.py index 638f97daa2ed..addc870ca205 100644 --- a/matrix/tests/test_matrix_operation.py +++ b/matrix/tests/test_matrix_operation.py @@ -12,7 +12,7 @@ import sys import numpy as np -import pytest # type: ignore +import pytest # Custom/local libraries from matrix import matrix_operation as matop diff --git a/project_euler/problem_092/sol1.py b/project_euler/problem_092/sol1.py index 8d3f0c9ddd7b..3e45e82207a7 100644 --- a/project_euler/problem_092/sol1.py +++ b/project_euler/problem_092/sol1.py @@ -68,7 +68,7 @@ def chain(number: int) -> bool: """ if CHAINS[number - 1] is not None: - return CHAINS[number - 1] # type: ignore + return CHAINS[number - 1] # type: ignore[return-value] number_chain = chain(next_number(number)) CHAINS[number - 1] = number_chain diff --git a/project_euler/problem_104/sol1.py b/project_euler/problem_104/sol1.py index 60fd6fe99adb..d84dbcfc9c65 100644 --- a/project_euler/problem_104/sol1.py +++ b/project_euler/problem_104/sol1.py @@ -15,7 +15,7 @@ import sys -sys.set_int_max_str_digits(0) # type: ignore +sys.set_int_max_str_digits(0) def check(number: int) -> bool: diff --git a/pyproject.toml b/pyproject.toml index e1d7dc91b2b8..7eac811395ae 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,6 @@ lint.ignore = [ # `ruff rule S101` for a description of that rule "EM101", # Exception must not use a string literal, assign to variable first "EXE001", # Shebang is present but file is not executable" -- FIX ME "G004", # Logging statement uses f-string - "PGH003", # Use specific rule codes when ignoring type issues -- FIX ME "PLC1901", # `{}` can be simplified to `{}` as an empty string is falsey "PLW060", # Using global for `{name}` but no assignment is done -- DO NOT FIX "PLW2901", # PLW2901: Redefined loop variable -- FIX ME diff --git a/scripts/validate_filenames.py b/scripts/validate_filenames.py index ed23f3907114..0890024dd349 100755 --- a/scripts/validate_filenames.py +++ b/scripts/validate_filenames.py @@ -4,7 +4,7 @@ try: from .build_directory_md import good_file_paths except ImportError: - from build_directory_md import good_file_paths # type: ignore + from build_directory_md import good_file_paths # type: ignore[no-redef] filepaths = list(good_file_paths()) assert filepaths, "good_file_paths() failed!" diff --git a/scripts/validate_solutions.py b/scripts/validate_solutions.py index 0afbdde315c7..68dcd68b3947 100755 --- a/scripts/validate_solutions.py +++ b/scripts/validate_solutions.py @@ -21,8 +21,8 @@ def convert_path_to_module(file_path: pathlib.Path) -> ModuleType: """Converts a file path to a Python module""" spec = importlib.util.spec_from_file_location(file_path.name, str(file_path)) - module = importlib.util.module_from_spec(spec) # type: ignore - spec.loader.exec_module(module) # type: ignore + module = importlib.util.module_from_spec(spec) # type: ignore[arg-type] + spec.loader.exec_module(module) # type: ignore[union-attr] return module @@ -92,7 +92,7 @@ def test_project_euler(solution_path: pathlib.Path) -> None: problem_number: str = solution_path.parent.name[8:].zfill(3) expected: str = PROBLEM_ANSWERS[problem_number] solution_module = convert_path_to_module(solution_path) - answer = str(solution_module.solution()) # type: ignore + answer = str(solution_module.solution()) answer = hashlib.sha256(answer.encode()).hexdigest() assert ( answer == expected diff --git a/web_programming/covid_stats_via_xpath.py b/web_programming/covid_stats_via_xpath.py index a95130badad9..7011a02bffa8 100644 --- a/web_programming/covid_stats_via_xpath.py +++ b/web_programming/covid_stats_via_xpath.py @@ -7,7 +7,7 @@ from typing import NamedTuple import requests -from lxml import html # type: ignore +from lxml import html class CovidData(NamedTuple): From cc2f5b13088b8a98181983b5589f48749016d4ce Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Mon, 8 Apr 2024 14:22:54 +0300 Subject: [PATCH 598/757] Do not fix ruff EXE001 rule (#11350) * Do not fix ruff EXE001 rule * Fix --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 7eac811395ae..264f06d1f750 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ lint.ignore = [ # `ruff rule S101` for a description of that rule "B905", # `zip()` without an explicit `strict=` parameter -- FIX ME "E741", # Ambiguous variable name 'l' -- FIX ME "EM101", # Exception must not use a string literal, assign to variable first - "EXE001", # Shebang is present but file is not executable" -- FIX ME + "EXE001", # Shebang is present but file is not executable -- DO NOT FIX "G004", # Logging statement uses f-string "PLC1901", # `{}` can be simplified to `{}` as an empty string is falsey "PLW060", # Using global for `{name}` but no assignment is done -- DO NOT FIX From 9e55c9d9845c07ce6390ab92a2d86be4816d4a69 Mon Sep 17 00:00:00 2001 From: Jiayou Qin <90779499+Jiayoqin@users.noreply.github.com> Date: Mon, 8 Apr 2024 07:35:22 -0400 Subject: [PATCH 599/757] Added documentations (#11352) * Added documentations * Update data_structures/queue/circular_queue.py --------- Co-authored-by: Christian Clauss --- data_structures/queue/circular_queue.py | 7 +++++-- data_structures/queue/circular_queue_linked_list.py | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/data_structures/queue/circular_queue.py b/data_structures/queue/circular_queue.py index 93a6ef805c7c..f2fb4c01e467 100644 --- a/data_structures/queue/circular_queue.py +++ b/data_structures/queue/circular_queue.py @@ -25,6 +25,7 @@ def __len__(self) -> int: def is_empty(self) -> bool: """ + Checks whether the queue is empty or not >>> cq = CircularQueue(5) >>> cq.is_empty() True @@ -35,6 +36,7 @@ def is_empty(self) -> bool: def first(self): """ + Returns the first element of the queue >>> cq = CircularQueue(5) >>> cq.first() False @@ -45,7 +47,8 @@ def first(self): def enqueue(self, data): """ - This function insert an element in the queue using self.rear value as an index + This function inserts an element at the end of the queue using self.rear value + as an index. >>> cq = CircularQueue(5) >>> cq.enqueue("A") # doctest: +ELLIPSIS >> cq = CircularQueue(5) >>> cq.dequeue() Traceback (most recent call last): diff --git a/data_structures/queue/circular_queue_linked_list.py b/data_structures/queue/circular_queue_linked_list.py index 62042c4bce96..da8629678e52 100644 --- a/data_structures/queue/circular_queue_linked_list.py +++ b/data_structures/queue/circular_queue_linked_list.py @@ -39,7 +39,7 @@ def create_linked_list(self, initial_capacity: int) -> None: def is_empty(self) -> bool: """ - Checks where the queue is empty or not + Checks whether the queue is empty or not >>> cq = CircularQueueLinkedList() >>> cq.is_empty() True From 14ca726951473dd1993b6b13993105ea3b077ac3 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 9 Apr 2024 07:23:51 +0200 Subject: [PATCH 600/757] [pre-commit.ci] pre-commit autoupdate (#11355) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/pre-commit-hooks: v4.5.0 → v4.6.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.5.0...v4.6.0) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e6b1b0442c04..d4b8d1136ed7 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.5.0 + rev: v4.6.0 hooks: - id: check-executables-have-shebangs - id: check-toml From 0a9a860eb1174a513b231db2cf1a3378ff7c5b33 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 15 Apr 2024 22:21:33 +0200 Subject: [PATCH 601/757] [pre-commit.ci] pre-commit autoupdate (#11364) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/MarcoGorelli/auto-walrus: v0.2.2 → 0.3.3](https://github.com/MarcoGorelli/auto-walrus/compare/v0.2.2...0.3.3) - [github.com/astral-sh/ruff-pre-commit: v0.3.5 → v0.3.7](https://github.com/astral-sh/ruff-pre-commit/compare/v0.3.5...v0.3.7) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d4b8d1136ed7..9472bcfa3e07 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,12 +11,12 @@ repos: - id: requirements-txt-fixer - repo: https://github.com/MarcoGorelli/auto-walrus - rev: v0.2.2 + rev: 0.3.3 hooks: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.3.5 + rev: v0.3.7 hooks: - id: ruff - id: ruff-format From a42eb357027328085f928a4ab6c7aa770aeb1d6b Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Fri, 19 Apr 2024 22:30:22 +0300 Subject: [PATCH 602/757] Enable ruff E741 rule (#11370) * Enable ruff E741 rule * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .../binary_tree/non_recursive_segment_tree.py | 22 ++++++------ data_structures/binary_tree/segment_tree.py | 36 +++++++++---------- data_structures/heap/min_heap.py | 12 +++---- .../longest_common_subsequence.py | 10 +++--- .../longest_increasing_subsequence_o_nlogn.py | 14 ++++---- graphs/articulation_points.py | 10 +++--- graphs/dinic.py | 2 +- .../sequential_minimum_optimization.py | 4 +-- maths/pi_generator.py | 10 +++--- other/sdes.py | 10 +++--- project_euler/problem_011/sol2.py | 22 ++++++++---- pyproject.toml | 1 - strings/jaro_winkler.py | 8 ++--- strings/manacher.py | 33 ++++++++--------- 14 files changed, 102 insertions(+), 92 deletions(-) diff --git a/data_structures/binary_tree/non_recursive_segment_tree.py b/data_structures/binary_tree/non_recursive_segment_tree.py index 45c476701d79..ca0d5c111c4f 100644 --- a/data_structures/binary_tree/non_recursive_segment_tree.py +++ b/data_structures/binary_tree/non_recursive_segment_tree.py @@ -87,12 +87,12 @@ def update(self, p: int, v: T) -> None: p = p // 2 self.st[p] = self.fn(self.st[p * 2], self.st[p * 2 + 1]) - def query(self, l: int, r: int) -> T | None: + def query(self, left: int, right: int) -> T | None: """ Get range query value in log(N) time - :param l: left element index - :param r: right element index - :return: element combined in the range [l, r] + :param left: left element index + :param right: right element index + :return: element combined in the range [left, right] >>> st = SegmentTree([1, 2, 3, 4], lambda a, b: a + b) >>> st.query(0, 2) @@ -104,15 +104,15 @@ def query(self, l: int, r: int) -> T | None: >>> st.query(2, 3) 7 """ - l, r = l + self.N, r + self.N + left, right = left + self.N, right + self.N res: T | None = None - while l <= r: - if l % 2 == 1: - res = self.st[l] if res is None else self.fn(res, self.st[l]) - if r % 2 == 0: - res = self.st[r] if res is None else self.fn(res, self.st[r]) - l, r = (l + 1) // 2, (r - 1) // 2 + while left <= right: + if left % 2 == 1: + res = self.st[left] if res is None else self.fn(res, self.st[left]) + if right % 2 == 0: + res = self.st[right] if res is None else self.fn(res, self.st[right]) + left, right = (left + 1) // 2, (right - 1) // 2 return res diff --git a/data_structures/binary_tree/segment_tree.py b/data_structures/binary_tree/segment_tree.py index bb9c1ae2268b..c7069b3f6069 100644 --- a/data_structures/binary_tree/segment_tree.py +++ b/data_structures/binary_tree/segment_tree.py @@ -35,13 +35,13 @@ def right(self, idx): """ return idx * 2 + 1 - def build(self, idx, l, r): - if l == r: - self.st[idx] = self.A[l] + def build(self, idx, left, right): + if left == right: + self.st[idx] = self.A[left] else: - mid = (l + r) // 2 - self.build(self.left(idx), l, mid) - self.build(self.right(idx), mid + 1, r) + mid = (left + right) // 2 + self.build(self.left(idx), left, mid) + self.build(self.right(idx), mid + 1, right) self.st[idx] = max(self.st[self.left(idx)], self.st[self.right(idx)]) def update(self, a, b, val): @@ -56,18 +56,18 @@ def update(self, a, b, val): """ return self.update_recursive(1, 0, self.N - 1, a - 1, b - 1, val) - def update_recursive(self, idx, l, r, a, b, val): + def update_recursive(self, idx, left, right, a, b, val): """ update(1, 1, N, a, b, v) for update val v to [a,b] """ - if r < a or l > b: + if right < a or left > b: return True - if l == r: + if left == right: self.st[idx] = val return True - mid = (l + r) // 2 - self.update_recursive(self.left(idx), l, mid, a, b, val) - self.update_recursive(self.right(idx), mid + 1, r, a, b, val) + mid = (left + right) // 2 + self.update_recursive(self.left(idx), left, mid, a, b, val) + self.update_recursive(self.right(idx), mid + 1, right, a, b, val) self.st[idx] = max(self.st[self.left(idx)], self.st[self.right(idx)]) return True @@ -83,17 +83,17 @@ def query(self, a, b): """ return self.query_recursive(1, 0, self.N - 1, a - 1, b - 1) - def query_recursive(self, idx, l, r, a, b): + def query_recursive(self, idx, left, right, a, b): """ query(1, 1, N, a, b) for query max of [a,b] """ - if r < a or l > b: + if right < a or left > b: return -math.inf - if l >= a and r <= b: + if left >= a and right <= b: return self.st[idx] - mid = (l + r) // 2 - q1 = self.query_recursive(self.left(idx), l, mid, a, b) - q2 = self.query_recursive(self.right(idx), mid + 1, r, a, b) + mid = (left + right) // 2 + q1 = self.query_recursive(self.left(idx), left, mid, a, b) + q2 = self.query_recursive(self.right(idx), mid + 1, right, a, b) return max(q1, q2) def show_data(self): diff --git a/data_structures/heap/min_heap.py b/data_structures/heap/min_heap.py index 39f6d99e8a4c..ce7ed570a58d 100644 --- a/data_structures/heap/min_heap.py +++ b/data_structures/heap/min_heap.py @@ -66,14 +66,14 @@ def build_heap(self, array): # this is min-heapify method def sift_down(self, idx, array): while True: - l = self.get_left_child_idx(idx) - r = self.get_right_child_idx(idx) + left = self.get_left_child_idx(idx) + right = self.get_right_child_idx(idx) smallest = idx - if l < len(array) and array[l] < array[idx]: - smallest = l - if r < len(array) and array[r] < array[smallest]: - smallest = r + if left < len(array) and array[left] < array[idx]: + smallest = left + if right < len(array) and array[right] < array[smallest]: + smallest = right if smallest != idx: array[idx], array[smallest] = array[smallest], array[idx] diff --git a/dynamic_programming/longest_common_subsequence.py b/dynamic_programming/longest_common_subsequence.py index 22f50a166ae4..9a98b1736ed5 100644 --- a/dynamic_programming/longest_common_subsequence.py +++ b/dynamic_programming/longest_common_subsequence.py @@ -38,30 +38,30 @@ def longest_common_subsequence(x: str, y: str): n = len(y) # declaring the array for storing the dp values - l = [[0] * (n + 1) for _ in range(m + 1)] + dp = [[0] * (n + 1) for _ in range(m + 1)] for i in range(1, m + 1): for j in range(1, n + 1): match = 1 if x[i - 1] == y[j - 1] else 0 - l[i][j] = max(l[i - 1][j], l[i][j - 1], l[i - 1][j - 1] + match) + dp[i][j] = max(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1] + match) seq = "" i, j = m, n while i > 0 and j > 0: match = 1 if x[i - 1] == y[j - 1] else 0 - if l[i][j] == l[i - 1][j - 1] + match: + if dp[i][j] == dp[i - 1][j - 1] + match: if match == 1: seq = x[i - 1] + seq i -= 1 j -= 1 - elif l[i][j] == l[i - 1][j]: + elif dp[i][j] == dp[i - 1][j]: i -= 1 else: j -= 1 - return l[m][n], seq + return dp[m][n], seq if __name__ == "__main__": diff --git a/dynamic_programming/longest_increasing_subsequence_o_nlogn.py b/dynamic_programming/longest_increasing_subsequence_o_nlogn.py index 44e333e97779..bbc7a62b6b5c 100644 --- a/dynamic_programming/longest_increasing_subsequence_o_nlogn.py +++ b/dynamic_programming/longest_increasing_subsequence_o_nlogn.py @@ -7,14 +7,14 @@ from __future__ import annotations -def ceil_index(v, l, r, key): - while r - l > 1: - m = (l + r) // 2 - if v[m] >= key: - r = m +def ceil_index(v, left, right, key): + while right - left > 1: + middle = (left + right) // 2 + if v[middle] >= key: + right = middle else: - l = m - return r + left = middle + return right def longest_increasing_subsequence_length(v: list[int]) -> int: diff --git a/graphs/articulation_points.py b/graphs/articulation_points.py index 3fcaffd73725..0bf16e55bc04 100644 --- a/graphs/articulation_points.py +++ b/graphs/articulation_points.py @@ -1,6 +1,6 @@ # Finding Articulation Points in Undirected Graph -def compute_ap(l): - n = len(l) +def compute_ap(graph): + n = len(graph) out_edge_count = 0 low = [0] * n visited = [False] * n @@ -12,7 +12,7 @@ def dfs(root, at, parent, out_edge_count): visited[at] = True low[at] = at - for to in l[at]: + for to in graph[at]: if to == parent: pass elif not visited[to]: @@ -41,7 +41,7 @@ def dfs(root, at, parent, out_edge_count): # Adjacency list of graph -data = { +graph = { 0: [1, 2], 1: [0, 2], 2: [0, 1, 3, 5], @@ -52,4 +52,4 @@ def dfs(root, at, parent, out_edge_count): 7: [6, 8], 8: [5, 7], } -compute_ap(data) +compute_ap(graph) diff --git a/graphs/dinic.py b/graphs/dinic.py index 4f5e81236984..7919e6bc060a 100644 --- a/graphs/dinic.py +++ b/graphs/dinic.py @@ -37,7 +37,7 @@ def depth_first_search(self, vertex, sink, flow): # Here we calculate the flow that reaches the sink def max_flow(self, source, sink): flow, self.q[0] = 0, source - for l in range(31): # l = 30 maybe faster for random data + for l in range(31): # l = 30 maybe faster for random data # noqa: E741 while True: self.lvl, self.ptr = [0] * len(self.q), [0] * len(self.q) qi, qe, self.lvl[source] = 0, 1, 1 diff --git a/machine_learning/sequential_minimum_optimization.py b/machine_learning/sequential_minimum_optimization.py index 408d59ab5d29..3abdd6ccbed8 100644 --- a/machine_learning/sequential_minimum_optimization.py +++ b/machine_learning/sequential_minimum_optimization.py @@ -309,9 +309,9 @@ def _get_new_alpha(self, i1, i2, a1, a2, e1, e2, y1, y2): # calculate L and H which bound the new alpha2 s = y1 * y2 if s == -1: - l, h = max(0.0, a2 - a1), min(self._c, self._c + a2 - a1) + l, h = max(0.0, a2 - a1), min(self._c, self._c + a2 - a1) # noqa: E741 else: - l, h = max(0.0, a2 + a1 - self._c), min(self._c, a2 + a1) + l, h = max(0.0, a2 + a1 - self._c), min(self._c, a2 + a1) # noqa: E741 if l == h: return None, None diff --git a/maths/pi_generator.py b/maths/pi_generator.py index addd921747ba..97f2c540c1ce 100644 --- a/maths/pi_generator.py +++ b/maths/pi_generator.py @@ -41,7 +41,7 @@ def calculate_pi(limit: int) -> str: t = 1 k = 1 n = 3 - l = 3 + m = 3 decimal = limit counter = 0 @@ -65,11 +65,11 @@ def calculate_pi(limit: int) -> str: q *= 10 r = nr else: - nr = (2 * q + r) * l - nn = (q * (7 * k) + 2 + (r * l)) // (t * l) + nr = (2 * q + r) * m + nn = (q * (7 * k) + 2 + (r * m)) // (t * m) q *= k - t *= l - l += 2 + t *= m + m += 2 k += 1 n = nn r = nr diff --git a/other/sdes.py b/other/sdes.py index a69add3430c3..42186f453a3d 100644 --- a/other/sdes.py +++ b/other/sdes.py @@ -44,11 +44,11 @@ def function(expansion, s0, s1, key, message): right = message[4:] temp = apply_table(right, expansion) temp = xor(temp, key) - l = apply_sbox(s0, temp[:4]) - r = apply_sbox(s1, temp[4:]) - l = "0" * (2 - len(l)) + l - r = "0" * (2 - len(r)) + r - temp = apply_table(l + r, p4_table) + left_bin_str = apply_sbox(s0, temp[:4]) + right_bin_str = apply_sbox(s1, temp[4:]) + left_bin_str = "0" * (2 - len(left_bin_str)) + left_bin_str + right_bin_str = "0" * (2 - len(right_bin_str)) + right_bin_str + temp = apply_table(left_bin_str + right_bin_str, p4_table) temp = xor(left, temp) return temp + right diff --git a/project_euler/problem_011/sol2.py b/project_euler/problem_011/sol2.py index 2958305331a9..09bf315702c5 100644 --- a/project_euler/problem_011/sol2.py +++ b/project_euler/problem_011/sol2.py @@ -35,37 +35,47 @@ def solution(): 70600674 """ with open(os.path.dirname(__file__) + "/grid.txt") as f: - l = [] + grid = [] for _ in range(20): - l.append([int(x) for x in f.readline().split()]) + grid.append([int(x) for x in f.readline().split()]) maximum = 0 # right for i in range(20): for j in range(17): - temp = l[i][j] * l[i][j + 1] * l[i][j + 2] * l[i][j + 3] + temp = grid[i][j] * grid[i][j + 1] * grid[i][j + 2] * grid[i][j + 3] if temp > maximum: maximum = temp # down for i in range(17): for j in range(20): - temp = l[i][j] * l[i + 1][j] * l[i + 2][j] * l[i + 3][j] + temp = grid[i][j] * grid[i + 1][j] * grid[i + 2][j] * grid[i + 3][j] if temp > maximum: maximum = temp # diagonal 1 for i in range(17): for j in range(17): - temp = l[i][j] * l[i + 1][j + 1] * l[i + 2][j + 2] * l[i + 3][j + 3] + temp = ( + grid[i][j] + * grid[i + 1][j + 1] + * grid[i + 2][j + 2] + * grid[i + 3][j + 3] + ) if temp > maximum: maximum = temp # diagonal 2 for i in range(17): for j in range(3, 20): - temp = l[i][j] * l[i + 1][j - 1] * l[i + 2][j - 2] * l[i + 3][j - 3] + temp = ( + grid[i][j] + * grid[i + 1][j - 1] + * grid[i + 2][j - 2] + * grid[i + 3][j - 3] + ) if temp > maximum: maximum = temp return maximum diff --git a/pyproject.toml b/pyproject.toml index 264f06d1f750..1ac70b2fab93 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,7 +2,6 @@ lint.ignore = [ # `ruff rule S101` for a description of that rule "B904", # Within an `except` clause, raise exceptions with `raise ... from err` -- FIX ME "B905", # `zip()` without an explicit `strict=` parameter -- FIX ME - "E741", # Ambiguous variable name 'l' -- FIX ME "EM101", # Exception must not use a string literal, assign to variable first "EXE001", # Shebang is present but file is not executable -- DO NOT FIX "G004", # Logging statement uses f-string diff --git a/strings/jaro_winkler.py b/strings/jaro_winkler.py index f4a8fbad3ac8..c18f0d85d9f4 100644 --- a/strings/jaro_winkler.py +++ b/strings/jaro_winkler.py @@ -28,12 +28,12 @@ def jaro_winkler(str1: str, str2: str) -> float: def get_matched_characters(_str1: str, _str2: str) -> str: matched = [] limit = min(len(_str1), len(_str2)) // 2 - for i, l in enumerate(_str1): + for i, char in enumerate(_str1): left = int(max(0, i - limit)) right = int(min(i + limit + 1, len(_str2))) - if l in _str2[left:right]: - matched.append(l) - _str2 = f"{_str2[0:_str2.index(l)]} {_str2[_str2.index(l) + 1:]}" + if char in _str2[left:right]: + matched.append(char) + _str2 = f"{_str2[0:_str2.index(char)]} {_str2[_str2.index(char) + 1:]}" return "".join(matched) diff --git a/strings/manacher.py b/strings/manacher.py index ca546e533acd..fc8b01cd9c1c 100644 --- a/strings/manacher.py +++ b/strings/manacher.py @@ -9,9 +9,9 @@ def palindromic_string(input_string: str) -> str: 1. first this convert input_string("xyx") into new_string("x|y|x") where odd positions are actual input characters. - 2. for each character in new_string it find corresponding length and store the - length and l,r to store previously calculated info.(please look the explanation - for details) + 2. for each character in new_string it find corresponding length and + store the length and left,right to store previously calculated info. + (please look the explanation for details) 3. return corresponding output_string by removing all "|" """ @@ -29,7 +29,7 @@ def palindromic_string(input_string: str) -> str: # we will store the starting and ending of previous furthest ending palindromic # substring - l, r = 0, 0 + left, right = 0, 0 # length[i] shows the length of palindromic substring with center i length = [1 for i in range(len(new_input_string))] @@ -37,7 +37,7 @@ def palindromic_string(input_string: str) -> str: # for each character in new_string find corresponding palindromic string start = 0 for j in range(len(new_input_string)): - k = 1 if j > r else min(length[l + r - j] // 2, r - j + 1) + k = 1 if j > right else min(length[left + right - j] // 2, right - j + 1) while ( j - k >= 0 and j + k < len(new_input_string) @@ -47,11 +47,11 @@ def palindromic_string(input_string: str) -> str: length[j] = 2 * k - 1 - # does this string is ending after the previously explored end (that is r) ? - # if yes the update the new r to the last index of this - if j + k - 1 > r: - l = j - k + 1 - r = j + k - 1 + # does this string is ending after the previously explored end (that is right) ? + # if yes the update the new right to the last index of this + if j + k - 1 > right: + left = j - k + 1 + right = j + k - 1 # update max_length and start position if max_length < length[j]: @@ -78,8 +78,9 @@ def palindromic_string(input_string: str) -> str: consider the string for which we are calculating the longest palindromic substring is shown above where ... are some characters in between and right now we are calculating the length of palindromic substring with center at a5 with following conditions : -i) we have stored the length of palindromic substring which has center at a3 (starts at - l ends at r) and it is the furthest ending till now, and it has ending after a6 +i) we have stored the length of palindromic substring which has center at a3 + (starts at left ends at right) and it is the furthest ending till now, + and it has ending after a6 ii) a2 and a4 are equally distant from a3 so char(a2) == char(a4) iii) a0 and a6 are equally distant from a3 so char(a0) == char(a6) iv) a1 is corresponding equal character of a5 in palindrome with center a3 (remember @@ -98,11 +99,11 @@ def palindromic_string(input_string: str) -> str: a1 but this only holds if a0 and a6 are inside the limits of palindrome centered at a3 so finally .. -len_of_palindrome__at(a5) = min(len_of_palindrome_at(a1), r-a5) -where a3 lies from l to r and we have to keep updating that +len_of_palindrome__at(a5) = min(len_of_palindrome_at(a1), right-a5) +where a3 lies from left to right and we have to keep updating that -and if the a5 lies outside of l,r boundary we calculate length of palindrome with -bruteforce and update l,r. +and if the a5 lies outside of left,right boundary we calculate length of palindrome with +bruteforce and update left,right. it gives the linear time complexity just like z-function """ From 42593489d974feff169cf4f3455e3f209d7bdfcf Mon Sep 17 00:00:00 2001 From: Kelvin Date: Sat, 20 Apr 2024 16:20:37 +0530 Subject: [PATCH 603/757] Add doctests in all functions in basic_string.py (#11374) * Add doctests in all functions in basic_string.py * Revert back to original basic_string.py * Add doctest in basic_string.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update genetic_algorithm/basic_string.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- genetic_algorithm/basic_string.py | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/genetic_algorithm/basic_string.py b/genetic_algorithm/basic_string.py index 089c5c99a1ec..a906ce85a779 100644 --- a/genetic_algorithm/basic_string.py +++ b/genetic_algorithm/basic_string.py @@ -33,7 +33,12 @@ def evaluate(item: str, main_target: str) -> tuple[str, float]: def crossover(parent_1: str, parent_2: str) -> tuple[str, str]: - """Slice and combine two string at a random point.""" + """ + Slice and combine two strings at a random point. + >>> random.seed(42) + >>> crossover("123456", "abcdef") + ('12345f', 'abcde6') + """ random_slice = random.randint(0, len(parent_1) - 1) child_1 = parent_1[:random_slice] + parent_2[random_slice:] child_2 = parent_2[:random_slice] + parent_1[random_slice:] @@ -41,7 +46,12 @@ def crossover(parent_1: str, parent_2: str) -> tuple[str, str]: def mutate(child: str, genes: list[str]) -> str: - """Mutate a random gene of a child with another one from the list.""" + """ + Mutate a random gene of a child with another one from the list. + >>> random.seed(123) + >>> mutate("123456", list("ABCDEF")) + '12345A' + """ child_list = list(child) if random.uniform(0, 1) < MUTATION_PROBABILITY: child_list[random.randint(0, len(child)) - 1] = random.choice(genes) @@ -54,7 +64,22 @@ def select( population_score: list[tuple[str, float]], genes: list[str], ) -> list[str]: - """Select the second parent and generate new population""" + """ + Select the second parent and generate new population + + >>> random.seed(42) + >>> parent_1 = ("123456", 8.0) + >>> population_score = [("abcdef", 4.0), ("ghijkl", 5.0), ("mnopqr", 7.0)] + >>> genes = list("ABCDEF") + >>> child_n = int(min(parent_1[1] + 1, 10)) + >>> population = [] + >>> for _ in range(child_n): + ... parent_2 = population_score[random.randrange(len(population_score))][0] + ... child_1, child_2 = crossover(parent_1[0], parent_2) + ... population.extend((mutate(child_1, genes), mutate(child_2, genes))) + >>> len(population) == (int(parent_1[1]) + 1) * 2 + True + """ pop = [] # Generate more children proportionally to the fitness score. child_n = int(parent_1[1] * 100) + 1 From 7b88e15b1cc67c784872b0d16189e516474cf5a5 Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Sat, 20 Apr 2024 17:20:27 +0300 Subject: [PATCH 604/757] Enable ruff RUF007 rule (#11349) * Enable ruff RUF005 rule * Enable ruff RUF007 rule * Fix * Fix * Fix * Update sorts/bead_sort.py Co-authored-by: Christian Clauss * Update sorts/bead_sort.py * Revert "Update sorts/bead_sort.py" This reverts commit b10e5632e4479c2117c8b67113b5aa6545f127aa. * Revert "Update sorts/bead_sort.py" This reverts commit 2c1816bf102eeec5aa39cb2f1806afb64b672d14. * Update sorts/bead_sort.py --------- Co-authored-by: Christian Clauss --- data_structures/linked_list/skip_list.py | 3 ++- pyproject.toml | 1 - sorts/bead_sort.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/data_structures/linked_list/skip_list.py b/data_structures/linked_list/skip_list.py index 88d3e0daddf0..13e9a94a8698 100644 --- a/data_structures/linked_list/skip_list.py +++ b/data_structures/linked_list/skip_list.py @@ -5,6 +5,7 @@ from __future__ import annotations +from itertools import pairwise from random import random from typing import Generic, TypeVar @@ -389,7 +390,7 @@ def traverse_keys(node): def test_iter_always_yields_sorted_values(): def is_sorted(lst): - return all(next_item >= item for item, next_item in zip(lst, lst[1:])) + return all(next_item >= item for item, next_item in pairwise(lst)) skip_list = SkipList() for i in range(10): diff --git a/pyproject.toml b/pyproject.toml index 1ac70b2fab93..e46293a8d526 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,7 +13,6 @@ lint.ignore = [ # `ruff rule S101` for a description of that rule "RUF001", # String contains ambiguous {}. Did you mean {}? "RUF002", # Docstring contains ambiguous {}. Did you mean {}? "RUF003", # Comment contains ambiguous {}. Did you mean {}? - "RUF007", # Prefer itertools.pairwise() over zip() when iterating over successive pairs "S101", # Use of `assert` detected -- DO NOT FIX "S113", # Probable use of requests call without timeout -- FIX ME "S311", # Standard pseudo-random generators are not suitable for cryptographic purposes -- FIX ME diff --git a/sorts/bead_sort.py b/sorts/bead_sort.py index e51173643d81..8ce0619fd573 100644 --- a/sorts/bead_sort.py +++ b/sorts/bead_sort.py @@ -31,7 +31,7 @@ def bead_sort(sequence: list) -> list: if any(not isinstance(x, int) or x < 0 for x in sequence): raise TypeError("Sequence must be list of non-negative integers") for _ in range(len(sequence)): - for i, (rod_upper, rod_lower) in enumerate(zip(sequence, sequence[1:])): + for i, (rod_upper, rod_lower) in enumerate(zip(sequence, sequence[1:])): # noqa: RUF007 if rod_upper > rod_lower: sequence[i] -= rod_upper - rod_lower sequence[i + 1] += rod_upper - rod_lower From 2702bf9400faece97a1ebc76d0f91b9cfe9658f6 Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Sun, 21 Apr 2024 20:34:18 +0300 Subject: [PATCH 605/757] Enable ruff S113 rule (#11375) * Enable ruff S113 rule * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- machine_learning/linear_regression.py | 3 ++- pyproject.toml | 1 - scripts/validate_solutions.py | 2 +- web_programming/co2_emission.py | 4 ++-- web_programming/covid_stats_via_xpath.py | 4 +++- web_programming/crawl_google_results.py | 2 +- web_programming/crawl_google_scholar_citation.py | 4 +++- web_programming/currency_converter.py | 2 +- web_programming/current_stock_price.py | 4 +++- web_programming/current_weather.py | 4 ++-- web_programming/daily_horoscope.py | 2 +- web_programming/download_images_from_google_query.py | 4 +++- web_programming/emails_from_url.py | 4 ++-- web_programming/fetch_anime_and_play.py | 8 +++++--- web_programming/fetch_bbc_news.py | 2 +- web_programming/fetch_github_info.py | 2 +- web_programming/fetch_jobs.py | 4 +++- web_programming/fetch_quotes.py | 4 ++-- web_programming/fetch_well_rx_price.py | 2 +- web_programming/get_amazon_product_data.py | 4 +++- web_programming/get_imdb_top_250_movies_csv.py | 2 +- web_programming/get_ip_geolocation.py | 2 +- web_programming/get_top_billionaires.py | 2 +- web_programming/get_top_hn_posts.py | 4 ++-- web_programming/giphy.py | 2 +- web_programming/instagram_crawler.py | 2 +- web_programming/instagram_pic.py | 4 ++-- web_programming/instagram_video.py | 4 ++-- web_programming/nasa_data.py | 6 +++--- web_programming/open_google_results.py | 1 + web_programming/random_anime_character.py | 6 ++++-- web_programming/recaptcha_verification.py | 4 +++- web_programming/reddit.py | 1 + web_programming/search_books_by_isbn.py | 2 +- web_programming/slack_message.py | 4 +++- web_programming/world_covid19_stats.py | 2 +- 36 files changed, 68 insertions(+), 46 deletions(-) diff --git a/machine_learning/linear_regression.py b/machine_learning/linear_regression.py index 39bee5712c16..839a5366d1cc 100644 --- a/machine_learning/linear_regression.py +++ b/machine_learning/linear_regression.py @@ -19,7 +19,8 @@ def collect_dataset(): """ response = requests.get( "https://raw.githubusercontent.com/yashLadha/The_Math_of_Intelligence/" - "master/Week1/ADRvsRating.csv" + "master/Week1/ADRvsRating.csv", + timeout=10, ) lines = response.text.splitlines() data = [] diff --git a/pyproject.toml b/pyproject.toml index e46293a8d526..ff22fba81c8a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,7 +14,6 @@ lint.ignore = [ # `ruff rule S101` for a description of that rule "RUF002", # Docstring contains ambiguous {}. Did you mean {}? "RUF003", # Comment contains ambiguous {}. Did you mean {}? "S101", # Use of `assert` detected -- DO NOT FIX - "S113", # Probable use of requests call without timeout -- FIX ME "S311", # Standard pseudo-random generators are not suitable for cryptographic purposes -- FIX ME "SLF001", # Private member accessed: `_Iterator` -- FIX ME "UP038", # Use `X | Y` in `{}` call instead of `(X, Y)` -- DO NOT FIX diff --git a/scripts/validate_solutions.py b/scripts/validate_solutions.py index 68dcd68b3947..325c245e0d77 100755 --- a/scripts/validate_solutions.py +++ b/scripts/validate_solutions.py @@ -57,7 +57,7 @@ def added_solution_file_path() -> list[pathlib.Path]: "Accept": "application/vnd.github.v3+json", "Authorization": "token " + os.environ["GITHUB_TOKEN"], } - files = requests.get(get_files_url(), headers=headers).json() + files = requests.get(get_files_url(), headers=headers, timeout=10).json() for file in files: filepath = pathlib.Path.cwd().joinpath(file["filename"]) if ( diff --git a/web_programming/co2_emission.py b/web_programming/co2_emission.py index 88a426cb976d..19af70489d1d 100644 --- a/web_programming/co2_emission.py +++ b/web_programming/co2_emission.py @@ -11,13 +11,13 @@ # Emission in the last half hour def fetch_last_half_hour() -> str: - last_half_hour = requests.get(BASE_URL).json()["data"][0] + last_half_hour = requests.get(BASE_URL, timeout=10).json()["data"][0] return last_half_hour["intensity"]["actual"] # Emissions in a specific date range def fetch_from_to(start, end) -> list: - return requests.get(f"{BASE_URL}/{start}/{end}").json()["data"] + return requests.get(f"{BASE_URL}/{start}/{end}", timeout=10).json()["data"] if __name__ == "__main__": diff --git a/web_programming/covid_stats_via_xpath.py b/web_programming/covid_stats_via_xpath.py index 7011a02bffa8..c27a5d12bb3f 100644 --- a/web_programming/covid_stats_via_xpath.py +++ b/web_programming/covid_stats_via_xpath.py @@ -18,7 +18,9 @@ class CovidData(NamedTuple): def covid_stats(url: str = "https://www.worldometers.info/coronavirus/") -> CovidData: xpath_str = '//div[@class = "maincounter-number"]/span/text()' - return CovidData(*html.fromstring(requests.get(url).content).xpath(xpath_str)) + return CovidData( + *html.fromstring(requests.get(url, timeout=10).content).xpath(xpath_str) + ) fmt = """Total COVID-19 cases in the world: {} diff --git a/web_programming/crawl_google_results.py b/web_programming/crawl_google_results.py index 1f5e6d31992b..cb75d450ff82 100644 --- a/web_programming/crawl_google_results.py +++ b/web_programming/crawl_google_results.py @@ -8,7 +8,7 @@ if __name__ == "__main__": print("Googling.....") url = "https://www.google.com/search?q=" + " ".join(sys.argv[1:]) - res = requests.get(url, headers={"UserAgent": UserAgent().random}) + res = requests.get(url, headers={"UserAgent": UserAgent().random}, timeout=10) # res.raise_for_status() with open("project1a.html", "wb") as out_file: # only for knowing the class for data in res.iter_content(10000): diff --git a/web_programming/crawl_google_scholar_citation.py b/web_programming/crawl_google_scholar_citation.py index f92a3d139520..5f2ccad5f414 100644 --- a/web_programming/crawl_google_scholar_citation.py +++ b/web_programming/crawl_google_scholar_citation.py @@ -11,7 +11,9 @@ def get_citation(base_url: str, params: dict) -> str: """ Return the citation number. """ - soup = BeautifulSoup(requests.get(base_url, params=params).content, "html.parser") + soup = BeautifulSoup( + requests.get(base_url, params=params, timeout=10).content, "html.parser" + ) div = soup.find("div", attrs={"class": "gs_ri"}) anchors = div.find("div", attrs={"class": "gs_fl"}).find_all("a") return anchors[2].get_text() diff --git a/web_programming/currency_converter.py b/web_programming/currency_converter.py index 3bbcafa8f89b..9623504b89ea 100644 --- a/web_programming/currency_converter.py +++ b/web_programming/currency_converter.py @@ -176,7 +176,7 @@ def convert_currency( params = locals() # from is a reserved keyword params["from"] = params.pop("from_") - res = requests.get(URL_BASE, params=params).json() + res = requests.get(URL_BASE, params=params, timeout=10).json() return str(res["amount"]) if res["error"] == 0 else res["error_message"] diff --git a/web_programming/current_stock_price.py b/web_programming/current_stock_price.py index 0c06354d8998..9567c05b0558 100644 --- a/web_programming/current_stock_price.py +++ b/web_programming/current_stock_price.py @@ -4,7 +4,9 @@ def stock_price(symbol: str = "AAPL") -> str: url = f"https://finance.yahoo.com/quote/{symbol}?p={symbol}" - yahoo_finance_source = requests.get(url, headers={"USER-AGENT": "Mozilla/5.0"}).text + yahoo_finance_source = requests.get( + url, headers={"USER-AGENT": "Mozilla/5.0"}, timeout=10 + ).text soup = BeautifulSoup(yahoo_finance_source, "html.parser") specific_fin_streamer_tag = soup.find("fin-streamer", {"data-test": "qsp-price"}) diff --git a/web_programming/current_weather.py b/web_programming/current_weather.py index 3b6cd177cdfb..4a8fa5e3c845 100644 --- a/web_programming/current_weather.py +++ b/web_programming/current_weather.py @@ -20,13 +20,13 @@ def current_weather(location: str) -> list[dict]: if OPENWEATHERMAP_API_KEY: params_openweathermap = {"q": location, "appid": OPENWEATHERMAP_API_KEY} response_openweathermap = requests.get( - OPENWEATHERMAP_URL_BASE, params=params_openweathermap + OPENWEATHERMAP_URL_BASE, params=params_openweathermap, timeout=10 ) weather_data.append({"OpenWeatherMap": response_openweathermap.json()}) if WEATHERSTACK_API_KEY: params_weatherstack = {"query": location, "access_key": WEATHERSTACK_API_KEY} response_weatherstack = requests.get( - WEATHERSTACK_URL_BASE, params=params_weatherstack + WEATHERSTACK_URL_BASE, params=params_weatherstack, timeout=10 ) weather_data.append({"Weatherstack": response_weatherstack.json()}) if not weather_data: diff --git a/web_programming/daily_horoscope.py b/web_programming/daily_horoscope.py index b0dd1cd65924..75e637d8e52c 100644 --- a/web_programming/daily_horoscope.py +++ b/web_programming/daily_horoscope.py @@ -7,7 +7,7 @@ def horoscope(zodiac_sign: int, day: str) -> str: "https://www.horoscope.com/us/horoscopes/general/" f"horoscope-general-daily-{day}.aspx?sign={zodiac_sign}" ) - soup = BeautifulSoup(requests.get(url).content, "html.parser") + soup = BeautifulSoup(requests.get(url, timeout=10).content, "html.parser") return soup.find("div", class_="main-horoscope").p.text diff --git a/web_programming/download_images_from_google_query.py b/web_programming/download_images_from_google_query.py index 441347459f8e..235cd35763ef 100644 --- a/web_programming/download_images_from_google_query.py +++ b/web_programming/download_images_from_google_query.py @@ -39,7 +39,9 @@ def download_images_from_google_query(query: str = "dhaka", max_images: int = 5) "ijn": "0", } - html = requests.get("https://www.google.com/search", params=params, headers=headers) + html = requests.get( + "https://www.google.com/search", params=params, headers=headers, timeout=10 + ) soup = BeautifulSoup(html.text, "html.parser") matched_images_data = "".join( re.findall(r"AF_initDataCallback\(([^<]+)\);", str(soup.select("script"))) diff --git a/web_programming/emails_from_url.py b/web_programming/emails_from_url.py index 26c88e1b13a5..43fd78dcf5a4 100644 --- a/web_programming/emails_from_url.py +++ b/web_programming/emails_from_url.py @@ -77,7 +77,7 @@ def emails_from_url(url: str = "https://github.com") -> list[str]: try: # Open URL - r = requests.get(url) + r = requests.get(url, timeout=10) # pass the raw HTML to the parser to get links parser.feed(r.text) @@ -88,7 +88,7 @@ def emails_from_url(url: str = "https://github.com") -> list[str]: # open URL. # read = requests.get(link) try: - read = requests.get(link) + read = requests.get(link, timeout=10) # Get the valid email. emails = re.findall("[a-zA-Z0-9]+@" + domain, read.text) # If not in list then append it. diff --git a/web_programming/fetch_anime_and_play.py b/web_programming/fetch_anime_and_play.py index 366807785e85..fd7c3a3a7381 100644 --- a/web_programming/fetch_anime_and_play.py +++ b/web_programming/fetch_anime_and_play.py @@ -28,7 +28,7 @@ def search_scraper(anime_name: str) -> list: search_url = f"{BASE_URL}/search/{anime_name}" response = requests.get( - search_url, headers={"UserAgent": UserAgent().chrome} + search_url, headers={"UserAgent": UserAgent().chrome}, timeout=10 ) # request the url. # Is the response ok? @@ -82,7 +82,9 @@ def search_anime_episode_list(episode_endpoint: str) -> list: request_url = f"{BASE_URL}{episode_endpoint}" - response = requests.get(url=request_url, headers={"UserAgent": UserAgent().chrome}) + response = requests.get( + url=request_url, headers={"UserAgent": UserAgent().chrome}, timeout=10 + ) response.raise_for_status() soup = BeautifulSoup(response.text, "html.parser") @@ -132,7 +134,7 @@ def get_anime_episode(episode_endpoint: str) -> list: episode_page_url = f"{BASE_URL}{episode_endpoint}" response = requests.get( - url=episode_page_url, headers={"User-Agent": UserAgent().chrome} + url=episode_page_url, headers={"User-Agent": UserAgent().chrome}, timeout=10 ) response.raise_for_status() diff --git a/web_programming/fetch_bbc_news.py b/web_programming/fetch_bbc_news.py index 7f8bc57b69f5..e5cd864a9d83 100644 --- a/web_programming/fetch_bbc_news.py +++ b/web_programming/fetch_bbc_news.py @@ -7,7 +7,7 @@ def fetch_bbc_news(bbc_news_api_key: str) -> None: # fetching a list of articles in json format - bbc_news_page = requests.get(_NEWS_API + bbc_news_api_key).json() + bbc_news_page = requests.get(_NEWS_API + bbc_news_api_key, timeout=10).json() # each article in the list is a dict for i, article in enumerate(bbc_news_page["articles"], 1): print(f"{i}.) {article['title']}") diff --git a/web_programming/fetch_github_info.py b/web_programming/fetch_github_info.py index 7a4985b68841..25d44245bb58 100644 --- a/web_programming/fetch_github_info.py +++ b/web_programming/fetch_github_info.py @@ -42,7 +42,7 @@ def fetch_github_info(auth_token: str) -> dict[Any, Any]: "Authorization": f"token {auth_token}", "Accept": "application/vnd.github.v3+json", } - return requests.get(AUTHENTICATED_USER_ENDPOINT, headers=headers).json() + return requests.get(AUTHENTICATED_USER_ENDPOINT, headers=headers, timeout=10).json() if __name__ == "__main__": # pragma: no cover diff --git a/web_programming/fetch_jobs.py b/web_programming/fetch_jobs.py index 49abd3c88eec..0d89bf45de57 100644 --- a/web_programming/fetch_jobs.py +++ b/web_programming/fetch_jobs.py @@ -13,7 +13,9 @@ def fetch_jobs(location: str = "mumbai") -> Generator[tuple[str, str], None, None]: - soup = BeautifulSoup(requests.get(url + location).content, "html.parser") + soup = BeautifulSoup( + requests.get(url + location, timeout=10).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"}): job_title = job.find("a", attrs={"data-tn-element": "jobTitle"}).text.strip() diff --git a/web_programming/fetch_quotes.py b/web_programming/fetch_quotes.py index d557e2d95e74..cf0add43f002 100644 --- a/web_programming/fetch_quotes.py +++ b/web_programming/fetch_quotes.py @@ -14,11 +14,11 @@ def quote_of_the_day() -> list: - return requests.get(API_ENDPOINT_URL + "/today").json() + return requests.get(API_ENDPOINT_URL + "/today", timeout=10).json() def random_quotes() -> list: - return requests.get(API_ENDPOINT_URL + "/random").json() + return requests.get(API_ENDPOINT_URL + "/random", timeout=10).json() if __name__ == "__main__": diff --git a/web_programming/fetch_well_rx_price.py b/web_programming/fetch_well_rx_price.py index ee51b9a5051b..93be2a9235d9 100644 --- a/web_programming/fetch_well_rx_price.py +++ b/web_programming/fetch_well_rx_price.py @@ -42,7 +42,7 @@ def fetch_pharmacy_and_price_list(drug_name: str, zip_code: str) -> list | None: return None request_url = BASE_URL.format(drug_name, zip_code) - response = get(request_url) + response = get(request_url, timeout=10) # Is the response ok? response.raise_for_status() diff --git a/web_programming/get_amazon_product_data.py b/web_programming/get_amazon_product_data.py index c2f2ac5ab291..b98ff2c030af 100644 --- a/web_programming/get_amazon_product_data.py +++ b/web_programming/get_amazon_product_data.py @@ -24,7 +24,9 @@ def get_amazon_product_data(product: str = "laptop") -> DataFrame: ), "Accept-Language": "en-US, en;q=0.5", } - soup = BeautifulSoup(requests.get(url, headers=header).text, features="lxml") + soup = BeautifulSoup( + requests.get(url, headers=header, timeout=10).text, features="lxml" + ) # Initialize a Pandas dataframe with the column titles data_frame = DataFrame( columns=[ diff --git a/web_programming/get_imdb_top_250_movies_csv.py b/web_programming/get_imdb_top_250_movies_csv.py index e54b076ebd94..c914b29cb3b3 100644 --- a/web_programming/get_imdb_top_250_movies_csv.py +++ b/web_programming/get_imdb_top_250_movies_csv.py @@ -8,7 +8,7 @@ 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") + soup = BeautifulSoup(requests.get(url, timeout=10).text, "html.parser") titles = soup.find_all("td", attrs="titleColumn") ratings = soup.find_all("td", class_="ratingColumn imdbRating") return { diff --git a/web_programming/get_ip_geolocation.py b/web_programming/get_ip_geolocation.py index 62eaeafceb7e..574d287f0db1 100644 --- a/web_programming/get_ip_geolocation.py +++ b/web_programming/get_ip_geolocation.py @@ -8,7 +8,7 @@ def get_ip_geolocation(ip_address: str) -> str: url = f"https://ipinfo.io/{ip_address}/json" # Send a GET request to the API - response = requests.get(url) + response = requests.get(url, timeout=10) # Check if the HTTP request was successful response.raise_for_status() diff --git a/web_programming/get_top_billionaires.py b/web_programming/get_top_billionaires.py index 703b635eef82..24828b6d787c 100644 --- a/web_programming/get_top_billionaires.py +++ b/web_programming/get_top_billionaires.py @@ -57,7 +57,7 @@ def get_forbes_real_time_billionaires() -> list[dict[str, int | str]]: Returns: List of top 10 realtime billionaires data. """ - response_json = requests.get(API_URL).json() + response_json = requests.get(API_URL, timeout=10).json() return [ { "Name": person["personName"], diff --git a/web_programming/get_top_hn_posts.py b/web_programming/get_top_hn_posts.py index fbb7c051a88e..f5d4f874c6c6 100644 --- a/web_programming/get_top_hn_posts.py +++ b/web_programming/get_top_hn_posts.py @@ -5,7 +5,7 @@ def get_hackernews_story(story_id: str) -> dict: url = f"https://hacker-news.firebaseio.com/v0/item/{story_id}.json?print=pretty" - return requests.get(url).json() + return requests.get(url, timeout=10).json() def hackernews_top_stories(max_stories: int = 10) -> list[dict]: @@ -13,7 +13,7 @@ def hackernews_top_stories(max_stories: int = 10) -> list[dict]: Get the top max_stories posts from HackerNews - https://news.ycombinator.com/ """ url = "https://hacker-news.firebaseio.com/v0/topstories.json?print=pretty" - story_ids = requests.get(url).json()[:max_stories] + story_ids = requests.get(url, timeout=10).json()[:max_stories] return [get_hackernews_story(story_id) for story_id in story_ids] diff --git a/web_programming/giphy.py b/web_programming/giphy.py index a5c3f8f7493e..2bf3e3ea9c0b 100644 --- a/web_programming/giphy.py +++ b/web_programming/giphy.py @@ -11,7 +11,7 @@ def get_gifs(query: str, api_key: str = giphy_api_key) -> list: """ formatted_query = "+".join(query.split()) url = f"https://api.giphy.com/v1/gifs/search?q={formatted_query}&api_key={api_key}" - gifs = requests.get(url).json()["data"] + gifs = requests.get(url, timeout=10).json()["data"] return [gif["url"] for gif in gifs] diff --git a/web_programming/instagram_crawler.py b/web_programming/instagram_crawler.py index 0816cd181051..df62735fb328 100644 --- a/web_programming/instagram_crawler.py +++ b/web_programming/instagram_crawler.py @@ -39,7 +39,7 @@ def get_json(self) -> dict: """ Return a dict of user information """ - html = requests.get(self.url, headers=headers).text + html = requests.get(self.url, headers=headers, timeout=10).text scripts = BeautifulSoup(html, "html.parser").find_all("script") try: return extract_user_profile(scripts[4]) diff --git a/web_programming/instagram_pic.py b/web_programming/instagram_pic.py index 2d987c1766dc..292cacc16c04 100644 --- a/web_programming/instagram_pic.py +++ b/web_programming/instagram_pic.py @@ -15,7 +15,7 @@ def download_image(url: str) -> str: A message indicating the result of the operation. """ try: - response = requests.get(url) + response = requests.get(url, timeout=10) response.raise_for_status() except requests.exceptions.RequestException as e: return f"An error occurred during the HTTP request to {url}: {e!r}" @@ -30,7 +30,7 @@ def download_image(url: str) -> str: return f"Image URL not found in meta tag {image_meta_tag}." try: - image_data = requests.get(image_url).content + image_data = requests.get(image_url, timeout=10).content except requests.exceptions.RequestException as e: return f"An error occurred during the HTTP request to {image_url}: {e!r}" if not image_data: diff --git a/web_programming/instagram_video.py b/web_programming/instagram_video.py index 1f1b0e297034..a4cddce25138 100644 --- a/web_programming/instagram_video.py +++ b/web_programming/instagram_video.py @@ -5,8 +5,8 @@ def download_video(url: str) -> bytes: base_url = "https://downloadgram.net/wp-json/wppress/video-downloader/video?url=" - video_url = requests.get(base_url + url).json()[0]["urls"][0]["src"] - return requests.get(video_url).content + video_url = requests.get(base_url + url, timeout=10).json()[0]["urls"][0]["src"] + return requests.get(video_url, timeout=10).content if __name__ == "__main__": diff --git a/web_programming/nasa_data.py b/web_programming/nasa_data.py index 81125e0a4f05..33a6406c52a6 100644 --- a/web_programming/nasa_data.py +++ b/web_programming/nasa_data.py @@ -9,14 +9,14 @@ def get_apod_data(api_key: str) -> dict: Get your API Key from: https://api.nasa.gov/ """ url = "https://api.nasa.gov/planetary/apod" - return requests.get(url, params={"api_key": api_key}).json() + return requests.get(url, params={"api_key": api_key}, timeout=10).json() def save_apod(api_key: str, path: str = ".") -> dict: apod_data = get_apod_data(api_key) img_url = apod_data["url"] img_name = img_url.split("/")[-1] - response = requests.get(img_url, stream=True) + response = requests.get(img_url, stream=True, timeout=10) with open(f"{path}/{img_name}", "wb+") as img_file: shutil.copyfileobj(response.raw, img_file) @@ -29,7 +29,7 @@ def get_archive_data(query: str) -> dict: Get the data of a particular query from NASA archives """ url = "https://images-api.nasa.gov/search" - return requests.get(url, params={"q": query}).json() + return requests.get(url, params={"q": query}, timeout=10).json() if __name__ == "__main__": diff --git a/web_programming/open_google_results.py b/web_programming/open_google_results.py index f61e3666dd7e..52dd37d7b91a 100644 --- a/web_programming/open_google_results.py +++ b/web_programming/open_google_results.py @@ -16,6 +16,7 @@ res = requests.get( url, headers={"User-Agent": str(UserAgent().random)}, + timeout=10, ) try: diff --git a/web_programming/random_anime_character.py b/web_programming/random_anime_character.py index f15a9c05d9e5..aed932866258 100644 --- a/web_programming/random_anime_character.py +++ b/web_programming/random_anime_character.py @@ -12,7 +12,7 @@ def save_image(image_url: str, image_title: str) -> None: """ Saves the image of anime character """ - image = requests.get(image_url, headers=headers) + image = requests.get(image_url, headers=headers, timeout=10) with open(image_title, "wb") as file: file.write(image.content) @@ -21,7 +21,9 @@ def random_anime_character() -> tuple[str, str, str]: """ Returns the Title, Description, and Image Title of a random anime character . """ - soup = BeautifulSoup(requests.get(URL, headers=headers).text, "html.parser") + soup = BeautifulSoup( + requests.get(URL, headers=headers, timeout=10).text, "html.parser" + ) title = soup.find("meta", attrs={"property": "og:title"}).attrs["content"] image_url = soup.find("meta", attrs={"property": "og:image"}).attrs["content"] description = soup.find("p", id="description").get_text() diff --git a/web_programming/recaptcha_verification.py b/web_programming/recaptcha_verification.py index c9b691b28a8b..168862204fa9 100644 --- a/web_programming/recaptcha_verification.py +++ b/web_programming/recaptcha_verification.py @@ -56,7 +56,9 @@ def login_using_recaptcha(request): client_key = request.POST.get("g-recaptcha-response") # post recaptcha response to Google's recaptcha api - response = requests.post(url, data={"secret": secret_key, "response": client_key}) + response = requests.post( + url, data={"secret": secret_key, "response": client_key}, timeout=10 + ) # if the recaptcha api verified our keys if response.json().get("success", False): # authenticate the user diff --git a/web_programming/reddit.py b/web_programming/reddit.py index 1c165ecc49ec..6cc1a6b62009 100644 --- a/web_programming/reddit.py +++ b/web_programming/reddit.py @@ -31,6 +31,7 @@ def get_subreddit_data( response = requests.get( f"https://reddit.com/r/{subreddit}/{age}.json?limit={limit}", headers={"User-agent": "A random string"}, + timeout=10, ) if response.status_code == 429: raise requests.HTTPError(response=response) diff --git a/web_programming/search_books_by_isbn.py b/web_programming/search_books_by_isbn.py index 07429e9a9678..6b69018e6639 100644 --- a/web_programming/search_books_by_isbn.py +++ b/web_programming/search_books_by_isbn.py @@ -25,7 +25,7 @@ def get_openlibrary_data(olid: str = "isbn/0140328726") -> dict: if new_olid.count("/") != 1: msg = f"{olid} is not a valid Open Library olid" raise ValueError(msg) - return requests.get(f"https://openlibrary.org/{new_olid}.json").json() + return requests.get(f"https://openlibrary.org/{new_olid}.json", timeout=10).json() def summarize_book(ol_book_data: dict) -> dict: diff --git a/web_programming/slack_message.py b/web_programming/slack_message.py index 5e97d6b64c75..d4d5658898ac 100644 --- a/web_programming/slack_message.py +++ b/web_programming/slack_message.py @@ -5,7 +5,9 @@ def send_slack_message(message_body: str, slack_url: str) -> None: headers = {"Content-Type": "application/json"} - response = requests.post(slack_url, json={"text": message_body}, headers=headers) + response = requests.post( + slack_url, json={"text": message_body}, headers=headers, timeout=10 + ) if response.status_code != 200: msg = ( "Request to slack returned an error " diff --git a/web_programming/world_covid19_stats.py b/web_programming/world_covid19_stats.py index ca81abdc4ce9..4948d8cfd43c 100644 --- a/web_programming/world_covid19_stats.py +++ b/web_programming/world_covid19_stats.py @@ -13,7 +13,7 @@ def world_covid19_stats(url: str = "https://www.worldometers.info/coronavirus") """ Return a dict of current worldwide COVID-19 statistics """ - soup = BeautifulSoup(requests.get(url).text, "html.parser") + soup = BeautifulSoup(requests.get(url, timeout=10).text, "html.parser") keys = soup.findAll("h1") values = soup.findAll("div", {"class": "maincounter-number"}) keys += soup.findAll("span", {"class": "panel-title"}) From dbfa21813ff6fe2d7b439dfd6daa60b14a64d24f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 22 Apr 2024 21:43:19 +0200 Subject: [PATCH 606/757] [pre-commit.ci] pre-commit autoupdate (#11380) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/astral-sh/ruff-pre-commit: v0.3.7 → v0.4.1](https://github.com/astral-sh/ruff-pre-commit/compare/v0.3.7...v0.4.1) - [github.com/tox-dev/pyproject-fmt: 1.7.0 → 1.8.0](https://github.com/tox-dev/pyproject-fmt/compare/1.7.0...1.8.0) * from keras import layers, models * Update lstm_prediction.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- .pre-commit-config.yaml | 4 ++-- computer_vision/cnn_classification.py | 2 +- machine_learning/lstm/lstm_prediction.py | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9472bcfa3e07..eedf6d939748 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.3.7 + rev: v0.4.1 hooks: - id: ruff - id: ruff-format @@ -29,7 +29,7 @@ repos: - tomli - repo: https://github.com/tox-dev/pyproject-fmt - rev: "1.7.0" + rev: "1.8.0" hooks: - id: pyproject-fmt diff --git a/computer_vision/cnn_classification.py b/computer_vision/cnn_classification.py index b813b71033f3..115333eba0d1 100644 --- a/computer_vision/cnn_classification.py +++ b/computer_vision/cnn_classification.py @@ -25,7 +25,7 @@ # Importing the Keras libraries and packages import tensorflow as tf -from tensorflow.keras import layers, models +from keras import layers, models if __name__ == "__main__": # Initialising the CNN diff --git a/machine_learning/lstm/lstm_prediction.py b/machine_learning/lstm/lstm_prediction.py index f0fd12c9de7f..81ac5f01d3d6 100644 --- a/machine_learning/lstm/lstm_prediction.py +++ b/machine_learning/lstm/lstm_prediction.py @@ -7,9 +7,9 @@ import numpy as np import pandas as pd +from keras.layers import LSTM, Dense +from keras.models import Sequential from sklearn.preprocessing import MinMaxScaler -from tensorflow.keras.layers import LSTM, Dense -from tensorflow.keras.models import Sequential if __name__ == "__main__": """ From 79dc7c97acc492d657b5f2f50686cee5b0f64b30 Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Mon, 22 Apr 2024 22:45:24 +0300 Subject: [PATCH 607/757] Enable ruff RUF001 rule (#11378) * Enable ruff RUF001 rule * Fix * Fix --- fuzzy_logic/fuzzy_operations.py | 6 +++--- physics/basic_orbital_capture.py | 6 +++--- physics/malus_law.py | 2 +- pyproject.toml | 1 - 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/fuzzy_logic/fuzzy_operations.py b/fuzzy_logic/fuzzy_operations.py index e41cd2120049..c5e4cbde019d 100644 --- a/fuzzy_logic/fuzzy_operations.py +++ b/fuzzy_logic/fuzzy_operations.py @@ -57,7 +57,7 @@ class FuzzySet: # Union Operations >>> siya.union(sheru) - FuzzySet(name='Siya ∪ Sheru', left_boundary=0.4, peak=0.7, right_boundary=1.0) + FuzzySet(name='Siya U Sheru', left_boundary=0.4, peak=0.7, right_boundary=1.0) """ name: str @@ -147,10 +147,10 @@ def union(self, other) -> FuzzySet: FuzzySet: A new fuzzy set representing the union. >>> FuzzySet("a", 0.1, 0.2, 0.3).union(FuzzySet("b", 0.4, 0.5, 0.6)) - FuzzySet(name='a ∪ b', left_boundary=0.1, peak=0.6, right_boundary=0.35) + FuzzySet(name='a U b', left_boundary=0.1, peak=0.6, right_boundary=0.35) """ return FuzzySet( - f"{self.name} ∪ {other.name}", + f"{self.name} U {other.name}", min(self.left_boundary, other.left_boundary), max(self.right_boundary, other.right_boundary), (self.peak + other.peak) / 2, diff --git a/physics/basic_orbital_capture.py b/physics/basic_orbital_capture.py index eeb45e60240c..a5434b5cb7cb 100644 --- a/physics/basic_orbital_capture.py +++ b/physics/basic_orbital_capture.py @@ -4,14 +4,14 @@ """ These two functions will return the radii of impact for a target object -of mass M and radius R as well as it's effective cross sectional area σ(sigma). -That is to say any projectile with velocity v passing within σ, will impact the +of mass M and radius R as well as it's effective cross sectional area sigma. +That is to say any projectile with velocity v passing within sigma, will impact the target object with mass M. The derivation of which is given at the bottom of this file. The derivation shows that a projectile does not need to aim directly at the target body in order to hit it, as R_capture>R_target. Astronomers refer to the effective -cross section for capture as σ=π*R_capture**2. +cross section for capture as sigma=π*R_capture**2. This algorithm does not account for an N-body problem. diff --git a/physics/malus_law.py b/physics/malus_law.py index ae77d45cf614..374b3423f8ff 100644 --- a/physics/malus_law.py +++ b/physics/malus_law.py @@ -31,7 +31,7 @@ Real polarizers are also not perfect blockers of the polarization orthogonal to their polarization axis; the ratio of the transmission of the unwanted component to the wanted component is called the extinction ratio, and varies from around -1:500 for Polaroid to about 1:106 for Glan–Taylor prism polarizers. +1:500 for Polaroid to about 1:106 for Glan-Taylor prism polarizers. Reference : "https://en.wikipedia.org/wiki/Polarizer#Malus's_law_and_other_properties" """ diff --git a/pyproject.toml b/pyproject.toml index ff22fba81c8a..0185f4d7b987 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,6 @@ lint.ignore = [ # `ruff rule S101` for a description of that rule "PLW2901", # PLW2901: Redefined loop variable -- FIX ME "PT011", # `pytest.raises(Exception)` is too broad, set the `match` parameter or use a more specific exception "PT018", # Assertion should be broken down into multiple parts - "RUF001", # String contains ambiguous {}. Did you mean {}? "RUF002", # Docstring contains ambiguous {}. Did you mean {}? "RUF003", # Comment contains ambiguous {}. Did you mean {}? "S101", # Use of `assert` detected -- DO NOT FIX From 4700297b3e332701eed1d0667f3afefc5b9b66be Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Mon, 22 Apr 2024 22:51:47 +0300 Subject: [PATCH 608/757] Enable ruff RUF002 rule (#11377) * Enable ruff RUF002 rule * Fix --------- Co-authored-by: Christian Clauss --- backtracking/sudoku.py | 4 ++-- .../single_bit_manipulation_operations.py | 14 +++++++------- compression/burrows_wheeler.py | 2 +- compression/lempel_ziv.py | 4 ++-- compression/lempel_ziv_decompress.py | 4 ++-- data_structures/binary_tree/red_black_tree.py | 2 +- digital_image_processing/edge_detection/canny.py | 4 ++-- digital_image_processing/index_calculation.py | 2 +- dynamic_programming/combination_sum_iv.py | 2 +- electronics/coulombs_law.py | 4 ++-- hashes/fletcher16.py | 2 +- linear_algebra/lu_decomposition.py | 2 +- linear_algebra/src/schur_complement.py | 2 +- machine_learning/polynomial_regression.py | 4 ++-- maths/chudnovsky_algorithm.py | 2 +- maths/entropy.py | 4 ++-- maths/lucas_lehmer_primality_test.py | 4 ++-- maths/modular_division.py | 2 +- maths/numerical_analysis/bisection_2.py | 2 +- maths/numerical_analysis/nevilles_method.py | 2 +- maths/simultaneous_linear_equation_solver.py | 6 +++--- matrix/largest_square_area_in_matrix.py | 4 ++-- matrix/spiral_print.py | 2 +- neural_network/back_propagation_neural_network.py | 4 ++-- other/davis_putnam_logemann_loveland.py | 2 +- other/fischer_yates_shuffle.py | 2 +- physics/archimedes_principle_of_buoyant_force.py | 2 +- physics/center_of_mass.py | 8 ++++---- physics/centripetal_force.py | 2 +- physics/lorentz_transformation_four_vector.py | 14 +++++++------- physics/reynolds_number.py | 4 ++-- physics/terminal_velocity.py | 4 ++-- project_euler/problem_004/sol1.py | 2 +- project_euler/problem_004/sol2.py | 2 +- project_euler/problem_008/sol1.py | 2 +- project_euler/problem_008/sol2.py | 2 +- project_euler/problem_008/sol3.py | 2 +- project_euler/problem_015/sol1.py | 4 ++-- project_euler/problem_020/sol1.py | 4 ++-- project_euler/problem_020/sol2.py | 4 ++-- project_euler/problem_020/sol3.py | 4 ++-- project_euler/problem_020/sol4.py | 4 ++-- project_euler/problem_022/sol1.py | 2 +- project_euler/problem_022/sol2.py | 2 +- project_euler/problem_025/sol1.py | 2 +- project_euler/problem_025/sol2.py | 2 +- project_euler/problem_025/sol3.py | 2 +- project_euler/problem_027/sol1.py | 8 ++++---- project_euler/problem_031/sol1.py | 10 +++++----- project_euler/problem_031/sol2.py | 12 ++++++------ project_euler/problem_032/sol32.py | 2 +- project_euler/problem_038/sol1.py | 6 +++--- project_euler/problem_040/sol1.py | 2 +- project_euler/problem_044/sol1.py | 6 +++--- project_euler/problem_045/sol1.py | 4 ++-- project_euler/problem_046/sol1.py | 12 ++++++------ project_euler/problem_047/sol1.py | 10 +++++----- project_euler/problem_053/sol1.py | 2 +- project_euler/problem_097/sol1.py | 4 ++-- project_euler/problem_104/sol1.py | 2 +- project_euler/problem_120/sol1.py | 2 +- project_euler/problem_123/sol1.py | 2 +- project_euler/problem_135/sol1.py | 4 ++-- project_euler/problem_144/sol1.py | 4 ++-- project_euler/problem_174/sol1.py | 2 +- pyproject.toml | 1 + strings/jaro_winkler.py | 2 +- strings/manacher.py | 2 +- strings/prefix_function.py | 2 +- 69 files changed, 132 insertions(+), 131 deletions(-) diff --git a/backtracking/sudoku.py b/backtracking/sudoku.py index 8f5459c76d45..cabeebb90433 100644 --- a/backtracking/sudoku.py +++ b/backtracking/sudoku.py @@ -1,7 +1,7 @@ """ -Given a partially filled 9×9 2D array, the objective is to fill a 9×9 +Given a partially filled 9x9 2D array, the objective is to fill a 9x9 square grid with digits numbered 1 to 9, so that every row, column, and -and each of the nine 3×3 sub-grids contains all of the digits. +and each of the nine 3x3 sub-grids contains all of the digits. This can be solved using Backtracking and is similar to n-queens. We check to see if a cell is safe or not and recursively call the diff --git a/bit_manipulation/single_bit_manipulation_operations.py b/bit_manipulation/single_bit_manipulation_operations.py index b43ff07b776f..fcbf033ccb24 100644 --- a/bit_manipulation/single_bit_manipulation_operations.py +++ b/bit_manipulation/single_bit_manipulation_operations.py @@ -8,8 +8,8 @@ def set_bit(number: int, position: int) -> int: Set the bit at position to 1. Details: perform bitwise or for given number and X. - Where X is a number with all the bits – zeroes and bit on given - position – one. + Where X is a number with all the bits - zeroes and bit on given + position - one. >>> set_bit(0b1101, 1) # 0b1111 15 @@ -26,8 +26,8 @@ def clear_bit(number: int, position: int) -> int: Set the bit at position to 0. Details: perform bitwise and for given number and X. - Where X is a number with all the bits – ones and bit on given - position – zero. + Where X is a number with all the bits - ones and bit on given + position - zero. >>> clear_bit(0b10010, 1) # 0b10000 16 @@ -42,8 +42,8 @@ def flip_bit(number: int, position: int) -> int: Flip the bit at position. Details: perform bitwise xor for given number and X. - Where X is a number with all the bits – zeroes and bit on given - position – one. + Where X is a number with all the bits - zeroes and bit on given + position - one. >>> flip_bit(0b101, 1) # 0b111 7 @@ -79,7 +79,7 @@ def get_bit(number: int, position: int) -> int: Get the bit at the given position Details: perform bitwise and for the given number and X, - Where X is a number with all the bits – zeroes and bit on given position – one. + Where X is a number with all the bits - zeroes and bit on given position - one. If the result is not equal to 0, then the bit on the given position is 1, else 0. >>> get_bit(0b1010, 0) diff --git a/compression/burrows_wheeler.py b/compression/burrows_wheeler.py index ce493a70c8f9..857d677c904e 100644 --- a/compression/burrows_wheeler.py +++ b/compression/burrows_wheeler.py @@ -1,7 +1,7 @@ """ https://en.wikipedia.org/wiki/Burrows%E2%80%93Wheeler_transform -The Burrows–Wheeler transform (BWT, also called block-sorting compression) +The Burrows-Wheeler transform (BWT, also called block-sorting compression) rearranges a character string into runs of similar characters. This is useful for compression, since it tends to be easy to compress a string that has runs of repeated characters by techniques such as move-to-front transform and diff --git a/compression/lempel_ziv.py b/compression/lempel_ziv.py index ac3f0c6cfc06..2751a0ebcdb6 100644 --- a/compression/lempel_ziv.py +++ b/compression/lempel_ziv.py @@ -1,5 +1,5 @@ """ -One of the several implementations of Lempel–Ziv–Welch compression algorithm +One of the several implementations of Lempel-Ziv-Welch compression algorithm https://en.wikipedia.org/wiki/Lempel%E2%80%93Ziv%E2%80%93Welch """ @@ -43,7 +43,7 @@ def add_key_to_lexicon( def compress_data(data_bits: str) -> str: """ - Compresses given data_bits using Lempel–Ziv–Welch compression algorithm + Compresses given data_bits using Lempel-Ziv-Welch compression algorithm and returns the result as a string """ lexicon = {"0": "0", "1": "1"} diff --git a/compression/lempel_ziv_decompress.py b/compression/lempel_ziv_decompress.py index 0e49c83fb790..225e96236c2c 100644 --- a/compression/lempel_ziv_decompress.py +++ b/compression/lempel_ziv_decompress.py @@ -1,5 +1,5 @@ """ -One of the several implementations of Lempel–Ziv–Welch decompression algorithm +One of the several implementations of Lempel-Ziv-Welch decompression algorithm https://en.wikipedia.org/wiki/Lempel%E2%80%93Ziv%E2%80%93Welch """ @@ -26,7 +26,7 @@ def read_file_binary(file_path: str) -> str: def decompress_data(data_bits: str) -> str: """ - Decompresses given data_bits using Lempel–Ziv–Welch compression algorithm + Decompresses given data_bits using Lempel-Ziv-Welch compression algorithm and returns the result as a string """ lexicon = {"0": "0", "1": "1"} diff --git a/data_structures/binary_tree/red_black_tree.py b/data_structures/binary_tree/red_black_tree.py index e68d8d1e3735..a9ecf897c701 100644 --- a/data_structures/binary_tree/red_black_tree.py +++ b/data_structures/binary_tree/red_black_tree.py @@ -17,7 +17,7 @@ class RedBlackTree: and slower for reading in the average case, though, because they're both balanced binary search trees, both will get the same asymptotic performance. - To read more about them, https://en.wikipedia.org/wiki/Red–black_tree + To read more about them, https://en.wikipedia.org/wiki/Red-black_tree Unless otherwise specified, all asymptotic runtimes are specified in terms of the size of the tree. """ diff --git a/digital_image_processing/edge_detection/canny.py b/digital_image_processing/edge_detection/canny.py index f8cbeedb3874..944161c31cfc 100644 --- a/digital_image_processing/edge_detection/canny.py +++ b/digital_image_processing/edge_detection/canny.py @@ -74,9 +74,9 @@ def detect_high_low_threshold( image_shape, destination, threshold_low, threshold_high, weak, strong ): """ - High-Low threshold detection. If an edge pixel’s gradient value is higher + High-Low threshold detection. If an edge pixel's gradient value is higher than the high threshold value, it is marked as a strong edge pixel. If an - edge pixel’s gradient value is smaller than the high threshold value and + edge pixel's gradient value is smaller than the high threshold value and larger than the low threshold value, it is marked as a weak edge pixel. If an edge pixel's value is smaller than the low threshold value, it will be suppressed. diff --git a/digital_image_processing/index_calculation.py b/digital_image_processing/index_calculation.py index 67830668b0da..988f8e72b9a8 100644 --- a/digital_image_processing/index_calculation.py +++ b/digital_image_processing/index_calculation.py @@ -182,7 +182,7 @@ def arv12(self): Atmospherically Resistant Vegetation Index 2 https://www.indexdatabase.de/db/i-single.php?id=396 :return: index - −0.18+1.17*(self.nir−self.red)/(self.nir+self.red) + -0.18+1.17*(self.nir-self.red)/(self.nir+self.red) """ return -0.18 + (1.17 * ((self.nir - self.red) / (self.nir + self.red))) diff --git a/dynamic_programming/combination_sum_iv.py b/dynamic_programming/combination_sum_iv.py index 4526729b70b7..113c06a27a9e 100644 --- a/dynamic_programming/combination_sum_iv.py +++ b/dynamic_programming/combination_sum_iv.py @@ -18,7 +18,7 @@ The basic idea is to go over recursively to find the way such that the sum of chosen elements is “tar”. For every element, we have two choices 1. Include the element in our set of chosen elements. - 2. Don’t include the element in our set of chosen elements. + 2. Don't include the element in our set of chosen elements. """ diff --git a/electronics/coulombs_law.py b/electronics/coulombs_law.py index 18c1a8179eb6..74bbea5ea8ec 100644 --- a/electronics/coulombs_law.py +++ b/electronics/coulombs_law.py @@ -20,8 +20,8 @@ def couloumbs_law( Reference ---------- - Coulomb (1785) "Premier mémoire sur l’électricité et le magnétisme," - Histoire de l’Académie Royale des Sciences, pp. 569–577. + Coulomb (1785) "Premier mémoire sur l'électricité et le magnétisme," + Histoire de l'Académie Royale des Sciences, pp. 569-577. Parameters ---------- diff --git a/hashes/fletcher16.py b/hashes/fletcher16.py index 7c23c98d72c5..add8e185bc06 100644 --- a/hashes/fletcher16.py +++ b/hashes/fletcher16.py @@ -1,6 +1,6 @@ """ The Fletcher checksum is an algorithm for computing a position-dependent -checksum devised by John G. Fletcher (1934–2012) at Lawrence Livermore Labs +checksum devised by John G. Fletcher (1934-2012) at Lawrence Livermore Labs in the late 1970s.[1] The objective of the Fletcher checksum was to provide error-detection properties approaching those of a cyclic redundancy check but with the lower computational effort associated diff --git a/linear_algebra/lu_decomposition.py b/linear_algebra/lu_decomposition.py index 1d364163d9a7..3620674835cd 100644 --- a/linear_algebra/lu_decomposition.py +++ b/linear_algebra/lu_decomposition.py @@ -1,5 +1,5 @@ """ -Lower–upper (LU) decomposition factors a matrix as a product of a lower +Lower-upper (LU) decomposition factors a matrix as a product of a lower triangular matrix and an upper triangular matrix. A square matrix has an LU decomposition under the following conditions: - If the matrix is invertible, then it has an LU decomposition if and only diff --git a/linear_algebra/src/schur_complement.py b/linear_algebra/src/schur_complement.py index 1cc084043856..7c79bb70abfc 100644 --- a/linear_algebra/src/schur_complement.py +++ b/linear_algebra/src/schur_complement.py @@ -18,7 +18,7 @@ def schur_complement( the pseudo_inv argument. Link to Wiki: https://en.wikipedia.org/wiki/Schur_complement - See also Convex Optimization – Boyd and Vandenberghe, A.5.5 + See also Convex Optimization - Boyd and Vandenberghe, A.5.5 >>> import numpy as np >>> a = np.array([[1, 2], [2, 1]]) >>> b = np.array([[0, 3], [3, 0]]) diff --git a/machine_learning/polynomial_regression.py b/machine_learning/polynomial_regression.py index 5bafea96f41e..19f7dc994017 100644 --- a/machine_learning/polynomial_regression.py +++ b/machine_learning/polynomial_regression.py @@ -11,7 +11,7 @@ β = (XᵀX)⁻¹Xᵀy = X⁺y -where X is the design matrix, y is the response vector, and X⁺ denotes the Moore–Penrose +where X is the design matrix, y is the response vector, and X⁺ denotes the Moore-Penrose pseudoinverse of X. In the case of polynomial regression, the design matrix is |1 x₁ x₁² ⋯ x₁ᵐ| @@ -106,7 +106,7 @@ def fit(self, x_train: np.ndarray, y_train: np.ndarray) -> None: β = (XᵀX)⁻¹Xᵀy = X⁺y - where X⁺ denotes the Moore–Penrose pseudoinverse of the design matrix X. This + where X⁺ denotes the Moore-Penrose pseudoinverse of the design matrix X. This function computes X⁺ using singular value decomposition (SVD). References: diff --git a/maths/chudnovsky_algorithm.py b/maths/chudnovsky_algorithm.py index aaee7462822e..d122bf0756f7 100644 --- a/maths/chudnovsky_algorithm.py +++ b/maths/chudnovsky_algorithm.py @@ -5,7 +5,7 @@ def pi(precision: int) -> str: """ The Chudnovsky algorithm is a fast method for calculating the digits of PI, - based on Ramanujan’s PI formulae. + based on Ramanujan's PI formulae. https://en.wikipedia.org/wiki/Chudnovsky_algorithm diff --git a/maths/entropy.py b/maths/entropy.py index 39ec67bea038..b816f1d193f7 100644 --- a/maths/entropy.py +++ b/maths/entropy.py @@ -21,10 +21,10 @@ def calculate_prob(text: str) -> None: :return: Prints 1) Entropy of information based on 1 alphabet 2) Entropy of information based on couples of 2 alphabet - 3) print Entropy of H(X n∣Xn−1) + 3) print Entropy of H(X n|Xn-1) Text from random books. Also, random quotes. - >>> text = ("Behind Winston’s back the voice " + >>> text = ("Behind Winston's back the voice " ... "from the telescreen was still " ... "babbling and the overfulfilment") >>> calculate_prob(text) diff --git a/maths/lucas_lehmer_primality_test.py b/maths/lucas_lehmer_primality_test.py index 292387414dee..af5c81133044 100644 --- a/maths/lucas_lehmer_primality_test.py +++ b/maths/lucas_lehmer_primality_test.py @@ -1,12 +1,12 @@ """ -In mathematics, the Lucas–Lehmer test (LLT) is a primality test for Mersenne +In mathematics, the Lucas-Lehmer test (LLT) is a primality test for Mersenne numbers. https://en.wikipedia.org/wiki/Lucas%E2%80%93Lehmer_primality_test A Mersenne number is a number that is one less than a power of two. That is M_p = 2^p - 1 https://en.wikipedia.org/wiki/Mersenne_prime -The Lucas–Lehmer test is the primality test used by the +The Lucas-Lehmer test is the primality test used by the Great Internet Mersenne Prime Search (GIMPS) to locate large primes. """ diff --git a/maths/modular_division.py b/maths/modular_division.py index 260d5683705d..2f8f4479b27d 100644 --- a/maths/modular_division.py +++ b/maths/modular_division.py @@ -9,7 +9,7 @@ def modular_division(a: int, b: int, n: int) -> int: GCD ( Greatest Common Divisor ) or HCF ( Highest Common Factor ) Given three integers a, b, and n, such that gcd(a,n)=1 and n>1, the algorithm should - return an integer x such that 0≤x≤n−1, and b/a=x(modn) (that is, b=ax(modn)). + return an integer x such that 0≤x≤n-1, and b/a=x(modn) (that is, b=ax(modn)). Theorem: a has a multiplicative inverse modulo n iff gcd(a,n) = 1 diff --git a/maths/numerical_analysis/bisection_2.py b/maths/numerical_analysis/bisection_2.py index 45f26d8d88e4..68ba6577ce29 100644 --- a/maths/numerical_analysis/bisection_2.py +++ b/maths/numerical_analysis/bisection_2.py @@ -1,5 +1,5 @@ """ -Given a function on floating number f(x) and two floating numbers ‘a’ and ‘b’ such that +Given a function on floating number f(x) and two floating numbers `a` and `b` such that f(a) * f(b) < 0 and f(x) is continuous in [a, b]. Here f(x) represents algebraic or transcendental equation. Find root of function in interval [a, b] (Or find a value of x such that f(x) is 0) diff --git a/maths/numerical_analysis/nevilles_method.py b/maths/numerical_analysis/nevilles_method.py index 256b61f5f218..25c93ac6c531 100644 --- a/maths/numerical_analysis/nevilles_method.py +++ b/maths/numerical_analysis/nevilles_method.py @@ -1,7 +1,7 @@ """ Python program to show how to interpolate and evaluate a polynomial using Neville's method. -Neville’s method evaluates a polynomial that passes through a +Neville's method evaluates a polynomial that passes through a given set of x and y points for a particular x value (x0) using the Newton polynomial form. Reference: diff --git a/maths/simultaneous_linear_equation_solver.py b/maths/simultaneous_linear_equation_solver.py index 1287b2002d00..9685a33e82fe 100644 --- a/maths/simultaneous_linear_equation_solver.py +++ b/maths/simultaneous_linear_equation_solver.py @@ -2,10 +2,10 @@ https://en.wikipedia.org/wiki/Augmented_matrix This algorithm solves simultaneous linear equations of the form -λa + λb + λc + λd + ... = γ as [λ, λ, λ, λ, ..., γ] -Where λ & γ are individual coefficients, the no. of equations = no. of coefficients - 1 +λa + λb + λc + λd + ... = y as [λ, λ, λ, λ, ..., y] +Where λ & y are individual coefficients, the no. of equations = no. of coefficients - 1 -Note in order to work there must exist 1 equation where all instances of λ and γ != 0 +Note in order to work there must exist 1 equation where all instances of λ and y != 0 """ diff --git a/matrix/largest_square_area_in_matrix.py b/matrix/largest_square_area_in_matrix.py index a93369c56bbd..16263fb798f1 100644 --- a/matrix/largest_square_area_in_matrix.py +++ b/matrix/largest_square_area_in_matrix.py @@ -31,7 +31,7 @@ Approach: We initialize another matrix (dp) with the same dimensions -as the original one initialized with all 0’s. +as the original one initialized with all 0's. dp_array(i,j) represents the side length of the maximum square whose bottom right corner is the cell with index (i,j) in the original matrix. @@ -39,7 +39,7 @@ Starting from index (0,0), for every 1 found in the original matrix, we update the value of the current element as -dp_array(i,j)=dp_array(dp(i−1,j),dp_array(i−1,j−1),dp_array(i,j−1)) + 1. +dp_array(i,j)=dp_array(dp(i-1,j),dp_array(i-1,j-1),dp_array(i,j-1)) + 1. """ diff --git a/matrix/spiral_print.py b/matrix/spiral_print.py index c16dde69cb56..88bde1db594d 100644 --- a/matrix/spiral_print.py +++ b/matrix/spiral_print.py @@ -89,7 +89,7 @@ def spiral_traversal(matrix: list[list]) -> list[int]: Algorithm: Step 1. first pop the 0 index list. (which is [1,2,3,4] and concatenate the output of [step 2]) - Step 2. Now perform matrix’s Transpose operation (Change rows to column + Step 2. Now perform matrix's Transpose operation (Change rows to column and vice versa) and reverse the resultant matrix. Step 3. Pass the output of [2nd step], to same recursive function till base case hits. diff --git a/neural_network/back_propagation_neural_network.py b/neural_network/back_propagation_neural_network.py index 6131a13e945e..182f759c5fc7 100644 --- a/neural_network/back_propagation_neural_network.py +++ b/neural_network/back_propagation_neural_network.py @@ -2,10 +2,10 @@ """ -A Framework of Back Propagation Neural Network(BP) model +A Framework of Back Propagation Neural Network (BP) model Easy to use: - * add many layers as you want !!! + * add many layers as you want ! ! ! * clearly see how the loss decreasing Easy to expand: * more activation functions diff --git a/other/davis_putnam_logemann_loveland.py b/other/davis_putnam_logemann_loveland.py index 3a76f3dfef08..0f3100b1bc2e 100644 --- a/other/davis_putnam_logemann_loveland.py +++ b/other/davis_putnam_logemann_loveland.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 """ -Davis–Putnam–Logemann–Loveland (DPLL) algorithm is a complete, backtracking-based +Davis-Putnam-Logemann-Loveland (DPLL) algorithm is a complete, backtracking-based search algorithm for deciding the satisfiability of propositional logic formulae in conjunctive normal form, i.e, for solving the Conjunctive Normal Form SATisfiability (CNF-SAT) problem. diff --git a/other/fischer_yates_shuffle.py b/other/fischer_yates_shuffle.py index 37e11479a4c9..5e90b10edd89 100644 --- a/other/fischer_yates_shuffle.py +++ b/other/fischer_yates_shuffle.py @@ -1,6 +1,6 @@ #!/usr/bin/python """ -The Fisher–Yates shuffle is an algorithm for generating a random permutation of a +The Fisher-Yates shuffle is an algorithm for generating a random permutation of a finite sequence. For more details visit wikipedia/Fischer-Yates-Shuffle. diff --git a/physics/archimedes_principle_of_buoyant_force.py b/physics/archimedes_principle_of_buoyant_force.py index 71043e0e1111..38f1a0a83832 100644 --- a/physics/archimedes_principle_of_buoyant_force.py +++ b/physics/archimedes_principle_of_buoyant_force.py @@ -3,7 +3,7 @@ fluid. This principle was discovered by the Greek mathematician Archimedes. Equation for calculating buoyant force: -Fb = ρ * V * g +Fb = p * V * g https://en.wikipedia.org/wiki/Archimedes%27_principle """ diff --git a/physics/center_of_mass.py b/physics/center_of_mass.py index 59c3b807f401..7a20e71be801 100644 --- a/physics/center_of_mass.py +++ b/physics/center_of_mass.py @@ -16,8 +16,8 @@ is the particle equivalent of a given object for the application of Newton's laws of motion. -In the case of a system of particles P_i, i = 1, ..., n , each with mass m_i that are -located in space with coordinates r_i, i = 1, ..., n , the coordinates R of the center +In the case of a system of particles P_i, i = 1, ..., n , each with mass m_i that are +located in space with coordinates r_i, i = 1, ..., n , the coordinates R of the center of mass corresponds to: R = (Σ(mi * ri) / Σ(mi)) @@ -36,8 +36,8 @@ def center_of_mass(particles: list[Particle]) -> Coord3D: Input Parameters ---------------- particles: list(Particle): - A list of particles where each particle is a tuple with it´s (x, y, z) position and - it´s mass. + A list of particles where each particle is a tuple with it's (x, y, z) position and + it's mass. Returns ------- diff --git a/physics/centripetal_force.py b/physics/centripetal_force.py index 04069d256468..a4c624582475 100644 --- a/physics/centripetal_force.py +++ b/physics/centripetal_force.py @@ -6,7 +6,7 @@ The unit of centripetal force is newton. The centripetal force is always directed perpendicular to the -direction of the object’s displacement. Using Newton’s second +direction of the object's displacement. Using Newton's second law of motion, it is found that the centripetal force of an object moving in a circular path always acts towards the centre of the circle. The Centripetal Force Formula is given as the product of mass (in kg) diff --git a/physics/lorentz_transformation_four_vector.py b/physics/lorentz_transformation_four_vector.py index f4fda4dff8cd..3b0fd83d45df 100644 --- a/physics/lorentz_transformation_four_vector.py +++ b/physics/lorentz_transformation_four_vector.py @@ -12,13 +12,13 @@ with respect to X, then the Lorentz transformation from X to X' is X' = BX, where - | γ -γβ 0 0| -B = |-γβ γ 0 0| + | y -γβ 0 0| +B = |-γβ y 0 0| | 0 0 1 0| | 0 0 0 1| is the matrix describing the Lorentz boost between X and X', -γ = 1 / √(1 - v²/c²) is the Lorentz factor, and β = v/c is the velocity as +y = 1 / √(1 - v²/c²) is the Lorentz factor, and β = v/c is the velocity as a fraction of c. Reference: https://en.wikipedia.org/wiki/Lorentz_transformation @@ -63,7 +63,7 @@ def beta(velocity: float) -> float: def gamma(velocity: float) -> float: """ - Calculate the Lorentz factor γ = 1 / √(1 - v²/c²) for a given velocity + Calculate the Lorentz factor y = 1 / √(1 - v²/c²) for a given velocity >>> gamma(4) 1.0000000000000002 >>> gamma(1e5) @@ -90,12 +90,12 @@ def transformation_matrix(velocity: float) -> np.ndarray: """ Calculate the Lorentz transformation matrix for movement in the x direction: - | γ -γβ 0 0| - |-γβ γ 0 0| + | y -γβ 0 0| + |-γβ y 0 0| | 0 0 1 0| | 0 0 0 1| - where γ is the Lorentz factor and β is the velocity as a fraction of c + where y is the Lorentz factor and β is the velocity as a fraction of c >>> transformation_matrix(29979245) array([[ 1.00503781, -0.10050378, 0. , 0. ], [-0.10050378, 1.00503781, 0. , 0. ], diff --git a/physics/reynolds_number.py b/physics/reynolds_number.py index dffe690f8822..c24a9e002855 100644 --- a/physics/reynolds_number.py +++ b/physics/reynolds_number.py @@ -8,10 +8,10 @@ viscous forces. R = Inertial Forces / Viscous Forces -R = (ρ * V * D)/μ +R = (p * V * D)/μ where : -ρ = Density of fluid (in Kg/m^3) +p = Density of fluid (in Kg/m^3) D = Diameter of pipe through which fluid flows (in m) V = Velocity of flow of the fluid (in m/s) μ = Viscosity of the fluid (in Ns/m^2) diff --git a/physics/terminal_velocity.py b/physics/terminal_velocity.py index cec54162e2b4..16714bd02671 100644 --- a/physics/terminal_velocity.py +++ b/physics/terminal_velocity.py @@ -8,13 +8,13 @@ object. The acceleration of the object is zero as the net force acting on the object is zero. -Vt = ((2 * m * g)/(ρ * A * Cd))^0.5 +Vt = ((2 * m * g)/(p * A * Cd))^0.5 where : Vt = Terminal velocity (in m/s) m = Mass of the falling object (in Kg) g = Acceleration due to gravity (value taken : imported from scipy) -ρ = Density of the fluid through which the object is falling (in Kg/m^3) +p = Density of the fluid through which the object is falling (in Kg/m^3) A = Projected area of the object (in m^2) Cd = Drag coefficient (dimensionless) diff --git a/project_euler/problem_004/sol1.py b/project_euler/problem_004/sol1.py index f237afdd942d..f80a3253e741 100644 --- a/project_euler/problem_004/sol1.py +++ b/project_euler/problem_004/sol1.py @@ -4,7 +4,7 @@ Largest palindrome product A palindromic number reads the same both ways. The largest palindrome made -from the product of two 2-digit numbers is 9009 = 91 × 99. +from the product of two 2-digit numbers is 9009 = 91 x 99. Find the largest palindrome made from the product of two 3-digit numbers. diff --git a/project_euler/problem_004/sol2.py b/project_euler/problem_004/sol2.py index abc880966d58..1fa75e7d0c83 100644 --- a/project_euler/problem_004/sol2.py +++ b/project_euler/problem_004/sol2.py @@ -4,7 +4,7 @@ Largest palindrome product A palindromic number reads the same both ways. The largest palindrome made -from the product of two 2-digit numbers is 9009 = 91 × 99. +from the product of two 2-digit numbers is 9009 = 91 x 99. Find the largest palindrome made from the product of two 3-digit numbers. diff --git a/project_euler/problem_008/sol1.py b/project_euler/problem_008/sol1.py index 69dd1b4736c1..adbac8d5ad1f 100644 --- a/project_euler/problem_008/sol1.py +++ b/project_euler/problem_008/sol1.py @@ -4,7 +4,7 @@ Largest product in a series The four adjacent digits in the 1000-digit number that have the greatest -product are 9 × 9 × 8 × 9 = 5832. +product are 9 x 9 x 8 x 9 = 5832. 73167176531330624919225119674426574742355349194934 96983520312774506326239578318016984801869478851843 diff --git a/project_euler/problem_008/sol2.py b/project_euler/problem_008/sol2.py index f83cb1db30b6..e48231e4023b 100644 --- a/project_euler/problem_008/sol2.py +++ b/project_euler/problem_008/sol2.py @@ -4,7 +4,7 @@ Largest product in a series The four adjacent digits in the 1000-digit number that have the greatest -product are 9 × 9 × 8 × 9 = 5832. +product are 9 x 9 x 8 x 9 = 5832. 73167176531330624919225119674426574742355349194934 96983520312774506326239578318016984801869478851843 diff --git a/project_euler/problem_008/sol3.py b/project_euler/problem_008/sol3.py index bf3bcb05b7e9..0d319b9684dd 100644 --- a/project_euler/problem_008/sol3.py +++ b/project_euler/problem_008/sol3.py @@ -4,7 +4,7 @@ Largest product in a series The four adjacent digits in the 1000-digit number that have the greatest -product are 9 × 9 × 8 × 9 = 5832. +product are 9 x 9 x 8 x 9 = 5832. 73167176531330624919225119674426574742355349194934 96983520312774506326239578318016984801869478851843 diff --git a/project_euler/problem_015/sol1.py b/project_euler/problem_015/sol1.py index fd9014a406f6..3c9dae1aed77 100644 --- a/project_euler/problem_015/sol1.py +++ b/project_euler/problem_015/sol1.py @@ -1,9 +1,9 @@ """ Problem 15: https://projecteuler.net/problem=15 -Starting in the top left corner of a 2×2 grid, and only being able to move to +Starting in the top left corner of a 2x2 grid, and only being able to move to the right and down, there are exactly 6 routes to the bottom right corner. -How many such routes are there through a 20×20 grid? +How many such routes are there through a 20x20 grid? """ from math import factorial diff --git a/project_euler/problem_020/sol1.py b/project_euler/problem_020/sol1.py index b472024e54c0..1439bdca38e6 100644 --- a/project_euler/problem_020/sol1.py +++ b/project_euler/problem_020/sol1.py @@ -1,9 +1,9 @@ """ Problem 20: https://projecteuler.net/problem=20 -n! means n × (n − 1) × ... × 3 × 2 × 1 +n! means n x (n - 1) x ... x 3 x 2 x 1 -For example, 10! = 10 × 9 × ... × 3 × 2 × 1 = 3628800, +For example, 10! = 10 x 9 x ... x 3 x 2 x 1 = 3628800, and the sum of the digits in the number 10! is 3 + 6 + 2 + 8 + 8 + 0 + 0 = 27. Find the sum of the digits in the number 100! diff --git a/project_euler/problem_020/sol2.py b/project_euler/problem_020/sol2.py index a1d56ade7708..61684cd5ef6d 100644 --- a/project_euler/problem_020/sol2.py +++ b/project_euler/problem_020/sol2.py @@ -1,9 +1,9 @@ """ Problem 20: https://projecteuler.net/problem=20 -n! means n × (n − 1) × ... × 3 × 2 × 1 +n! means n x (n - 1) x ... x 3 x 2 x 1 -For example, 10! = 10 × 9 × ... × 3 × 2 × 1 = 3628800, +For example, 10! = 10 x 9 x ... x 3 x 2 x 1 = 3628800, and the sum of the digits in the number 10! is 3 + 6 + 2 + 8 + 8 + 0 + 0 = 27. Find the sum of the digits in the number 100! diff --git a/project_euler/problem_020/sol3.py b/project_euler/problem_020/sol3.py index 1886e05463f4..8984def9c34e 100644 --- a/project_euler/problem_020/sol3.py +++ b/project_euler/problem_020/sol3.py @@ -1,9 +1,9 @@ """ Problem 20: https://projecteuler.net/problem=20 -n! means n × (n − 1) × ... × 3 × 2 × 1 +n! means n x (n - 1) x ... x 3 x 2 x 1 -For example, 10! = 10 × 9 × ... × 3 × 2 × 1 = 3628800, +For example, 10! = 10 x 9 x ... x 3 x 2 x 1 = 3628800, and the sum of the digits in the number 10! is 3 + 6 + 2 + 8 + 8 + 0 + 0 = 27. Find the sum of the digits in the number 100! diff --git a/project_euler/problem_020/sol4.py b/project_euler/problem_020/sol4.py index b32ce309dfa6..511ac81e176b 100644 --- a/project_euler/problem_020/sol4.py +++ b/project_euler/problem_020/sol4.py @@ -1,9 +1,9 @@ """ Problem 20: https://projecteuler.net/problem=20 -n! means n × (n − 1) × ... × 3 × 2 × 1 +n! means n x (n - 1) x ... x 3 x 2 x 1 -For example, 10! = 10 × 9 × ... × 3 × 2 × 1 = 3628800, +For example, 10! = 10 x 9 x ... x 3 x 2 x 1 = 3628800, and the sum of the digits in the number 10! is 3 + 6 + 2 + 8 + 8 + 0 + 0 = 27. Find the sum of the digits in the number 100! diff --git a/project_euler/problem_022/sol1.py b/project_euler/problem_022/sol1.py index b6386186e7df..c4af5dfa81df 100644 --- a/project_euler/problem_022/sol1.py +++ b/project_euler/problem_022/sol1.py @@ -10,7 +10,7 @@ For example, when the list is sorted into alphabetical order, COLIN, which is worth 3 + 15 + 12 + 9 + 14 = 53, is the 938th name in the list. So, COLIN would -obtain a score of 938 × 53 = 49714. +obtain a score of 938 x 53 = 49714. What is the total of all the name scores in the file? """ diff --git a/project_euler/problem_022/sol2.py b/project_euler/problem_022/sol2.py index f7092ea1cd12..9c22b6bba0cc 100644 --- a/project_euler/problem_022/sol2.py +++ b/project_euler/problem_022/sol2.py @@ -10,7 +10,7 @@ For example, when the list is sorted into alphabetical order, COLIN, which is worth 3 + 15 + 12 + 9 + 14 = 53, is the 938th name in the list. So, COLIN would -obtain a score of 938 × 53 = 49714. +obtain a score of 938 x 53 = 49714. What is the total of all the name scores in the file? """ diff --git a/project_euler/problem_025/sol1.py b/project_euler/problem_025/sol1.py index 803464b5d786..b3bbb56d20be 100644 --- a/project_euler/problem_025/sol1.py +++ b/project_euler/problem_025/sol1.py @@ -1,7 +1,7 @@ """ The Fibonacci sequence is defined by the recurrence relation: - Fn = Fn−1 + Fn−2, where F1 = 1 and F2 = 1. + Fn = Fn-1 + Fn-2, where F1 = 1 and F2 = 1. Hence the first 12 terms will be: diff --git a/project_euler/problem_025/sol2.py b/project_euler/problem_025/sol2.py index 9e950b355f7a..a0f056023bc9 100644 --- a/project_euler/problem_025/sol2.py +++ b/project_euler/problem_025/sol2.py @@ -1,7 +1,7 @@ """ The Fibonacci sequence is defined by the recurrence relation: - Fn = Fn−1 + Fn−2, where F1 = 1 and F2 = 1. + Fn = Fn-1 + Fn-2, where F1 = 1 and F2 = 1. Hence the first 12 terms will be: diff --git a/project_euler/problem_025/sol3.py b/project_euler/problem_025/sol3.py index 0b9f3a0c84ef..e33b159ac65c 100644 --- a/project_euler/problem_025/sol3.py +++ b/project_euler/problem_025/sol3.py @@ -1,7 +1,7 @@ """ The Fibonacci sequence is defined by the recurrence relation: - Fn = Fn−1 + Fn−2, where F1 = 1 and F2 = 1. + Fn = Fn-1 + Fn-2, where F1 = 1 and F2 = 1. Hence the first 12 terms will be: diff --git a/project_euler/problem_027/sol1.py b/project_euler/problem_027/sol1.py index c93e2b4fa251..48755ec19763 100644 --- a/project_euler/problem_027/sol1.py +++ b/project_euler/problem_027/sol1.py @@ -9,12 +9,12 @@ It turns out that the formula will produce 40 primes for the consecutive values n = 0 to 39. However, when n = 40, 402 + 40 + 41 = 40(40 + 1) + 41 is divisible by 41, and certainly when n = 41, 412 + 41 + 41 is clearly divisible by 41. -The incredible formula n2 − 79n + 1601 was discovered, which produces 80 primes -for the consecutive values n = 0 to 79. The product of the coefficients, −79 and -1601, is −126479. +The incredible formula n2 - 79n + 1601 was discovered, which produces 80 primes +for the consecutive values n = 0 to 79. The product of the coefficients, -79 and +1601, is -126479. Considering quadratics of the form: n² + an + b, where |a| < 1000 and |b| < 1000 -where |n| is the modulus/absolute value of ne.g. |11| = 11 and |−4| = 4 +where |n| is the modulus/absolute value of ne.g. |11| = 11 and |-4| = 4 Find the product of the coefficients, a and b, for the quadratic expression that produces the maximum number of primes for consecutive values of n, starting with n = 0. diff --git a/project_euler/problem_031/sol1.py b/project_euler/problem_031/sol1.py index ba40cf383175..4c9c533eecb7 100644 --- a/project_euler/problem_031/sol1.py +++ b/project_euler/problem_031/sol1.py @@ -2,14 +2,14 @@ Coin sums Problem 31: https://projecteuler.net/problem=31 -In England the currency is made up of pound, £, and pence, p, and there are +In England the currency is made up of pound, f, and pence, p, and there are eight coins in general circulation: -1p, 2p, 5p, 10p, 20p, 50p, £1 (100p) and £2 (200p). -It is possible to make £2 in the following way: +1p, 2p, 5p, 10p, 20p, 50p, f1 (100p) and f2 (200p). +It is possible to make f2 in the following way: -1×£1 + 1×50p + 2×20p + 1×5p + 1×2p + 3×1p -How many different ways can £2 be made using any number of coins? +1xf1 + 1x50p + 2x20p + 1x5p + 1x2p + 3x1p +How many different ways can f2 be made using any number of coins? """ diff --git a/project_euler/problem_031/sol2.py b/project_euler/problem_031/sol2.py index f9e4dc384bff..574f8d4107a1 100644 --- a/project_euler/problem_031/sol2.py +++ b/project_euler/problem_031/sol2.py @@ -3,17 +3,17 @@ Coin sums -In England the currency is made up of pound, £, and pence, p, and there are +In England the currency is made up of pound, f, and pence, p, and there are eight coins in general circulation: -1p, 2p, 5p, 10p, 20p, 50p, £1 (100p) and £2 (200p). -It is possible to make £2 in the following way: +1p, 2p, 5p, 10p, 20p, 50p, f1 (100p) and f2 (200p). +It is possible to make f2 in the following way: -1×£1 + 1×50p + 2×20p + 1×5p + 1×2p + 3×1p -How many different ways can £2 be made using any number of coins? +1xf1 + 1x50p + 2x20p + 1x5p + 1x2p + 3x1p +How many different ways can f2 be made using any number of coins? Hint: - > There are 100 pence in a pound (£1 = 100p) + > There are 100 pence in a pound (f1 = 100p) > There are coins(in pence) are available: 1, 2, 5, 10, 20, 50, 100 and 200. > how many different ways you can combine these values to create 200 pence. diff --git a/project_euler/problem_032/sol32.py b/project_euler/problem_032/sol32.py index a402b5584061..c0ca2ce10791 100644 --- a/project_euler/problem_032/sol32.py +++ b/project_euler/problem_032/sol32.py @@ -3,7 +3,7 @@ digits 1 to n exactly once; for example, the 5-digit number, 15234, is 1 through 5 pandigital. -The product 7254 is unusual, as the identity, 39 × 186 = 7254, containing +The product 7254 is unusual, as the identity, 39 x 186 = 7254, containing multiplicand, multiplier, and product is 1 through 9 pandigital. Find the sum of all products whose multiplicand/multiplier/product identity can diff --git a/project_euler/problem_038/sol1.py b/project_euler/problem_038/sol1.py index 5bef273ea2a9..382892723b7d 100644 --- a/project_euler/problem_038/sol1.py +++ b/project_euler/problem_038/sol1.py @@ -3,9 +3,9 @@ Take the number 192 and multiply it by each of 1, 2, and 3: -192 × 1 = 192 -192 × 2 = 384 -192 × 3 = 576 +192 x 1 = 192 +192 x 2 = 384 +192 x 3 = 576 By concatenating each product we get the 1 to 9 pandigital, 192384576. We will call 192384576 the concatenated product of 192 and (1,2,3) diff --git a/project_euler/problem_040/sol1.py b/project_euler/problem_040/sol1.py index 69be377723a5..721bd063c28a 100644 --- a/project_euler/problem_040/sol1.py +++ b/project_euler/problem_040/sol1.py @@ -11,7 +11,7 @@ If dn represents the nth digit of the fractional part, find the value of the following expression. -d1 × d10 × d100 × d1000 × d10000 × d100000 × d1000000 +d1 x d10 x d100 x d1000 x d10000 x d100000 x d1000000 """ diff --git a/project_euler/problem_044/sol1.py b/project_euler/problem_044/sol1.py index 3b75b6a56a8e..2613563a4bf1 100644 --- a/project_euler/problem_044/sol1.py +++ b/project_euler/problem_044/sol1.py @@ -1,14 +1,14 @@ """ Problem 44: https://projecteuler.net/problem=44 -Pentagonal numbers are generated by the formula, Pn=n(3n−1)/2. The first ten +Pentagonal numbers are generated by the formula, Pn=n(3n-1)/2. The first ten pentagonal numbers are: 1, 5, 12, 22, 35, 51, 70, 92, 117, 145, ... It can be seen that P4 + P7 = 22 + 70 = 92 = P8. However, their difference, -70 − 22 = 48, is not pentagonal. +70 - 22 = 48, is not pentagonal. Find the pair of pentagonal numbers, Pj and Pk, for which their sum and difference -are pentagonal and D = |Pk − Pj| is minimised; what is the value of D? +are pentagonal and D = |Pk - Pj| is minimised; what is the value of D? """ diff --git a/project_euler/problem_045/sol1.py b/project_euler/problem_045/sol1.py index d921b2802c2d..8d016de6e542 100644 --- a/project_euler/problem_045/sol1.py +++ b/project_euler/problem_045/sol1.py @@ -3,8 +3,8 @@ Triangle, pentagonal, and hexagonal numbers are generated by the following formulae: Triangle T(n) = (n * (n + 1)) / 2 1, 3, 6, 10, 15, ... -Pentagonal P(n) = (n * (3 * n − 1)) / 2 1, 5, 12, 22, 35, ... -Hexagonal H(n) = n * (2 * n − 1) 1, 6, 15, 28, 45, ... +Pentagonal P(n) = (n * (3 * n - 1)) / 2 1, 5, 12, 22, 35, ... +Hexagonal H(n) = n * (2 * n - 1) 1, 6, 15, 28, 45, ... It can be verified that T(285) = P(165) = H(143) = 40755. Find the next triangle number that is also pentagonal and hexagonal. diff --git a/project_euler/problem_046/sol1.py b/project_euler/problem_046/sol1.py index 07dd9bbf84c8..f27f658e63e5 100644 --- a/project_euler/problem_046/sol1.py +++ b/project_euler/problem_046/sol1.py @@ -4,12 +4,12 @@ It was proposed by Christian Goldbach that every odd composite number can be written as the sum of a prime and twice a square. -9 = 7 + 2 × 12 -15 = 7 + 2 × 22 -21 = 3 + 2 × 32 -25 = 7 + 2 × 32 -27 = 19 + 2 × 22 -33 = 31 + 2 × 12 +9 = 7 + 2 x 12 +15 = 7 + 2 x 22 +21 = 3 + 2 x 32 +25 = 7 + 2 x 32 +27 = 19 + 2 x 22 +33 = 31 + 2 x 12 It turns out that the conjecture was false. diff --git a/project_euler/problem_047/sol1.py b/project_euler/problem_047/sol1.py index 1287e0d9e107..c9c44a9832dd 100644 --- a/project_euler/problem_047/sol1.py +++ b/project_euler/problem_047/sol1.py @@ -5,14 +5,14 @@ The first two consecutive numbers to have two distinct prime factors are: -14 = 2 × 7 -15 = 3 × 5 +14 = 2 x 7 +15 = 3 x 5 The first three consecutive numbers to have three distinct prime factors are: -644 = 2² × 7 × 23 -645 = 3 × 5 × 43 -646 = 2 × 17 × 19. +644 = 2² x 7 x 23 +645 = 3 x 5 x 43 +646 = 2 x 17 x 19. Find the first four consecutive integers to have four distinct prime factors each. What is the first of these numbers? diff --git a/project_euler/problem_053/sol1.py b/project_euler/problem_053/sol1.py index a32b73c545d6..192cbf25e50c 100644 --- a/project_euler/problem_053/sol1.py +++ b/project_euler/problem_053/sol1.py @@ -10,7 +10,7 @@ In general, -nCr = n!/(r!(n−r)!),where r ≤ n, n! = n×(n−1)×...×3×2×1, and 0! = 1. +nCr = n!/(r!(n-r)!),where r ≤ n, n! = nx(n-1)x...x3x2x1, and 0! = 1. It is not until n = 23, that a value exceeds one-million: 23C10 = 1144066. How many, not necessarily distinct, values of nCr, for 1 ≤ n ≤ 100, are greater diff --git a/project_euler/problem_097/sol1.py b/project_euler/problem_097/sol1.py index 2807e893ded0..a349f3a1dbc9 100644 --- a/project_euler/problem_097/sol1.py +++ b/project_euler/problem_097/sol1.py @@ -1,7 +1,7 @@ """ 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 +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)). diff --git a/project_euler/problem_104/sol1.py b/project_euler/problem_104/sol1.py index d84dbcfc9c65..a0267faa6a38 100644 --- a/project_euler/problem_104/sol1.py +++ b/project_euler/problem_104/sol1.py @@ -3,7 +3,7 @@ The Fibonacci sequence is defined by the recurrence relation: -Fn = Fn−1 + Fn−2, where F1 = 1 and F2 = 1. +Fn = Fn-1 + Fn-2, where F1 = 1 and F2 = 1. It turns out that F541, which contains 113 digits, is the first Fibonacci number for which the last nine digits are 1-9 pandigital (contain all the digits 1 to 9, but not necessarily in order). And F2749, which contains 575 digits, is the first diff --git a/project_euler/problem_120/sol1.py b/project_euler/problem_120/sol1.py index 0e6821214560..2f403972502f 100644 --- a/project_euler/problem_120/sol1.py +++ b/project_euler/problem_120/sol1.py @@ -3,7 +3,7 @@ Description: -Let r be the remainder when (a−1)^n + (a+1)^n is divided by a^2. +Let r be the remainder when (a-1)^n + (a+1)^n is divided by a^2. For example, if a = 7 and n = 3, then r = 42: 6^3 + 8^3 = 728 ≡ 42 mod 49. And as n varies, so too will r, but for a = 7 it turns out that r_max = 42. For 3 ≤ a ≤ 1000, find ∑ r_max. diff --git a/project_euler/problem_123/sol1.py b/project_euler/problem_123/sol1.py index 7239e13a51e9..3dd31a2e8505 100644 --- a/project_euler/problem_123/sol1.py +++ b/project_euler/problem_123/sol1.py @@ -4,7 +4,7 @@ Name: Prime square remainders Let pn be the nth prime: 2, 3, 5, 7, 11, ..., and -let r be the remainder when (pn−1)^n + (pn+1)^n is divided by pn^2. +let r be the remainder when (pn-1)^n + (pn+1)^n is divided by pn^2. For example, when n = 3, p3 = 5, and 43 + 63 = 280 ≡ 5 mod 25. The least value of n for which the remainder first exceeds 10^9 is 7037. diff --git a/project_euler/problem_135/sol1.py b/project_euler/problem_135/sol1.py index ac91fa4e2b9d..d57ace489191 100644 --- a/project_euler/problem_135/sol1.py +++ b/project_euler/problem_135/sol1.py @@ -3,9 +3,9 @@ Given the positive integers, x, y, and z, are consecutive terms of an arithmetic progression, the least value of the positive integer, n, for which the equation, -x2 − y2 − z2 = n, has exactly two solutions is n = 27: +x2 - y2 - z2 = n, has exactly two solutions is n = 27: -342 − 272 − 202 = 122 − 92 − 62 = 27 +342 - 272 - 202 = 122 - 92 - 62 = 27 It turns out that n = 1155 is the least value which has exactly ten solutions. diff --git a/project_euler/problem_144/sol1.py b/project_euler/problem_144/sol1.py index bc16bf985f41..9070455de79f 100644 --- a/project_euler/problem_144/sol1.py +++ b/project_euler/problem_144/sol1.py @@ -6,7 +6,7 @@ The specific white cell we will be considering is an ellipse with the equation 4x^2 + y^2 = 100 -The section corresponding to −0.01 ≤ x ≤ +0.01 at the top is missing, allowing the +The section corresponding to -0.01 ≤ x ≤ +0.01 at the top is missing, allowing the light to enter and exit through the hole.  The light beam in this problem starts at the point (0.0,10.1) just outside the white @@ -20,7 +20,7 @@ the laser beam and the wall of the white cell; the blue line shows the line tangent to the ellipse at the point of incidence of the first bounce. -The slope m of the tangent line at any point (x,y) of the given ellipse is: m = −4x/y +The slope m of the tangent line at any point (x,y) of the given ellipse is: m = -4x/y The normal line is perpendicular to this tangent line at the point of incidence. diff --git a/project_euler/problem_174/sol1.py b/project_euler/problem_174/sol1.py index 33c1b158adbb..9a75e8638880 100644 --- a/project_euler/problem_174/sol1.py +++ b/project_euler/problem_174/sol1.py @@ -14,7 +14,7 @@ Let N(n) be the number of t ≤ 1000000 such that t is type L(n); for example, N(15) = 832. -What is ∑ N(n) for 1 ≤ n ≤ 10? +What is sum N(n) for 1 ≤ n ≤ 10? """ from collections import defaultdict diff --git a/pyproject.toml b/pyproject.toml index 0185f4d7b987..ff22fba81c8a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,6 +10,7 @@ lint.ignore = [ # `ruff rule S101` for a description of that rule "PLW2901", # PLW2901: Redefined loop variable -- FIX ME "PT011", # `pytest.raises(Exception)` is too broad, set the `match` parameter or use a more specific exception "PT018", # Assertion should be broken down into multiple parts + "RUF001", # String contains ambiguous {}. Did you mean {}? "RUF002", # Docstring contains ambiguous {}. Did you mean {}? "RUF003", # Comment contains ambiguous {}. Did you mean {}? "S101", # Use of `assert` detected -- DO NOT FIX diff --git a/strings/jaro_winkler.py b/strings/jaro_winkler.py index c18f0d85d9f4..cae2068fabc1 100644 --- a/strings/jaro_winkler.py +++ b/strings/jaro_winkler.py @@ -3,7 +3,7 @@ def jaro_winkler(str1: str, str2: str) -> float: """ - Jaro–Winkler distance is a string metric measuring an edit distance between two + Jaro-Winkler distance is a string metric measuring an edit distance between two sequences. Output value is between 0.0 and 1.0. diff --git a/strings/manacher.py b/strings/manacher.py index fc8b01cd9c1c..af1b10cf81fb 100644 --- a/strings/manacher.py +++ b/strings/manacher.py @@ -5,7 +5,7 @@ def palindromic_string(input_string: str) -> str: >>> palindromic_string('ababa') 'ababa' - Manacher’s algorithm which finds Longest palindromic Substring in linear time. + Manacher's algorithm which finds Longest palindromic Substring in linear time. 1. first this convert input_string("xyx") into new_string("x|y|x") where odd positions are actual input characters. diff --git a/strings/prefix_function.py b/strings/prefix_function.py index 65bbe9100735..04987deef469 100644 --- a/strings/prefix_function.py +++ b/strings/prefix_function.py @@ -1,7 +1,7 @@ """ https://cp-algorithms.com/string/prefix-function.html -Prefix function Knuth–Morris–Pratt algorithm +Prefix function Knuth-Morris-Pratt algorithm Different algorithm than Knuth-Morris-Pratt pattern finding From d016fda51c08a604738e556a7ccb19e0f9c81dcb Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Mon, 22 Apr 2024 22:56:14 +0300 Subject: [PATCH 609/757] Enable ruff RUF003 rule (#11376) * Enable ruff RUF003 rule * Update pyproject.toml --------- Co-authored-by: Christian Clauss --- dynamic_programming/fast_fibonacci.py | 2 +- graphs/ant_colony_optimization_algorithms.py | 4 ++-- machine_learning/polynomial_regression.py | 2 +- pyproject.toml | 3 --- strings/credit_card_validator.py | 2 +- 5 files changed, 5 insertions(+), 8 deletions(-) diff --git a/dynamic_programming/fast_fibonacci.py b/dynamic_programming/fast_fibonacci.py index 9f956ca2f979..d04a5ac8249b 100644 --- a/dynamic_programming/fast_fibonacci.py +++ b/dynamic_programming/fast_fibonacci.py @@ -26,7 +26,7 @@ def _fib(n: int) -> tuple[int, int]: if n == 0: # (F(0), F(1)) return (0, 1) - # F(2n) = F(n)[2F(n+1) − F(n)] + # F(2n) = F(n)[2F(n+1) - F(n)] # F(2n+1) = F(n+1)^2+F(n)^2 a, b = _fib(n // 2) c = a * (b * 2 - a) diff --git a/graphs/ant_colony_optimization_algorithms.py b/graphs/ant_colony_optimization_algorithms.py index 652ad6144297..13637da44874 100644 --- a/graphs/ant_colony_optimization_algorithms.py +++ b/graphs/ant_colony_optimization_algorithms.py @@ -33,7 +33,7 @@ def main( pheromone_evaporation: float, alpha: float, beta: float, - q: float, # Pheromone system parameters Q,which is a constant + q: float, # Pheromone system parameters Q, which is a constant ) -> tuple[list[int], float]: """ Ant colony algorithm main function @@ -117,7 +117,7 @@ def pheromone_update( cities: dict[int, list[int]], pheromone_evaporation: float, ants_route: list[list[int]], - q: float, # Pheromone system parameters Q,which is a constant + q: float, # Pheromone system parameters Q, which is a constant best_path: list[int], best_distance: float, ) -> tuple[list[list[float]], list[int], float]: diff --git a/machine_learning/polynomial_regression.py b/machine_learning/polynomial_regression.py index 19f7dc994017..212f40bea197 100644 --- a/machine_learning/polynomial_regression.py +++ b/machine_learning/polynomial_regression.py @@ -146,7 +146,7 @@ def fit(self, x_train: np.ndarray, y_train: np.ndarray) -> None: "Design matrix is not full rank, can't compute coefficients" ) - # np.linalg.pinv() computes the Moore–Penrose pseudoinverse using SVD + # np.linalg.pinv() computes the Moore-Penrose pseudoinverse using SVD self.params = np.linalg.pinv(X) @ y_train def predict(self, data: np.ndarray) -> np.ndarray: diff --git a/pyproject.toml b/pyproject.toml index ff22fba81c8a..1134b773308e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,9 +10,6 @@ lint.ignore = [ # `ruff rule S101` for a description of that rule "PLW2901", # PLW2901: Redefined loop variable -- FIX ME "PT011", # `pytest.raises(Exception)` is too broad, set the `match` parameter or use a more specific exception "PT018", # Assertion should be broken down into multiple parts - "RUF001", # String contains ambiguous {}. Did you mean {}? - "RUF002", # Docstring contains ambiguous {}. Did you mean {}? - "RUF003", # Comment contains ambiguous {}. Did you mean {}? "S101", # Use of `assert` detected -- DO NOT FIX "S311", # Standard pseudo-random generators are not suitable for cryptographic purposes -- FIX ME "SLF001", # Private member accessed: `_Iterator` -- FIX ME diff --git a/strings/credit_card_validator.py b/strings/credit_card_validator.py index 78bf45740a63..b8da1c745124 100644 --- a/strings/credit_card_validator.py +++ b/strings/credit_card_validator.py @@ -36,7 +36,7 @@ def luhn_validation(credit_card_number: str) -> bool: digit = int(cc_number[i]) digit *= 2 # If doubling of a number results in a two digit number - # i.e greater than 9(e.g., 6 × 2 = 12), + # i.e greater than 9(e.g., 6 x 2 = 12), # then add the digits of the product (e.g., 12: 1 + 2 = 3, 15: 1 + 5 = 6), # to get a single digit number. if digit > 9: From 3925b8155bebd84eababfba0f5a12e5129cfaa44 Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Wed, 24 Apr 2024 07:32:25 +0300 Subject: [PATCH 610/757] Fix ARG005 per file ignore (#11383) --- machine_learning/linear_discriminant_analysis.py | 2 +- pyproject.toml | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/machine_learning/linear_discriminant_analysis.py b/machine_learning/linear_discriminant_analysis.py index 606e11f3698e..86f28aef671a 100644 --- a/machine_learning/linear_discriminant_analysis.py +++ b/machine_learning/linear_discriminant_analysis.py @@ -256,7 +256,7 @@ def valid_input( input_type: Callable[[object], num], # Usually float or int input_msg: str, err_msg: str, - condition: Callable[[num], bool] = lambda x: True, + condition: Callable[[num], bool] = lambda _: True, default: str | None = None, ) -> num: """ diff --git a/pyproject.toml b/pyproject.toml index 1134b773308e..37ebeeb9ce37 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -86,7 +86,6 @@ max-complexity = 17 # default: 10 "graphs/minimum_spanning_tree_prims.py" = ["SIM114"] "hashes/enigma_machine.py" = ["BLE001"] "machine_learning/decision_tree.py" = ["SIM114"] -"machine_learning/linear_discriminant_analysis.py" = ["ARG005"] "machine_learning/sequential_minimum_optimization.py" = ["SIM115"] "matrix/sherman_morrison.py" = ["SIM103", "SIM114"] "other/l*u_cache.py" = ["RUF012"] From 2d6be5fbb0be2b738d2c246138db9ccda9b6a853 Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Tue, 30 Apr 2024 07:40:26 +0300 Subject: [PATCH 611/757] Enable ruff UP031 rule (#11388) --- data_structures/arrays/sudoku_solver.py | 4 ++-- neural_network/input_data.py | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/data_structures/arrays/sudoku_solver.py b/data_structures/arrays/sudoku_solver.py index 5c1cff06f9d4..a8157a520c97 100644 --- a/data_structures/arrays/sudoku_solver.py +++ b/data_structures/arrays/sudoku_solver.py @@ -150,7 +150,7 @@ def time_solve(grid): display(grid_values(grid)) if values: display(values) - print("(%.5f seconds)\n" % t) + print(f"({t:.5f} seconds)\n") return (t, solved(values)) times, results = zip(*[time_solve(grid) for grid in grids]) @@ -217,4 +217,4 @@ def shuffled(seq): start = time.monotonic() solve(puzzle) t = time.monotonic() - start - print("Solved: %.5f sec" % t) + print(f"Solved: {t:.5f} sec") diff --git a/neural_network/input_data.py b/neural_network/input_data.py index d189e3f9e0d9..f90287fe3f5b 100644 --- a/neural_network/input_data.py +++ b/neural_network/input_data.py @@ -156,7 +156,8 @@ def __init__( self._rng = np.random.default_rng(seed1 if seed is None else seed2) dtype = dtypes.as_dtype(dtype).base_dtype if dtype not in (dtypes.uint8, dtypes.float32): - raise TypeError("Invalid image dtype %r, expected uint8 or float32" % dtype) + msg = f"Invalid image dtype {dtype!r}, expected uint8 or float32" + raise TypeError(msg) if fake_data: self._num_examples = 10000 self.one_hot = one_hot From a7e0b141d8eac30e8f9c4f01c3050e6cdb90f7d4 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 30 Apr 2024 06:58:03 +0200 Subject: [PATCH 612/757] [pre-commit.ci] pre-commit autoupdate (#11387) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/MarcoGorelli/auto-walrus: 0.3.3 → 0.3.4](https://github.com/MarcoGorelli/auto-walrus/compare/0.3.3...0.3.4) - [github.com/astral-sh/ruff-pre-commit: v0.4.1 → v0.4.2](https://github.com/astral-sh/ruff-pre-commit/compare/v0.4.1...v0.4.2) - [github.com/pre-commit/mirrors-mypy: v1.9.0 → v1.10.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.9.0...v1.10.0) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index eedf6d939748..744efc55f41b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,12 +11,12 @@ repos: - id: requirements-txt-fixer - repo: https://github.com/MarcoGorelli/auto-walrus - rev: 0.3.3 + rev: 0.3.4 hooks: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.4.1 + rev: v0.4.2 hooks: - id: ruff - id: ruff-format @@ -47,7 +47,7 @@ repos: - id: validate-pyproject - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.9.0 + rev: v1.10.0 hooks: - id: mypy args: From c026b1952f92836c58e63017f4c75e76c43448a1 Mon Sep 17 00:00:00 2001 From: Margaret <62753112+meg-1@users.noreply.github.com> Date: Wed, 1 May 2024 13:42:54 +0300 Subject: [PATCH 613/757] adding a matrix equalization algorithm (#11360) * adding a matrix equalization algorithm * Adding url for more details * Implementing suggestions --- matrix/matrix_equalization.py | 55 +++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 matrix/matrix_equalization.py diff --git a/matrix/matrix_equalization.py b/matrix/matrix_equalization.py new file mode 100644 index 000000000000..e7e76505cf63 --- /dev/null +++ b/matrix/matrix_equalization.py @@ -0,0 +1,55 @@ +from sys import maxsize + + +def array_equalization(vector: list[int], step_size: int) -> int: + """ + This algorithm equalizes all elements of the input vector + to a common value, by making the minimal number of + "updates" under the constraint of a step size (step_size). + + >>> array_equalization([1, 1, 6, 2, 4, 6, 5, 1, 7, 2, 2, 1, 7, 2, 2], 4) + 4 + >>> array_equalization([22, 81, 88, 71, 22, 81, 632, 81, 81, 22, 92], 2) + 5 + >>> array_equalization([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 5) + 0 + >>> array_equalization([22, 22, 22, 33, 33, 33], 2) + 2 + >>> array_equalization([1, 2, 3], 0) + Traceback (most recent call last): + ValueError: Step size must be positive and non-zero. + >>> array_equalization([1, 2, 3], -1) + Traceback (most recent call last): + ValueError: Step size must be positive and non-zero. + >>> array_equalization([1, 2, 3], 0.5) + Traceback (most recent call last): + ValueError: Step size must be an integer. + >>> array_equalization([1, 2, 3], maxsize) + 1 + """ + if step_size <= 0: + raise ValueError("Step size must be positive and non-zero.") + if not isinstance(step_size, int): + raise ValueError("Step size must be an integer.") + + unique_elements = set(vector) + min_updates = maxsize + + for element in unique_elements: + elem_index = 0 + updates = 0 + while elem_index < len(vector): + if vector[elem_index] != element: + updates += 1 + elem_index += step_size + else: + elem_index += 1 + min_updates = min(min_updates, updates) + + return min_updates + + +if __name__ == "__main__": + from doctest import testmod + + testmod() From 5131e3145dcec9e232c8e8a807ad387f4f9a3d38 Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Wed, 1 May 2024 22:27:59 +0300 Subject: [PATCH 614/757] Fix some ARG002 per file ignores (#11382) * Fix some ARG002 per file ignores * Fix * updating DIRECTORY.md * Fix review issue * Fix review issue --------- Co-authored-by: MaximSmolskiy --- DIRECTORY.md | 1 + audio_filters/show_response.py | 3 ++- data_structures/hashing/hash_table.py | 3 +++ data_structures/hashing/quadratic_probing.py | 2 +- pyproject.toml | 3 --- 5 files changed, 7 insertions(+), 5 deletions(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index f6d6cb463faa..4a053a3f1b7f 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -773,6 +773,7 @@ * [Inverse Of Matrix](matrix/inverse_of_matrix.py) * [Largest Square Area In Matrix](matrix/largest_square_area_in_matrix.py) * [Matrix Class](matrix/matrix_class.py) + * [Matrix Equalization](matrix/matrix_equalization.py) * [Matrix Multiplication Recursion](matrix/matrix_multiplication_recursion.py) * [Matrix Operation](matrix/matrix_operation.py) * [Max Area Of Island](matrix/max_area_of_island.py) diff --git a/audio_filters/show_response.py b/audio_filters/show_response.py index 097b8152b4e6..f9c9537c047c 100644 --- a/audio_filters/show_response.py +++ b/audio_filters/show_response.py @@ -1,5 +1,6 @@ from __future__ import annotations +from abc import abstractmethod from math import pi from typing import Protocol @@ -8,6 +9,7 @@ class FilterType(Protocol): + @abstractmethod def process(self, sample: float) -> float: """ Calculate y[n] @@ -15,7 +17,6 @@ def process(self, sample: float) -> float: >>> issubclass(FilterType, Protocol) True """ - return 0.0 def get_bounds( diff --git a/data_structures/hashing/hash_table.py b/data_structures/hashing/hash_table.py index 7fe57068f6a3..40fcad9a3dab 100644 --- a/data_structures/hashing/hash_table.py +++ b/data_structures/hashing/hash_table.py @@ -1,4 +1,6 @@ #!/usr/bin/env python3 +from abc import abstractmethod + from .number_theory.prime_numbers import next_prime @@ -173,6 +175,7 @@ def _set_value(self, key, data): self.values[key] = data self._keys[key] = data + @abstractmethod def _collision_resolution(self, key, data=None): """ This method is a type of open addressing which is used for handling collision. diff --git a/data_structures/hashing/quadratic_probing.py b/data_structures/hashing/quadratic_probing.py index 2f3401ec8918..56d4926eee9b 100644 --- a/data_structures/hashing/quadratic_probing.py +++ b/data_structures/hashing/quadratic_probing.py @@ -11,7 +11,7 @@ class QuadraticProbing(HashTable): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - def _collision_resolution(self, key, data=None): + def _collision_resolution(self, key, data=None): # noqa: ARG002 """ Quadratic probing is an open addressing scheme used for resolving collisions in hash table. diff --git a/pyproject.toml b/pyproject.toml index 37ebeeb9ce37..4c512ca896b4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -76,11 +76,8 @@ max-complexity = 17 # default: 10 [tool.ruff.lint.per-file-ignores] "arithmetic_analysis/newton_raphson.py" = ["PGH001"] -"audio_filters/show_response.py" = ["ARG002"] "data_structures/binary_tree/binary_search_tree_recursive.py" = ["BLE001"] "data_structures/binary_tree/treap.py" = ["SIM114"] -"data_structures/hashing/hash_table.py" = ["ARG002"] -"data_structures/hashing/quadratic_probing.py" = ["ARG002"] "data_structures/hashing/tests/test_hash_map.py" = ["BLE001"] "data_structures/heap/max_heap.py" = ["SIM114"] "graphs/minimum_spanning_tree_prims.py" = ["SIM114"] From ea53051576a9c5e7398ca2ae6a0823ca54ac3947 Mon Sep 17 00:00:00 2001 From: Xuehai Pan Date: Fri, 3 May 2024 00:43:59 +0800 Subject: [PATCH 615/757] Use `spawn` start method in multiprocessing programs (#11391) * Use `spawn` start method in multiprocessing programs * Set `spawn` start method in doctest * Use `with` statement for locks * Pass multiprocessing context explicitly --- sorts/odd_even_transposition_parallel.py | 79 ++++++++++++++++-------- 1 file changed, 53 insertions(+), 26 deletions(-) diff --git a/sorts/odd_even_transposition_parallel.py b/sorts/odd_even_transposition_parallel.py index 9d2bcdbd7576..5d4e09b211c0 100644 --- a/sorts/odd_even_transposition_parallel.py +++ b/sorts/odd_even_transposition_parallel.py @@ -11,11 +11,11 @@ synchronization could be used. """ -from multiprocessing import Lock, Pipe, Process +import multiprocessing as mp # lock used to ensure that two processes do not access a pipe at the same time # NOTE This breaks testing on build runner. May work better locally -# process_lock = Lock() +# process_lock = mp.Lock() """ The function run by the processes that sorts the list @@ -29,8 +29,17 @@ """ -def oe_process(position, value, l_send, r_send, lr_cv, rr_cv, result_pipe): - process_lock = Lock() +def oe_process( + position, + value, + l_send, + r_send, + lr_cv, + rr_cv, + result_pipe, + multiprocessing_context, +): + process_lock = multiprocessing_context.Lock() # we perform n swaps since after n swaps we know we are sorted # we *could* stop early if we are sorted already, but it takes as long to @@ -38,27 +47,23 @@ def oe_process(position, value, l_send, r_send, lr_cv, rr_cv, result_pipe): for i in range(10): if (i + position) % 2 == 0 and r_send is not None: # send your value to your right neighbor - process_lock.acquire() - r_send[1].send(value) - process_lock.release() + with process_lock: + r_send[1].send(value) # receive your right neighbor's value - process_lock.acquire() - temp = rr_cv[0].recv() - process_lock.release() + with process_lock: + temp = rr_cv[0].recv() # take the lower value since you are on the left value = min(value, temp) elif (i + position) % 2 != 0 and l_send is not None: # send your value to your left neighbor - process_lock.acquire() - l_send[1].send(value) - process_lock.release() + with process_lock: + l_send[1].send(value) # receive your left neighbor's value - process_lock.acquire() - temp = lr_cv[0].recv() - process_lock.release() + with process_lock: + temp = lr_cv[0].recv() # take the higher value since you are on the right value = max(value, temp) @@ -94,39 +99,60 @@ def odd_even_transposition(arr): >>> odd_even_transposition(unsorted_list) == sorted(unsorted_list + [1]) False """ + # spawn method is considered safer than fork + multiprocessing_context = mp.get_context("spawn") + process_array_ = [] result_pipe = [] # initialize the list of pipes where the values will be retrieved for _ in arr: - result_pipe.append(Pipe()) + result_pipe.append(multiprocessing_context.Pipe()) # creates the processes # the first and last process only have one neighbor so they are made outside # of the loop - temp_rs = Pipe() - temp_rr = Pipe() + temp_rs = multiprocessing_context.Pipe() + temp_rr = multiprocessing_context.Pipe() process_array_.append( - Process( + multiprocessing_context.Process( target=oe_process, - args=(0, arr[0], None, temp_rs, None, temp_rr, result_pipe[0]), + args=( + 0, + arr[0], + None, + temp_rs, + None, + temp_rr, + result_pipe[0], + multiprocessing_context, + ), ) ) temp_lr = temp_rs temp_ls = temp_rr for i in range(1, len(arr) - 1): - temp_rs = Pipe() - temp_rr = Pipe() + temp_rs = multiprocessing_context.Pipe() + temp_rr = multiprocessing_context.Pipe() process_array_.append( - Process( + multiprocessing_context.Process( target=oe_process, - args=(i, arr[i], temp_ls, temp_rs, temp_lr, temp_rr, result_pipe[i]), + args=( + i, + arr[i], + temp_ls, + temp_rs, + temp_lr, + temp_rr, + result_pipe[i], + multiprocessing_context, + ), ) ) temp_lr = temp_rs temp_ls = temp_rr process_array_.append( - Process( + multiprocessing_context.Process( target=oe_process, args=( len(arr) - 1, @@ -136,6 +162,7 @@ def odd_even_transposition(arr): temp_lr, None, result_pipe[len(arr) - 1], + multiprocessing_context, ), ) ) From 1868c0b6375188a9034478a2711e40c343d00c2e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 6 May 2024 21:38:58 +0200 Subject: [PATCH 616/757] [pre-commit.ci] pre-commit autoupdate (#11394) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.4.2 → v0.4.3](https://github.com/astral-sh/ruff-pre-commit/compare/v0.4.2...v0.4.3) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 744efc55f41b..210b7494036e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.4.2 + rev: v0.4.3 hooks: - id: ruff - id: ruff-format From c599f6c9107a1b09c08ddce17053d7b5d0895a83 Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Fri, 10 May 2024 22:59:53 +0300 Subject: [PATCH 617/757] Fix some SIM114 per file ignores (#11395) * updating DIRECTORY.md * Fix some SIM114 per file ignores * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix review issue --------- Co-authored-by: MaximSmolskiy Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- data_structures/binary_tree/treap.py | 4 +--- data_structures/heap/max_heap.py | 2 +- graphs/minimum_spanning_tree_prims.py | 2 +- machine_learning/decision_tree.py | 2 +- matrix/sherman_morrison.py | 2 +- pyproject.toml | 6 +----- 6 files changed, 6 insertions(+), 12 deletions(-) diff --git a/data_structures/binary_tree/treap.py b/data_structures/binary_tree/treap.py index e7ddf931b83a..3114c6fa1c26 100644 --- a/data_structures/binary_tree/treap.py +++ b/data_structures/binary_tree/treap.py @@ -39,9 +39,7 @@ def split(root: Node | None, value: int) -> tuple[Node | None, Node | None]: Left tree contains all values less than split value. Right tree contains all values greater or equal, than split value """ - if root is None: # None tree is split into 2 Nones - return None, None - elif root.value is None: + if root is None or root.value is None: # None tree is split into 2 Nones return None, None elif value < root.value: """ diff --git a/data_structures/heap/max_heap.py b/data_structures/heap/max_heap.py index 5a9f9cf88433..589f2595a8da 100644 --- a/data_structures/heap/max_heap.py +++ b/data_structures/heap/max_heap.py @@ -38,7 +38,7 @@ def insert(self, value: int) -> None: def __swap_down(self, i: int) -> None: """Swap the element down""" while self.__size >= 2 * i: - if 2 * i + 1 > self.__size: + if 2 * i + 1 > self.__size: # noqa: SIM114 bigger_child = 2 * i elif self.__heap[2 * i] > self.__heap[2 * i + 1]: bigger_child = 2 * i diff --git a/graphs/minimum_spanning_tree_prims.py b/graphs/minimum_spanning_tree_prims.py index 90c9f4c91e86..d0b45d7ef139 100644 --- a/graphs/minimum_spanning_tree_prims.py +++ b/graphs/minimum_spanning_tree_prims.py @@ -16,7 +16,7 @@ def top_to_bottom(self, heap, start, size, positions): if start > size // 2 - 1: return else: - if 2 * start + 2 >= size: + if 2 * start + 2 >= size: # noqa: SIM114 smallest_child = 2 * start + 1 elif heap[2 * start + 1] < heap[2 * start + 2]: smallest_child = 2 * start + 1 diff --git a/machine_learning/decision_tree.py b/machine_learning/decision_tree.py index e48905eeac6a..d0bd6ab0b555 100644 --- a/machine_learning/decision_tree.py +++ b/machine_learning/decision_tree.py @@ -105,7 +105,7 @@ def train(self, x, y): the predictor """ for i in range(len(x)): - if len(x[:i]) < self.min_leaf_size: + if len(x[:i]) < self.min_leaf_size: # noqa: SIM114 continue elif len(x[i:]) < self.min_leaf_size: continue diff --git a/matrix/sherman_morrison.py b/matrix/sherman_morrison.py index 7f10ae706e85..e2a09c1d0070 100644 --- a/matrix/sherman_morrison.py +++ b/matrix/sherman_morrison.py @@ -65,7 +65,7 @@ def validate_indices(self, loc: tuple[int, int]) -> bool: >>> a.validate_indices((0, 0)) True """ - if not (isinstance(loc, (list, tuple)) and len(loc) == 2): + if not (isinstance(loc, (list, tuple)) and len(loc) == 2): # noqa: SIM114 return False elif not (0 <= loc[0] < self.row and 0 <= loc[1] < self.column): return False diff --git a/pyproject.toml b/pyproject.toml index 4c512ca896b4..c07bc9c48e51 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -77,14 +77,10 @@ max-complexity = 17 # default: 10 [tool.ruff.lint.per-file-ignores] "arithmetic_analysis/newton_raphson.py" = ["PGH001"] "data_structures/binary_tree/binary_search_tree_recursive.py" = ["BLE001"] -"data_structures/binary_tree/treap.py" = ["SIM114"] "data_structures/hashing/tests/test_hash_map.py" = ["BLE001"] -"data_structures/heap/max_heap.py" = ["SIM114"] -"graphs/minimum_spanning_tree_prims.py" = ["SIM114"] "hashes/enigma_machine.py" = ["BLE001"] -"machine_learning/decision_tree.py" = ["SIM114"] "machine_learning/sequential_minimum_optimization.py" = ["SIM115"] -"matrix/sherman_morrison.py" = ["SIM103", "SIM114"] +"matrix/sherman_morrison.py" = ["SIM103"] "other/l*u_cache.py" = ["RUF012"] "physics/newtons_second_law_of_motion.py" = ["BLE001"] "project_euler/problem_099/sol1.py" = ["SIM115"] From 1f368da06d361e3d1415a2ec7d8857068b746586 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 14 May 2024 13:38:55 +0200 Subject: [PATCH 618/757] [pre-commit.ci] pre-commit autoupdate (#11402) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/astral-sh/ruff-pre-commit: v0.4.3 → v0.4.4](https://github.com/astral-sh/ruff-pre-commit/compare/v0.4.3...v0.4.4) - [github.com/tox-dev/pyproject-fmt: 1.8.0 → 2.0.4](https://github.com/tox-dev/pyproject-fmt/compare/1.8.0...2.0.4) - [github.com/abravalheri/validate-pyproject: v0.16 → v0.17](https://github.com/abravalheri/validate-pyproject/compare/v0.16...v0.17) * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 6 +- pyproject.toml | 184 +++++++++++++++++++++++----------------- 2 files changed, 107 insertions(+), 83 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 210b7494036e..521769096369 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.4.3 + rev: v0.4.4 hooks: - id: ruff - id: ruff-format @@ -29,7 +29,7 @@ repos: - tomli - repo: https://github.com/tox-dev/pyproject-fmt - rev: "1.8.0" + rev: "2.0.4" hooks: - id: pyproject-fmt @@ -42,7 +42,7 @@ repos: pass_filenames: false - repo: https://github.com/abravalheri/validate-pyproject - rev: v0.16 + rev: v0.17 hooks: - id: validate-pyproject diff --git a/pyproject.toml b/pyproject.toml index c07bc9c48e51..89ed22bc6ab1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,61 +1,61 @@ [tool.ruff] -lint.ignore = [ # `ruff rule S101` for a description of that rule - "B904", # Within an `except` clause, raise exceptions with `raise ... from err` -- FIX ME - "B905", # `zip()` without an explicit `strict=` parameter -- FIX ME - "EM101", # Exception must not use a string literal, assign to variable first - "EXE001", # Shebang is present but file is not executable -- DO NOT FIX - "G004", # Logging statement uses f-string - "PLC1901", # `{}` can be simplified to `{}` as an empty string is falsey - "PLW060", # Using global for `{name}` but no assignment is done -- DO NOT FIX - "PLW2901", # PLW2901: Redefined loop variable -- FIX ME - "PT011", # `pytest.raises(Exception)` is too broad, set the `match` parameter or use a more specific exception - "PT018", # Assertion should be broken down into multiple parts - "S101", # Use of `assert` detected -- DO NOT FIX - "S311", # Standard pseudo-random generators are not suitable for cryptographic purposes -- FIX ME - "SLF001", # Private member accessed: `_Iterator` -- FIX ME - "UP038", # Use `X | Y` in `{}` call instead of `(X, Y)` -- DO NOT FIX -] -lint.select = [ # https://beta.ruff.rs/docs/rules - "A", # flake8-builtins - "ARG", # flake8-unused-arguments - "ASYNC", # flake8-async - "B", # flake8-bugbear - "BLE", # flake8-blind-except - "C4", # flake8-comprehensions - "C90", # McCabe cyclomatic complexity - "DJ", # flake8-django - "DTZ", # flake8-datetimez - "E", # pycodestyle - "EM", # flake8-errmsg - "EXE", # flake8-executable - "F", # Pyflakes - "FA", # flake8-future-annotations - "FLY", # flynt - "G", # flake8-logging-format - "I", # isort - "ICN", # flake8-import-conventions - "INP", # flake8-no-pep420 - "INT", # flake8-gettext - "ISC", # flake8-implicit-str-concat - "N", # pep8-naming - "NPY", # NumPy-specific rules - "PD", # pandas-vet - "PGH", # pygrep-hooks - "PIE", # flake8-pie - "PL", # Pylint - "PT", # flake8-pytest-style - "PYI", # flake8-pyi - "RSE", # flake8-raise - "RUF", # Ruff-specific rules - "S", # flake8-bandit - "SIM", # flake8-simplify - "SLF", # flake8-self - "T10", # flake8-debugger - "TD", # flake8-todos - "TID", # flake8-tidy-imports - "UP", # pyupgrade - "W", # pycodestyle - "YTT", # flake8-2020 +lint.ignore = [ # `ruff rule S101` for a description of that rule + "B904", # Within an `except` clause, raise exceptions with `raise ... from err` -- FIX ME + "B905", # `zip()` without an explicit `strict=` parameter -- FIX ME + "EM101", # Exception must not use a string literal, assign to variable first + "EXE001", # Shebang is present but file is not executable -- DO NOT FIX + "G004", # Logging statement uses f-string + "PLC1901", # `{}` can be simplified to `{}` as an empty string is falsey + "PLW060", # Using global for `{name}` but no assignment is done -- DO NOT FIX + "PLW2901", # PLW2901: Redefined loop variable -- FIX ME + "PT011", # `pytest.raises(Exception)` is too broad, set the `match` parameter or use a more specific exception + "PT018", # Assertion should be broken down into multiple parts + "S101", # Use of `assert` detected -- DO NOT FIX + "S311", # Standard pseudo-random generators are not suitable for cryptographic purposes -- FIX ME + "SLF001", # Private member accessed: `_Iterator` -- FIX ME + "UP038", # Use `X | Y` in `{}` call instead of `(X, Y)` -- DO NOT FIX +] +lint.select = [ # https://beta.ruff.rs/docs/rules + "A", # flake8-builtins + "ARG", # flake8-unused-arguments + "ASYNC", # flake8-async + "B", # flake8-bugbear + "BLE", # flake8-blind-except + "C4", # flake8-comprehensions + "C90", # McCabe cyclomatic complexity + "DJ", # flake8-django + "DTZ", # flake8-datetimez + "E", # pycodestyle + "EM", # flake8-errmsg + "EXE", # flake8-executable + "F", # Pyflakes + "FA", # flake8-future-annotations + "FLY", # flynt + "G", # flake8-logging-format + "I", # isort + "ICN", # flake8-import-conventions + "INP", # flake8-no-pep420 + "INT", # flake8-gettext + "ISC", # flake8-implicit-str-concat + "N", # pep8-naming + "NPY", # NumPy-specific rules + "PD", # pandas-vet + "PGH", # pygrep-hooks + "PIE", # flake8-pie + "PL", # Pylint + "PT", # flake8-pytest-style + "PYI", # flake8-pyi + "RSE", # flake8-raise + "RUF", # Ruff-specific rules + "S", # flake8-bandit + "SIM", # flake8-simplify + "SLF", # flake8-self + "T10", # flake8-debugger + "TD", # flake8-todos + "TID", # flake8-tidy-imports + "UP", # pyupgrade + "W", # pycodestyle + "YTT", # flake8-2020 # "ANN", # flake8-annotations # FIX ME? # "COM", # flake8-commas # "D", # pydocstyle -- FIX ME? @@ -71,27 +71,51 @@ lint.select = [ # https://beta.ruff.rs/docs/rules output-format = "full" target-version = "py312" -[tool.ruff.lint.mccabe] # DO NOT INCREASE THIS VALUE -max-complexity = 17 # default: 10 +[tool.ruff.lint.mccabe] # DO NOT INCREASE THIS VALUE +max-complexity = 17 # default: 10 [tool.ruff.lint.per-file-ignores] -"arithmetic_analysis/newton_raphson.py" = ["PGH001"] -"data_structures/binary_tree/binary_search_tree_recursive.py" = ["BLE001"] -"data_structures/hashing/tests/test_hash_map.py" = ["BLE001"] -"hashes/enigma_machine.py" = ["BLE001"] -"machine_learning/sequential_minimum_optimization.py" = ["SIM115"] -"matrix/sherman_morrison.py" = ["SIM103"] -"other/l*u_cache.py" = ["RUF012"] -"physics/newtons_second_law_of_motion.py" = ["BLE001"] -"project_euler/problem_099/sol1.py" = ["SIM115"] -"sorts/external_sort.py" = ["SIM115"] +"arithmetic_analysis/newton_raphson.py" = [ + "PGH001", +] +"data_structures/binary_tree/binary_search_tree_recursive.py" = [ + "BLE001", +] +"data_structures/hashing/tests/test_hash_map.py" = [ + "BLE001", +] +"hashes/enigma_machine.py" = [ + "BLE001", +] +"machine_learning/sequential_minimum_optimization.py" = [ + "SIM115", +] +"matrix/sherman_morrison.py" = [ + "SIM103", +] +"other/l*u_cache.py" = [ + "RUF012", +] +"physics/newtons_second_law_of_motion.py" = [ + "BLE001", +] +"project_euler/problem_099/sol1.py" = [ + "SIM115", +] +"sorts/external_sort.py" = [ + "SIM115", +] -[tool.ruff.lint.pylint] # DO NOT INCREASE THESE VALUES -allow-magic-value-types = ["float", "int", "str"] -max-args = 10 # default: 5 -max-branches = 20 # default: 12 -max-returns = 8 # default: 6 -max-statements = 88 # default: 50 +[tool.ruff.lint.pylint] # DO NOT INCREASE THESE VALUES +allow-magic-value-types = [ + "float", + "int", + "str", +] +max-args = 10 # default: 5 +max-branches = 20 # default: 12 +max-returns = 8 # default: 6 +max-statements = 88 # default: 50 [tool.codespell] ignore-words-list = "3rt,ans,bitap,crate,damon,fo,followings,hist,iff,kwanza,manuel,mater,secant,som,sur,tim,toi,zar" @@ -99,17 +123,17 @@ skip = "./.*,*.json,ciphers/prehistoric_men.txt,project_euler/problem_022/p022_n [tool.pytest.ini_options] markers = [ - "mat_ops: mark a test as utilizing matrix operations.", + "mat_ops: mark a test as utilizing matrix operations.", ] addopts = [ - "--durations=10", - "--doctest-modules", - "--showlocals", + "--durations=10", + "--doctest-modules", + "--showlocals", ] [tool.coverage.report] omit = [ ".env/*", - "project_euler/*" + "project_euler/*", ] sort = "Cover" From 0139143abb286027bd3954f3862aab4558642019 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 20 May 2024 22:44:57 +0200 Subject: [PATCH 619/757] [pre-commit.ci] pre-commit autoupdate (#11408) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/tox-dev/pyproject-fmt: 2.0.4 → 2.1.1](https://github.com/tox-dev/pyproject-fmt/compare/2.0.4...2.1.1) - [github.com/abravalheri/validate-pyproject: v0.17 → v0.18](https://github.com/abravalheri/validate-pyproject/compare/v0.17...v0.18) * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 4 +-- pyproject.toml | 79 ++++++++++++++++++++--------------------- 2 files changed, 40 insertions(+), 43 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 521769096369..b63457ca85e3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -29,7 +29,7 @@ repos: - tomli - repo: https://github.com/tox-dev/pyproject-fmt - rev: "2.0.4" + rev: "2.1.1" hooks: - id: pyproject-fmt @@ -42,7 +42,7 @@ repos: pass_filenames: false - repo: https://github.com/abravalheri/validate-pyproject - rev: v0.17 + rev: v0.18 hooks: - id: validate-pyproject diff --git a/pyproject.toml b/pyproject.toml index 89ed22bc6ab1..5b8ce4e72dfd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,21 +1,9 @@ [tool.ruff] -lint.ignore = [ # `ruff rule S101` for a description of that rule - "B904", # Within an `except` clause, raise exceptions with `raise ... from err` -- FIX ME - "B905", # `zip()` without an explicit `strict=` parameter -- FIX ME - "EM101", # Exception must not use a string literal, assign to variable first - "EXE001", # Shebang is present but file is not executable -- DO NOT FIX - "G004", # Logging statement uses f-string - "PLC1901", # `{}` can be simplified to `{}` as an empty string is falsey - "PLW060", # Using global for `{name}` but no assignment is done -- DO NOT FIX - "PLW2901", # PLW2901: Redefined loop variable -- FIX ME - "PT011", # `pytest.raises(Exception)` is too broad, set the `match` parameter or use a more specific exception - "PT018", # Assertion should be broken down into multiple parts - "S101", # Use of `assert` detected -- DO NOT FIX - "S311", # Standard pseudo-random generators are not suitable for cryptographic purposes -- FIX ME - "SLF001", # Private member accessed: `_Iterator` -- FIX ME - "UP038", # Use `X | Y` in `{}` call instead of `(X, Y)` -- DO NOT FIX -] -lint.select = [ # https://beta.ruff.rs/docs/rules +target-version = "py312" + +output-format = "full" +lint.select = [ + # https://beta.ruff.rs/docs/rules "A", # flake8-builtins "ARG", # flake8-unused-arguments "ASYNC", # flake8-async @@ -68,54 +56,63 @@ lint.select = [ # https://beta.ruff.rs/docs/rules # "TCH", # flake8-type-checking # "TRY", # tryceratops ] -output-format = "full" -target-version = "py312" - -[tool.ruff.lint.mccabe] # DO NOT INCREASE THIS VALUE -max-complexity = 17 # default: 10 - -[tool.ruff.lint.per-file-ignores] -"arithmetic_analysis/newton_raphson.py" = [ +lint.per-file-ignores."arithmetic_analysis/newton_raphson.py" = [ "PGH001", ] -"data_structures/binary_tree/binary_search_tree_recursive.py" = [ +lint.per-file-ignores."data_structures/binary_tree/binary_search_tree_recursive.py" = [ "BLE001", ] -"data_structures/hashing/tests/test_hash_map.py" = [ +lint.per-file-ignores."data_structures/hashing/tests/test_hash_map.py" = [ "BLE001", ] -"hashes/enigma_machine.py" = [ +lint.per-file-ignores."hashes/enigma_machine.py" = [ "BLE001", ] -"machine_learning/sequential_minimum_optimization.py" = [ +lint.per-file-ignores."machine_learning/sequential_minimum_optimization.py" = [ "SIM115", ] -"matrix/sherman_morrison.py" = [ +lint.per-file-ignores."matrix/sherman_morrison.py" = [ "SIM103", ] -"other/l*u_cache.py" = [ +lint.per-file-ignores."other/l*u_cache.py" = [ "RUF012", ] -"physics/newtons_second_law_of_motion.py" = [ +lint.per-file-ignores."physics/newtons_second_law_of_motion.py" = [ "BLE001", ] -"project_euler/problem_099/sol1.py" = [ +lint.per-file-ignores."project_euler/problem_099/sol1.py" = [ "SIM115", ] -"sorts/external_sort.py" = [ +lint.per-file-ignores."sorts/external_sort.py" = [ "SIM115", ] - -[tool.ruff.lint.pylint] # DO NOT INCREASE THESE VALUES -allow-magic-value-types = [ +lint.mccabe.max-complexity = 17 # default: 10 +lint.pylint.allow-magic-value-types = [ "float", "int", "str", ] -max-args = 10 # default: 5 -max-branches = 20 # default: 12 -max-returns = 8 # default: 6 -max-statements = 88 # default: 50 +lint.pylint.max-args = 10 # default: 5 +lint.pylint.max-branches = 20 # default: 12 +lint.pylint.max-returns = 8 # default: 6 +lint.pylint.max-statements = 88 # default: 50 +lint.ignore = [ + # `ruff rule S101` for a description of that rule + "B904", # Within an `except` clause, raise exceptions with `raise ... from err` -- FIX ME + "B905", # `zip()` without an explicit `strict=` parameter -- FIX ME + "EM101", # Exception must not use a string literal, assign to variable first + "EXE001", # Shebang is present but file is not executable -- DO NOT FIX + "G004", # Logging statement uses f-string + "PLC1901", # `{}` can be simplified to `{}` as an empty string is falsey + "PLW060", # Using global for `{name}` but no assignment is done -- DO NOT FIX + "PLW2901", # PLW2901: Redefined loop variable -- FIX ME + "PT011", # `pytest.raises(Exception)` is too broad, set the `match` parameter or use a more specific exception + "PT018", # Assertion should be broken down into multiple parts + "S101", # Use of `assert` detected -- DO NOT FIX + "S311", # Standard pseudo-random generators are not suitable for cryptographic purposes -- FIX ME + "SLF001", # Private member accessed: `_Iterator` -- FIX ME + "UP038", # Use `X | Y` in `{}` call instead of `(X, Y)` -- DO NOT FIX +] [tool.codespell] ignore-words-list = "3rt,ans,bitap,crate,damon,fo,followings,hist,iff,kwanza,manuel,mater,secant,som,sur,tim,toi,zar" From 82aa909db7736d8022532bee4dc381072d8c5b1f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 27 May 2024 21:56:48 -0400 Subject: [PATCH 620/757] [pre-commit.ci] pre-commit autoupdate (#11417) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/astral-sh/ruff-pre-commit: v0.4.4 → v0.4.5](https://github.com/astral-sh/ruff-pre-commit/compare/v0.4.4...v0.4.5) - [github.com/codespell-project/codespell: v2.2.6 → v2.3.0](https://github.com/codespell-project/codespell/compare/v2.2.6...v2.3.0) - [github.com/tox-dev/pyproject-fmt: 2.1.1 → 2.1.3](https://github.com/tox-dev/pyproject-fmt/compare/2.1.1...2.1.3) * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * iterable * at most --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- .pre-commit-config.yaml | 6 +++--- graphs/dijkstra_algorithm.py | 2 +- project_euler/problem_047/sol1.py | 2 +- pyproject.toml | 35 ++++++++++++++++--------------- 4 files changed, 23 insertions(+), 22 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b63457ca85e3..43bf547dec6e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,20 +16,20 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.4.4 + rev: v0.4.5 hooks: - id: ruff - id: ruff-format - repo: https://github.com/codespell-project/codespell - rev: v2.2.6 + rev: v2.3.0 hooks: - id: codespell additional_dependencies: - tomli - repo: https://github.com/tox-dev/pyproject-fmt - rev: "2.1.1" + rev: "2.1.3" hooks: - id: pyproject-fmt diff --git a/graphs/dijkstra_algorithm.py b/graphs/dijkstra_algorithm.py index 2efa2cb634ff..51412b790bac 100644 --- a/graphs/dijkstra_algorithm.py +++ b/graphs/dijkstra_algorithm.py @@ -215,7 +215,7 @@ def decrease_key(self, tup, new_d): [(5, 'A'), (15, 'B')] """ idx = self.pos[tup[1]] - # assuming the new_d is atmost old_d + # assuming the new_d is at most old_d self.array[idx] = (new_d, tup[1]) while idx > 0 and self.array[self.par(idx)][0] > self.array[idx][0]: self.swap(idx, self.par(idx)) diff --git a/project_euler/problem_047/sol1.py b/project_euler/problem_047/sol1.py index c9c44a9832dd..4ecd4f4b44c1 100644 --- a/project_euler/problem_047/sol1.py +++ b/project_euler/problem_047/sol1.py @@ -58,7 +58,7 @@ def upf_len(num: int) -> int: def equality(iterable: list) -> bool: """ - Check equality of ALL elements in an interable. + Check equality of ALL elements in an iterable >>> equality([1, 2, 3, 4]) False >>> equality([2, 2, 2, 2]) diff --git a/pyproject.toml b/pyproject.toml index 5b8ce4e72dfd..429f4fab9a52 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -56,6 +56,24 @@ lint.select = [ # "TCH", # flake8-type-checking # "TRY", # tryceratops ] +lint.ignore = [ + # `ruff rule S101` for a description of that rule + "B904", # Within an `except` clause, raise exceptions with `raise ... from err` -- FIX ME + "B905", # `zip()` without an explicit `strict=` parameter -- FIX ME + "EM101", # Exception must not use a string literal, assign to variable first + "EXE001", # Shebang is present but file is not executable -- DO NOT FIX + "G004", # Logging statement uses f-string + "PLC1901", # `{}` can be simplified to `{}` as an empty string is falsey + "PLW060", # Using global for `{name}` but no assignment is done -- DO NOT FIX + "PLW2901", # PLW2901: Redefined loop variable -- FIX ME + "PT011", # `pytest.raises(Exception)` is too broad, set the `match` parameter or use a more specific exception + "PT018", # Assertion should be broken down into multiple parts + "S101", # Use of `assert` detected -- DO NOT FIX + "S311", # Standard pseudo-random generators are not suitable for cryptographic purposes -- FIX ME + "SLF001", # Private member accessed: `_Iterator` -- FIX ME + "UP038", # Use `X | Y` in `{}` call instead of `(X, Y)` -- DO NOT FIX +] + lint.per-file-ignores."arithmetic_analysis/newton_raphson.py" = [ "PGH001", ] @@ -96,23 +114,6 @@ lint.pylint.max-args = 10 # default: 5 lint.pylint.max-branches = 20 # default: 12 lint.pylint.max-returns = 8 # default: 6 lint.pylint.max-statements = 88 # default: 50 -lint.ignore = [ - # `ruff rule S101` for a description of that rule - "B904", # Within an `except` clause, raise exceptions with `raise ... from err` -- FIX ME - "B905", # `zip()` without an explicit `strict=` parameter -- FIX ME - "EM101", # Exception must not use a string literal, assign to variable first - "EXE001", # Shebang is present but file is not executable -- DO NOT FIX - "G004", # Logging statement uses f-string - "PLC1901", # `{}` can be simplified to `{}` as an empty string is falsey - "PLW060", # Using global for `{name}` but no assignment is done -- DO NOT FIX - "PLW2901", # PLW2901: Redefined loop variable -- FIX ME - "PT011", # `pytest.raises(Exception)` is too broad, set the `match` parameter or use a more specific exception - "PT018", # Assertion should be broken down into multiple parts - "S101", # Use of `assert` detected -- DO NOT FIX - "S311", # Standard pseudo-random generators are not suitable for cryptographic purposes -- FIX ME - "SLF001", # Private member accessed: `_Iterator` -- FIX ME - "UP038", # Use `X | Y` in `{}` call instead of `(X, Y)` -- DO NOT FIX -] [tool.codespell] ignore-words-list = "3rt,ans,bitap,crate,damon,fo,followings,hist,iff,kwanza,manuel,mater,secant,som,sur,tim,toi,zar" From b8afb214f8c8d185dc42dafb9676becf512ca7fa Mon Sep 17 00:00:00 2001 From: Marco-campione-github <80974790+Marco-campione-github@users.noreply.github.com> Date: Fri, 31 May 2024 10:11:09 +0200 Subject: [PATCH 621/757] Changed the N to self.N in show_data in segment_tree.py (#11276) --- data_structures/binary_tree/segment_tree.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data_structures/binary_tree/segment_tree.py b/data_structures/binary_tree/segment_tree.py index c7069b3f6069..084fcf84955d 100644 --- a/data_structures/binary_tree/segment_tree.py +++ b/data_structures/binary_tree/segment_tree.py @@ -98,7 +98,7 @@ def query_recursive(self, idx, left, right, a, b): def show_data(self): show_list = [] - for i in range(1, N + 1): + for i in range(1, self.N + 1): show_list += [self.query(i, i)] print(show_list) From 70bd06db4642a2323ff397b041d40bc95ed6a5bf Mon Sep 17 00:00:00 2001 From: Pedram_Mohajer <48964282+pedram-mohajer@users.noreply.github.com> Date: Sat, 1 Jun 2024 05:09:03 -0400 Subject: [PATCH 622/757] add doctest/document to actual_power and document to power (#11187) * Update power.py * Update divide_and_conquer/power.py --------- Co-authored-by: Tianyi Zheng --- divide_and_conquer/power.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/divide_and_conquer/power.py b/divide_and_conquer/power.py index f2e023afd536..faf6a3476d40 100644 --- a/divide_and_conquer/power.py +++ b/divide_and_conquer/power.py @@ -2,6 +2,20 @@ def actual_power(a: int, b: int): """ Function using divide and conquer to calculate a^b. It only works for integer a,b. + + :param a: The base of the power operation, an integer. + :param b: The exponent of the power operation, a non-negative integer. + :return: The result of a^b. + + Examples: + >>> actual_power(3, 2) + 9 + >>> actual_power(5, 3) + 125 + >>> actual_power(2, 5) + 32 + >>> actual_power(7, 0) + 1 """ if b == 0: return 1 @@ -13,6 +27,10 @@ def actual_power(a: int, b: int): def power(a: int, b: int) -> float: """ + :param a: The base (integer). + :param b: The exponent (integer). + :return: The result of a^b, as a float for negative exponents. + >>> power(4,6) 4096 >>> power(2,3) From 723cf9c42839c47e9e6fb83362a7391177355505 Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Sat, 1 Jun 2024 02:17:07 -0700 Subject: [PATCH 623/757] Remove duplicate implementation of median of two arrays algorithm (#11420) * Remove duplicate implementation of median of two arrays algorithm Remove maths/median_of_two_arrays.py because the repo has two implementations of this algorithm, with data_structures/arrays/median_two_array.py being the other. Even though maths/median_of_two_arrays.py is the older implementation, the newer implementation is better documented, has better error handling, and is already located in a more appropriate directory. * updating DIRECTORY.md --------- Co-authored-by: tianyizheng02 --- DIRECTORY.md | 1 - maths/median_of_two_arrays.py | 33 --------------------------------- 2 files changed, 34 deletions(-) delete mode 100644 maths/median_of_two_arrays.py diff --git a/DIRECTORY.md b/DIRECTORY.md index 4a053a3f1b7f..2094fc3a980e 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -661,7 +661,6 @@ * [Manhattan Distance](maths/manhattan_distance.py) * [Matrix Exponentiation](maths/matrix_exponentiation.py) * [Max Sum Sliding Window](maths/max_sum_sliding_window.py) - * [Median Of Two Arrays](maths/median_of_two_arrays.py) * [Minkowski Distance](maths/minkowski_distance.py) * [Mobius Function](maths/mobius_function.py) * [Modular Division](maths/modular_division.py) diff --git a/maths/median_of_two_arrays.py b/maths/median_of_two_arrays.py deleted file mode 100644 index 55aa587a9c4b..000000000000 --- a/maths/median_of_two_arrays.py +++ /dev/null @@ -1,33 +0,0 @@ -from __future__ import annotations - - -def median_of_two_arrays(nums1: list[float], nums2: list[float]) -> float: - """ - >>> median_of_two_arrays([1, 2], [3]) - 2 - >>> median_of_two_arrays([0, -1.1], [2.5, 1]) - 0.5 - >>> median_of_two_arrays([], [2.5, 1]) - 1.75 - >>> median_of_two_arrays([], [0]) - 0 - >>> median_of_two_arrays([], []) - Traceback (most recent call last): - ... - IndexError: list index out of range - """ - all_numbers = sorted(nums1 + nums2) - div, mod = divmod(len(all_numbers), 2) - if mod == 1: - return all_numbers[div] - else: - return (all_numbers[div] + all_numbers[div - 1]) / 2 - - -if __name__ == "__main__": - import doctest - - doctest.testmod() - array_1 = [float(x) for x in input("Enter the elements of first array: ").split()] - array_2 = [float(x) for x in input("Enter the elements of second array: ").split()] - print(f"The median of two arrays is: {median_of_two_arrays(array_1, array_2)}") From edee8e644b09a21a1f70d3a59d57feed51c74004 Mon Sep 17 00:00:00 2001 From: Vishal Kumar Gupta Date: Sun, 2 Jun 2024 02:41:40 +0100 Subject: [PATCH 624/757] use format to remove '0b' (#11307) * use format to remove '0b' * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix: error message for float input --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- bit_manipulation/binary_and_operator.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bit_manipulation/binary_and_operator.py b/bit_manipulation/binary_and_operator.py index 36f6c668d9b3..f33b8b1c0ab4 100644 --- a/bit_manipulation/binary_and_operator.py +++ b/bit_manipulation/binary_and_operator.py @@ -26,7 +26,7 @@ def binary_and(a: int, b: int) -> str: >>> binary_and(0, 1.1) Traceback (most recent call last): ... - TypeError: 'float' object cannot be interpreted as an integer + ValueError: Unknown format code 'b' for object of type 'float' >>> binary_and("0", "1") Traceback (most recent call last): ... @@ -35,8 +35,8 @@ def binary_and(a: int, b: int) -> str: if a < 0 or b < 0: raise ValueError("the value of both inputs must be positive") - a_binary = str(bin(a))[2:] # remove the leading "0b" - b_binary = str(bin(b))[2:] # remove the leading "0b" + a_binary = format(a, "b") + b_binary = format(b, "b") max_len = max(len(a_binary), len(b_binary)) From 2f1704dae579295ea2f47584ef80b4b321a284d7 Mon Sep 17 00:00:00 2001 From: Mandeep Singh <135956602+MannCode@users.noreply.github.com> Date: Sun, 2 Jun 2024 18:27:35 -0700 Subject: [PATCH 625/757] issue #11150 Ensure explicit column selection and data type setting in data reading process. (#11302) * issue #11150 Ensure explicit column selection and data type setting in data reading process. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- machine_learning/sequential_minimum_optimization.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/machine_learning/sequential_minimum_optimization.py b/machine_learning/sequential_minimum_optimization.py index 3abdd6ccbed8..2ebdeb764a80 100644 --- a/machine_learning/sequential_minimum_optimization.py +++ b/machine_learning/sequential_minimum_optimization.py @@ -463,7 +463,11 @@ def test_cancel_data(): with open(r"cancel_data.csv", "w") as f: f.write(content) - data = pd.read_csv(r"cancel_data.csv", header=None) + data = pd.read_csv( + "cancel_data.csv", + header=None, + dtype={0: str}, # Assuming the first column contains string data + ) # 1: pre-processing data del data[data.columns.tolist()[0]] From ffaa976f6c5a5de30e284ae2fc8122f40cd3fa6a Mon Sep 17 00:00:00 2001 From: Harsh buddhdev Date: Sun, 2 Jun 2024 23:00:26 -0400 Subject: [PATCH 626/757] Fixes #9943 (#10252) * added doctest for all_permutations.py * added doctest for all_subsequences.py * added doctest for all_subsequences.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * doctest added * updated * Update backtracking/all_subsequences.py --------- Co-authored-by: Harsh Buddhdev Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Tianyi Zheng --- backtracking/all_permutations.py | 36 ++++++++++++++++++++++ backtracking/all_subsequences.py | 52 +++++++++++++++++++++++++++++++- 2 files changed, 87 insertions(+), 1 deletion(-) diff --git a/backtracking/all_permutations.py b/backtracking/all_permutations.py index c483cd62c99b..f376e6fa0945 100644 --- a/backtracking/all_permutations.py +++ b/backtracking/all_permutations.py @@ -23,6 +23,42 @@ def create_state_space_tree( Creates a state space tree to iterate through each branch using DFS. We know that each state has exactly len(sequence) - index children. It terminates when it reaches the end of the given sequence. + + :param sequence: The input sequence for which permutations are generated. + :param current_sequence: The current permutation being built. + :param index: The current index in the sequence. + :param index_used: list to track which elements are used in permutation. + + Example 1: + >>> sequence = [1, 2, 3] + >>> current_sequence = [] + >>> index_used = [False, False, False] + >>> create_state_space_tree(sequence, current_sequence, 0, index_used) + [1, 2, 3] + [1, 3, 2] + [2, 1, 3] + [2, 3, 1] + [3, 1, 2] + [3, 2, 1] + + Example 2: + >>> sequence = ["A", "B", "C"] + >>> current_sequence = [] + >>> index_used = [False, False, False] + >>> create_state_space_tree(sequence, current_sequence, 0, index_used) + ['A', 'B', 'C'] + ['A', 'C', 'B'] + ['B', 'A', 'C'] + ['B', 'C', 'A'] + ['C', 'A', 'B'] + ['C', 'B', 'A'] + + Example 3: + >>> sequence = [1] + >>> current_sequence = [] + >>> index_used = [False] + >>> create_state_space_tree(sequence, current_sequence, 0, index_used) + [1] """ if index == len(sequence): diff --git a/backtracking/all_subsequences.py b/backtracking/all_subsequences.py index 7844a829d046..18696054eb7e 100644 --- a/backtracking/all_subsequences.py +++ b/backtracking/all_subsequences.py @@ -22,6 +22,56 @@ def create_state_space_tree( Creates a state space tree to iterate through each branch using DFS. We know that each state has exactly two children. It terminates when it reaches the end of the given sequence. + + :param sequence: The input sequence for which subsequences are generated. + :param current_subsequence: The current subsequence being built. + :param index: The current index in the sequence. + + Example: + >>> sequence = [3, 2, 1] + >>> current_subsequence = [] + >>> create_state_space_tree(sequence, current_subsequence, 0) + [] + [1] + [2] + [2, 1] + [3] + [3, 1] + [3, 2] + [3, 2, 1] + + >>> sequence = ["A", "B"] + >>> current_subsequence = [] + >>> create_state_space_tree(sequence, current_subsequence, 0) + [] + ['B'] + ['A'] + ['A', 'B'] + + >>> sequence = [] + >>> current_subsequence = [] + >>> create_state_space_tree(sequence, current_subsequence, 0) + [] + + >>> sequence = [1, 2, 3, 4] + >>> current_subsequence = [] + >>> create_state_space_tree(sequence, current_subsequence, 0) + [] + [4] + [3] + [3, 4] + [2] + [2, 4] + [2, 3] + [2, 3, 4] + [1] + [1, 4] + [1, 3] + [1, 3, 4] + [1, 2] + [1, 2, 4] + [1, 2, 3] + [1, 2, 3, 4] """ if index == len(sequence): @@ -35,7 +85,7 @@ def create_state_space_tree( if __name__ == "__main__": - seq: list[Any] = [3, 1, 2, 4] + seq: list[Any] = [1, 2, 3] generate_all_subsequences(seq) seq.clear() From c919579869ae9f57d6878336af6de6bc9a001c61 Mon Sep 17 00:00:00 2001 From: AtomicVar Date: Mon, 3 Jun 2024 11:15:01 +0800 Subject: [PATCH 627/757] Add KL divergence loss algorithm (#11238) * Add KL divergence loss algorithm * Apply suggestions from code review --------- Co-authored-by: Tianyi Zheng --- machine_learning/loss_functions.py | 34 ++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/machine_learning/loss_functions.py b/machine_learning/loss_functions.py index 16e5a3278b73..150035661eb7 100644 --- a/machine_learning/loss_functions.py +++ b/machine_learning/loss_functions.py @@ -629,6 +629,40 @@ def smooth_l1_loss(y_true: np.ndarray, y_pred: np.ndarray, beta: float = 1.0) -> return np.mean(loss) +def kullback_leibler_divergence(y_true: np.ndarray, y_pred: np.ndarray) -> float: + """ + Calculate the Kullback-Leibler divergence (KL divergence) loss between true labels + and predicted probabilities. + + KL divergence loss quantifies dissimilarity between true labels and predicted + probabilities. It's often used in training generative models. + + KL = Σ(y_true * ln(y_true / y_pred)) + + Reference: https://en.wikipedia.org/wiki/Kullback%E2%80%93Leibler_divergence + + Parameters: + - y_true: True class probabilities + - y_pred: Predicted class probabilities + + >>> true_labels = np.array([0.2, 0.3, 0.5]) + >>> predicted_probs = np.array([0.3, 0.3, 0.4]) + >>> kullback_leibler_divergence(true_labels, predicted_probs) + 0.030478754035472025 + >>> true_labels = np.array([0.2, 0.3, 0.5]) + >>> predicted_probs = np.array([0.3, 0.3, 0.4, 0.5]) + >>> kullback_leibler_divergence(true_labels, predicted_probs) + Traceback (most recent call last): + ... + ValueError: Input arrays must have the same length. + """ + if len(y_true) != len(y_pred): + raise ValueError("Input arrays must have the same length.") + + kl_loss = y_true * np.log(y_true / y_pred) + return np.sum(kl_loss) + + if __name__ == "__main__": import doctest From 5827aac79a36f0d43e9bd9f1c9ca11da07b2d623 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 3 Jun 2024 18:21:27 -0300 Subject: [PATCH 628/757] [pre-commit.ci] pre-commit autoupdate (#11430) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.4.5 → v0.4.7](https://github.com/astral-sh/ruff-pre-commit/compare/v0.4.5...v0.4.7) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 43bf547dec6e..a04f4f8b2165 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.4.5 + rev: v0.4.7 hooks: - id: ruff - id: ruff-format From 41a1cdf38d9cb1a14c9149d2d815efa2259679ef Mon Sep 17 00:00:00 2001 From: Yuri Batista Ishizawa Date: Tue, 11 Jun 2024 06:45:00 -0300 Subject: [PATCH 629/757] Add rainfall intensity calculation function (#11432) * Add rainfall intensity calculation function * chore: improve fuction and coefficient documentation * Update physics/rainfall_intensity.py --------- Co-authored-by: Tianyi Zheng --- physics/rainfall_intensity.py | 143 ++++++++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) create mode 100644 physics/rainfall_intensity.py diff --git a/physics/rainfall_intensity.py b/physics/rainfall_intensity.py new file mode 100644 index 000000000000..cee8d50ddc2f --- /dev/null +++ b/physics/rainfall_intensity.py @@ -0,0 +1,143 @@ +""" +Rainfall Intensity +================== +This module contains functions to calculate the intensity of +a rainfall event for a given duration and return period. + +This function uses the Sherman intensity-duration-frequency curve. + +References +---------- +- Aparicio, F. (1997): Fundamentos de Hidrología de Superficie. + Balderas, México, Limusa. 303 p. +- https://en.wikipedia.org/wiki/Intensity-duration-frequency_curve +""" + + +def rainfall_intensity( + coefficient_k: float, + coefficient_a: float, + coefficient_b: float, + coefficient_c: float, + return_period: float, + duration: float, +) -> float: + """ + Calculate the intensity of a rainfall event for a given duration and return period. + It's based on the Sherman intensity-duration-frequency curve: + + I = k * T^a / (D + b)^c + + where: + I = Intensity of the rainfall event [mm/h] + k, a, b, c = Coefficients obtained through statistical distribution adjust + T = Return period in years + D = Rainfall event duration in minutes + + Parameters + ---------- + coefficient_k : float + Coefficient obtained through statistical distribution adjust. + coefficient_a : float + Coefficient obtained through statistical distribution adjust. + coefficient_b : float + Coefficient obtained through statistical distribution adjust. + coefficient_c : float + Coefficient obtained through statistical distribution adjust. + return_period : float + Return period in years. + duration : float + Rainfall event duration in minutes. + + Returns + ------- + intensity : float + Intensity of the rainfall event in mm/h. + + Raises + ------ + ValueError + If any of the parameters are not positive. + + Examples + -------- + + >>> rainfall_intensity(1000, 0.2, 11.6, 0.81, 10, 60) + 49.83339231138578 + + >>> rainfall_intensity(1000, 0.2, 11.6, 0.81, 10, 30) + 77.36319588106228 + + >>> rainfall_intensity(1000, 0.2, 11.6, 0.81, 5, 60) + 43.382487747633625 + + >>> rainfall_intensity(0, 0.2, 11.6, 0.81, 10, 60) + Traceback (most recent call last): + ... + ValueError: All parameters must be positive. + + >>> rainfall_intensity(1000, -0.2, 11.6, 0.81, 10, 60) + Traceback (most recent call last): + ... + ValueError: All parameters must be positive. + + >>> rainfall_intensity(1000, 0.2, -11.6, 0.81, 10, 60) + Traceback (most recent call last): + ... + ValueError: All parameters must be positive. + + >>> rainfall_intensity(1000, 0.2, 11.6, -0.81, 10, 60) + Traceback (most recent call last): + ... + ValueError: All parameters must be positive. + + >>> rainfall_intensity(1000, 0, 11.6, 0.81, 10, 60) + Traceback (most recent call last): + ... + ValueError: All parameters must be positive. + + >>> rainfall_intensity(1000, 0.2, 0, 0.81, 10, 60) + Traceback (most recent call last): + ... + ValueError: All parameters must be positive. + + >>> rainfall_intensity(1000, 0.2, 11.6, 0, 10, 60) + Traceback (most recent call last): + ... + ValueError: All parameters must be positive. + + >>> rainfall_intensity(0, 0.2, 11.6, 0.81, 10, 60) + Traceback (most recent call last): + ... + ValueError: All parameters must be positive. + + >>> rainfall_intensity(1000, 0.2, 11.6, 0.81, 0, 60) + Traceback (most recent call last): + ... + ValueError: All parameters must be positive. + + >>> rainfall_intensity(1000, 0.2, 11.6, 0.81, 10, 0) + Traceback (most recent call last): + ... + ValueError: All parameters must be positive. + + """ + if ( + coefficient_k <= 0 + or coefficient_a <= 0 + or coefficient_b <= 0 + or coefficient_c <= 0 + or return_period <= 0 + or duration <= 0 + ): + raise ValueError("All parameters must be positive.") + intensity = (coefficient_k * (return_period**coefficient_a)) / ( + (duration + coefficient_b) ** coefficient_c + ) + return intensity + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 446742387e83f94f3d54ce640cb07004180130ee Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Thu, 13 Jun 2024 14:47:29 -0700 Subject: [PATCH 630/757] Fix grammar and spelling mistakes in sequential_minimum_optimization.py (#11427) --- .../sequential_minimum_optimization.py | 135 +++++++++--------- 1 file changed, 66 insertions(+), 69 deletions(-) diff --git a/machine_learning/sequential_minimum_optimization.py b/machine_learning/sequential_minimum_optimization.py index 2ebdeb764a80..625fc28fe60c 100644 --- a/machine_learning/sequential_minimum_optimization.py +++ b/machine_learning/sequential_minimum_optimization.py @@ -1,11 +1,9 @@ """ - Implementation of sequential minimal optimization (SMO) for support vector machines - (SVM). +Sequential minimal optimization (SMO) for support vector machines (SVM) - Sequential minimal optimization (SMO) is an algorithm for solving the quadratic - programming (QP) problem that arises during the training of support vector - machines. - It was invented by John Platt in 1998. +Sequential minimal optimization (SMO) is an algorithm for solving the quadratic +programming (QP) problem that arises during the training of SVMs. It was invented by +John Platt in 1998. Input: 0: type: numpy.ndarray. @@ -124,8 +122,7 @@ def fit(self): b_old = self._b self._b = b - # 4: update error value,here we only calculate those non-bound samples' - # error + # 4: update error, here we only calculate the error for non-bound samples self._unbound = [i for i in self._all_samples if self._is_unbound(i)] for s in self.unbound: if s in (i1, i2): @@ -136,7 +133,7 @@ def fit(self): + (self._b - b_old) ) - # if i1 or i2 is non-bound,update there error value to zero + # if i1 or i2 is non-bound, update their error value to zero if self._is_unbound(i1): self._error[i1] = 0 if self._is_unbound(i2): @@ -161,7 +158,7 @@ def predict(self, test_samples, classify=True): results.append(result) return np.array(results) - # Check if alpha violate KKT condition + # Check if alpha violates the KKT condition def _check_obey_kkt(self, index): alphas = self.alphas tol = self._tol @@ -172,20 +169,19 @@ def _check_obey_kkt(self, index): # Get value calculated from kernel function def _k(self, i1, i2): - # for test samples,use Kernel function + # for test samples, use kernel function if isinstance(i2, np.ndarray): return self.Kernel(self.samples[i1], i2) - # for train samples,Kernel values have been saved in matrix + # for training samples, kernel values have been saved in matrix else: return self._K_matrix[i1, i2] - # Get sample's error + # Get error for sample def _e(self, index): """ Two cases: - 1:Sample[index] is non-bound,Fetch error from list: _error - 2:sample[index] is bound,Use predicted value deduct true value: g(xi) - yi - + 1: Sample[index] is non-bound, fetch error from list: _error + 2: sample[index] is bound, use predicted value minus true value: g(xi) - yi """ # get from error data if self._is_unbound(index): @@ -196,7 +192,7 @@ def _e(self, index): yi = self.tags[index] return gx - yi - # Calculate Kernel matrix of all possible i1,i2 ,saving time + # Calculate kernel matrix of all possible i1, i2, saving time def _calculate_k_matrix(self): k_matrix = np.zeros([self.length, self.length]) for i in self._all_samples: @@ -206,7 +202,7 @@ def _calculate_k_matrix(self): ) return k_matrix - # Predict test sample's tag + # Predict tag for test sample def _predict(self, sample): k = self._k predicted_value = ( @@ -222,30 +218,31 @@ def _predict(self, sample): # Choose alpha1 and alpha2 def _choose_alphas(self): - locis = yield from self._choose_a1() - if not locis: + loci = yield from self._choose_a1() + if not loci: return None - return locis + return loci def _choose_a1(self): """ - Choose first alpha ;steps: - 1:First loop over all sample - 2:Second loop over all non-bound samples till all non-bound samples does not - voilate kkt condition. - 3:Repeat this two process endlessly,till all samples does not voilate kkt - condition samples after first loop. + Choose first alpha + Steps: + 1: First loop over all samples + 2: Second loop over all non-bound samples until no non-bound samples violate + the KKT condition. + 3: Repeat these two processes until no samples violate the KKT condition + after the first loop. """ while True: all_not_obey = True # all sample - print("scanning all sample!") + print("Scanning all samples!") for i1 in [i for i in self._all_samples if self._check_obey_kkt(i)]: all_not_obey = False yield from self._choose_a2(i1) # non-bound sample - print("scanning non-bound sample!") + print("Scanning non-bound samples!") while True: not_obey = True for i1 in [ @@ -256,20 +253,21 @@ def _choose_a1(self): not_obey = False yield from self._choose_a2(i1) if not_obey: - print("all non-bound samples fit the KKT condition!") + print("All non-bound samples satisfy the KKT condition!") break if all_not_obey: - print("all samples fit the KKT condition! Optimization done!") + print("All samples satisfy the KKT condition!") break return False def _choose_a2(self, i1): """ - Choose the second alpha by using heuristic algorithm ;steps: - 1: Choose alpha2 which gets the maximum step size (|E1 - E2|). - 2: Start in a random point,loop over all non-bound samples till alpha1 and + Choose the second alpha using a heuristic algorithm + Steps: + 1: Choose alpha2 that maximizes the step size (|E1 - E2|). + 2: Start in a random point, loop over all non-bound samples till alpha1 and alpha2 are optimized. - 3: Start in a random point,loop over all samples till alpha1 and alpha2 are + 3: Start in a random point, loop over all samples till alpha1 and alpha2 are optimized. """ self._unbound = [i for i in self._all_samples if self._is_unbound(i)] @@ -306,7 +304,7 @@ def _get_new_alpha(self, i1, i2, a1, a2, e1, e2, y1, y2): if i1 == i2: return None, None - # calculate L and H which bound the new alpha2 + # calculate L and H which bound the new alpha2 s = y1 * y2 if s == -1: l, h = max(0.0, a2 - a1), min(self._c, self._c + a2 - a1) # noqa: E741 @@ -320,7 +318,7 @@ def _get_new_alpha(self, i1, i2, a1, a2, e1, e2, y1, y2): k22 = k(i2, i2) k12 = k(i1, i2) - # select the new alpha2 which could get the minimal objectives + # select the new alpha2 which could achieve the minimal objectives if (eta := k11 + k22 - 2.0 * k12) > 0.0: a2_new_unc = a2 + (y2 * (e1 - e2)) / eta # a2_new has a boundary @@ -335,7 +333,7 @@ def _get_new_alpha(self, i1, i2, a1, a2, e1, e2, y1, y2): l1 = a1 + s * (a2 - l) h1 = a1 + s * (a2 - h) - # way 1 + # Method 1 f1 = y1 * (e1 + b) - a1 * k(i1, i1) - s * a2 * k(i1, i2) f2 = y2 * (e2 + b) - a2 * k(i2, i2) - s * a1 * k(i1, i2) ol = ( @@ -353,9 +351,8 @@ def _get_new_alpha(self, i1, i2, a1, a2, e1, e2, y1, y2): + s * h * h1 * k(i1, i2) ) """ - # way 2 - Use objective function check which alpha2 new could get the minimal - objectives + Method 2: Use objective function to check which alpha2_new could achieve the + minimal objectives """ if ol < (oh - self._eps): a2_new = l @@ -375,7 +372,7 @@ def _get_new_alpha(self, i1, i2, a1, a2, e1, e2, y1, y2): return a1_new, a2_new - # Normalise data using min_max way + # Normalize data using min-max method def _norm(self, data): if self._init: self._min = np.min(data, axis=0) @@ -424,7 +421,7 @@ def _rbf(self, v1, v2): def _check(self): if self._kernel == self._rbf and self.gamma < 0: - raise ValueError("gamma value must greater than 0") + raise ValueError("gamma value must be non-negative") def _get_kernel(self, kernel_name): maps = {"linear": self._linear, "poly": self._polynomial, "rbf": self._rbf} @@ -444,27 +441,27 @@ def call_func(*args, **kwargs): start_time = time.time() func(*args, **kwargs) end_time = time.time() - print(f"smo algorithm cost {end_time - start_time} seconds") + print(f"SMO algorithm cost {end_time - start_time} seconds") return call_func @count_time -def test_cancel_data(): - print("Hello!\nStart test svm by smo algorithm!") +def test_cancer_data(): + print("Hello!\nStart test SVM using the SMO algorithm!") # 0: download dataset and load into pandas' dataframe - if not os.path.exists(r"cancel_data.csv"): + if not os.path.exists(r"cancer_data.csv"): request = urllib.request.Request( # noqa: S310 CANCER_DATASET_URL, headers={"User-Agent": "Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)"}, ) response = urllib.request.urlopen(request) # noqa: S310 content = response.read().decode("utf-8") - with open(r"cancel_data.csv", "w") as f: + with open(r"cancer_data.csv", "w") as f: f.write(content) data = pd.read_csv( - "cancel_data.csv", + "cancer_data.csv", header=None, dtype={0: str}, # Assuming the first column contains string data ) @@ -479,14 +476,14 @@ def test_cancel_data(): train_data, test_data = samples[:328, :], samples[328:, :] test_tags, test_samples = test_data[:, 0], test_data[:, 1:] - # 3: choose kernel function,and set initial alphas to zero(optional) - mykernel = Kernel(kernel="rbf", degree=5, coef0=1, gamma=0.5) + # 3: choose kernel function, and set initial alphas to zero (optional) + my_kernel = Kernel(kernel="rbf", degree=5, coef0=1, gamma=0.5) al = np.zeros(train_data.shape[0]) # 4: calculating best alphas using SMO algorithm and predict test_data samples mysvm = SmoSVM( train=train_data, - kernel_func=mykernel, + kernel_func=my_kernel, alpha_list=al, cost=0.4, b=0.0, @@ -501,30 +498,30 @@ def test_cancel_data(): for i in range(test_tags.shape[0]): if test_tags[i] == predict[i]: score += 1 - print(f"\nall: {test_num}\nright: {score}\nfalse: {test_num - score}") + print(f"\nAll: {test_num}\nCorrect: {score}\nIncorrect: {test_num - score}") print(f"Rough Accuracy: {score / test_tags.shape[0]}") def test_demonstration(): # change stdout - print("\nStart plot,please wait!!!") + print("\nStarting plot, please wait!") sys.stdout = open(os.devnull, "w") ax1 = plt.subplot2grid((2, 2), (0, 0)) ax2 = plt.subplot2grid((2, 2), (0, 1)) ax3 = plt.subplot2grid((2, 2), (1, 0)) ax4 = plt.subplot2grid((2, 2), (1, 1)) - ax1.set_title("linear svm,cost:0.1") + ax1.set_title("Linear SVM, cost = 0.1") test_linear_kernel(ax1, cost=0.1) - ax2.set_title("linear svm,cost:500") + ax2.set_title("Linear SVM, cost = 500") test_linear_kernel(ax2, cost=500) - ax3.set_title("rbf kernel svm,cost:0.1") + ax3.set_title("RBF kernel SVM, cost = 0.1") test_rbf_kernel(ax3, cost=0.1) - ax4.set_title("rbf kernel svm,cost:500") + ax4.set_title("RBF kernel SVM, cost = 500") test_rbf_kernel(ax4, cost=500) sys.stdout = sys.__stdout__ - print("Plot done!!!") + print("Plot done!") def test_linear_kernel(ax, cost): @@ -535,10 +532,10 @@ def test_linear_kernel(ax, cost): scaler = StandardScaler() train_x_scaled = scaler.fit_transform(train_x, train_y) train_data = np.hstack((train_y.reshape(500, 1), train_x_scaled)) - mykernel = Kernel(kernel="linear", degree=5, coef0=1, gamma=0.5) + my_kernel = Kernel(kernel="linear", degree=5, coef0=1, gamma=0.5) mysvm = SmoSVM( train=train_data, - kernel_func=mykernel, + kernel_func=my_kernel, cost=cost, tolerance=0.001, auto_norm=False, @@ -555,10 +552,10 @@ def test_rbf_kernel(ax, cost): scaler = StandardScaler() train_x_scaled = scaler.fit_transform(train_x, train_y) train_data = np.hstack((train_y.reshape(500, 1), train_x_scaled)) - mykernel = Kernel(kernel="rbf", degree=5, coef0=1, gamma=0.5) + my_kernel = Kernel(kernel="rbf", degree=5, coef0=1, gamma=0.5) mysvm = SmoSVM( train=train_data, - kernel_func=mykernel, + kernel_func=my_kernel, cost=cost, tolerance=0.001, auto_norm=False, @@ -571,11 +568,11 @@ def plot_partition_boundary( model, train_data, ax, resolution=100, colors=("b", "k", "r") ): """ - We can not get the optimum w of our kernel svm model which is different from linear - svm. For this reason, we generate randomly distributed points with high desity and - prediced values of these points are calculated by using our trained model. Then we - could use this prediced values to draw contour map. - And this contour map can represent svm's partition boundary. + We cannot get the optimal w of our kernel SVM model, which is different from a + linear SVM. For this reason, we generate randomly distributed points with high + density, and predicted values of these points are calculated using our trained + model. Then we could use this predicted values to draw contour map, and this contour + map represents the SVM's partition boundary. """ train_data_x = train_data[:, 1] train_data_y = train_data[:, 2] @@ -620,6 +617,6 @@ def plot_partition_boundary( if __name__ == "__main__": - test_cancel_data() + test_cancer_data() test_demonstration() plt.show() From af6a45e982213ef52a2f747dec6b58d668bfce5b Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Mon, 17 Jun 2024 00:19:32 +0300 Subject: [PATCH 631/757] Remove some per file ignores (#11381) * Remove some per file ignores * updating DIRECTORY.md * updating DIRECTORY.md --------- Co-authored-by: MaximSmolskiy --- DIRECTORY.md | 1 + pyproject.toml | 6 ------ 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index 2094fc3a980e..04551fad3685 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -863,6 +863,7 @@ * [Newtons Second Law Of Motion](physics/newtons_second_law_of_motion.py) * [Photoelectric Effect](physics/photoelectric_effect.py) * [Potential Energy](physics/potential_energy.py) + * [Rainfall Intensity](physics/rainfall_intensity.py) * [Reynolds Number](physics/reynolds_number.py) * [Rms Speed Of Molecule](physics/rms_speed_of_molecule.py) * [Shear Stress](physics/shear_stress.py) diff --git a/pyproject.toml b/pyproject.toml index 429f4fab9a52..bb8657183164 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -74,12 +74,6 @@ lint.ignore = [ "UP038", # Use `X | Y` in `{}` call instead of `(X, Y)` -- DO NOT FIX ] -lint.per-file-ignores."arithmetic_analysis/newton_raphson.py" = [ - "PGH001", -] -lint.per-file-ignores."data_structures/binary_tree/binary_search_tree_recursive.py" = [ - "BLE001", -] lint.per-file-ignores."data_structures/hashing/tests/test_hash_map.py" = [ "BLE001", ] From df94d460ac8d220f97851f358abc0102ae47d3db Mon Sep 17 00:00:00 2001 From: raj <64704676+ra230537@users.noreply.github.com> Date: Sun, 16 Jun 2024 19:17:55 -0300 Subject: [PATCH 632/757] Fix/fixes get top billionaries code (#11466) * fix: modify the depracated code and add new tests * fix: remove test from pr * fix: remove the useless utc import * fix: add explicit tz argument * fix: fixes ruff checking * Remove UP017 #noqa comments from code * Update get_top_billionaires.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update get_top_billionaires.py --------- Co-authored-by: Christian Clauss Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- web_programming/get_top_billionaires.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/web_programming/get_top_billionaires.py b/web_programming/get_top_billionaires.py index 24828b6d787c..99f6e0be948a 100644 --- a/web_programming/get_top_billionaires.py +++ b/web_programming/get_top_billionaires.py @@ -65,7 +65,7 @@ def get_forbes_real_time_billionaires() -> list[dict[str, int | str]]: "Country": person["countryOfCitizenship"], "Gender": person["gender"], "Worth ($)": f"{person['finalWorth'] / 1000:.1f} Billion", - "Age": years_old(person["birthDate"]), + "Age": str(years_old(person["birthDate"] / 1000)), } for person in response_json["personList"]["personsLists"] ] @@ -95,4 +95,7 @@ def display_billionaires(forbes_billionaires: list[dict[str, int | str]]) -> Non if __name__ == "__main__": + from doctest import testmod + + testmod() display_billionaires(get_forbes_real_time_billionaires()) From 31d1cd8402ba48aca26d9f1d2774f929610e7180 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 17 Jun 2024 08:31:32 -0400 Subject: [PATCH 633/757] [pre-commit.ci] pre-commit autoupdate (#11435) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/astral-sh/ruff-pre-commit: v0.4.7 → v0.4.8](https://github.com/astral-sh/ruff-pre-commit/compare/v0.4.7...v0.4.8) * Update .pre-commit-config.yaml --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a04f4f8b2165..fc8545b5159b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.4.7 + rev: v0.4.9 hooks: - id: ruff - id: ruff-format From 1cfca52db73ee18b9e9e08febe9e7d42f96e43db Mon Sep 17 00:00:00 2001 From: Snoppy Date: Mon, 17 Jun 2024 21:27:07 +0800 Subject: [PATCH 634/757] chore: fix typos (#11467) * chore: fix typos Signed-off-by: snoppy * Apply suggestions from code review Co-authored-by: Tianyi Zheng --------- Signed-off-by: snoppy Co-authored-by: Christian Clauss Co-authored-by: Tianyi Zheng --- computer_vision/haralick_descriptors.py | 2 +- graphs/strongly_connected_components.py | 2 +- maths/points_are_collinear_3d.py | 10 +++++----- neural_network/convolution_neural_network.py | 8 ++++---- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/computer_vision/haralick_descriptors.py b/computer_vision/haralick_descriptors.py index 712bd49668f8..634f0495797b 100644 --- a/computer_vision/haralick_descriptors.py +++ b/computer_vision/haralick_descriptors.py @@ -141,7 +141,7 @@ def transform( center_x, center_y = (x // 2 for x in kernel.shape) - # Use padded image when applying convolotion + # Use padded image when applying convolution # to not go out of bounds of the original the image transformed = np.zeros(image.shape, dtype=np.uint8) padded = np.pad(image, 1, "constant", constant_values=constant) diff --git a/graphs/strongly_connected_components.py b/graphs/strongly_connected_components.py index 325e5c1f33a3..4d4cf88035b5 100644 --- a/graphs/strongly_connected_components.py +++ b/graphs/strongly_connected_components.py @@ -38,7 +38,7 @@ def find_components( reversed_graph: dict[int, list[int]], vert: int, visited: list[bool] ) -> list[int]: """ - Use depth first search to find strongliy connected + Use depth first search to find strongly connected vertices. Now graph is reversed >>> find_components({0: [1], 1: [2], 2: [0]}, 0, 5 * [False]) [0, 1, 2] diff --git a/maths/points_are_collinear_3d.py b/maths/points_are_collinear_3d.py index 3bc0b3b9ebe5..c7adddda9494 100644 --- a/maths/points_are_collinear_3d.py +++ b/maths/points_are_collinear_3d.py @@ -76,9 +76,9 @@ def get_3d_vectors_cross(ab: Vector3d, ac: Vector3d) -> Vector3d: def is_zero_vector(vector: Vector3d, accuracy: int) -> bool: """ - Check if vector is equal to (0, 0, 0) of not. + Check if vector is equal to (0, 0, 0) or not. - Sine the algorithm is very accurate, we will never get a zero vector, + Since the algorithm is very accurate, we will never get a zero vector, so we need to round the vector axis, because we want a result that is either True or False. In other applications, we can return a float that represents the collinearity ratio. @@ -97,9 +97,9 @@ def are_collinear(a: Point3d, b: Point3d, c: Point3d, accuracy: int = 10) -> boo """ Check if three points are collinear or not. - 1- Create tow vectors AB and AC. - 2- Get the cross vector of the tow vectors. - 3- Calcolate the length of the cross vector. + 1- Create two vectors AB and AC. + 2- Get the cross vector of the two vectors. + 3- Calculate the length of the cross vector. 4- If the length is zero then the points are collinear, else they are not. The use of the accuracy parameter is explained in is_zero_vector docstring. diff --git a/neural_network/convolution_neural_network.py b/neural_network/convolution_neural_network.py index 3c551924442d..d4ac360a98de 100644 --- a/neural_network/convolution_neural_network.py +++ b/neural_network/convolution_neural_network.py @@ -1,7 +1,7 @@ """ - - - - - -- - - - - - - - - - - - - - - - - - - - - - - Name - - CNN - Convolution Neural Network For Photo Recognizing -Goal - - Recognize Handing Writing Word Photo +Goal - - Recognize Handwriting Word Photo Detail: Total 5 layers neural network * Convolution layer * Pooling layer @@ -135,7 +135,7 @@ def convolute(self, data, convs, w_convs, thre_convs, conv_step): ) data_featuremap.append(featuremap) - # expanding the data slice to One dimenssion + # expanding the data slice to one dimension focus1_list = [] for each_focus in data_focus: focus1_list.extend(self.Expand_Mat(each_focus)) @@ -304,7 +304,7 @@ def draw_error(): plt.grid(True, alpha=0.5) plt.show() - print("------------------Training Complished---------------------") + print("------------------Training Complete---------------------") print((" - - Training epoch: ", rp, f" - - Mse: {mse:.6f}")) if draw_e: draw_error() @@ -353,5 +353,5 @@ def convolution(self, data): if __name__ == "__main__": """ - I will put the example on other file + I will put the example in another file """ From 75b86671879cfbb83d241c3a3487b32c6dac9d91 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 25 Jun 2024 00:00:47 +0200 Subject: [PATCH 635/757] [pre-commit.ci] pre-commit autoupdate (#11472) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.4.9 → v0.4.10](https://github.com/astral-sh/ruff-pre-commit/compare/v0.4.9...v0.4.10) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index fc8545b5159b..1eddff7ab0e6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.4.9 + rev: v0.4.10 hooks: - id: ruff - id: ruff-format From 6882a8b80806f2dc53d53a0ecc00c2c98bec3fba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Vitor?= <92267577+ShiryuReb@users.noreply.github.com> Date: Wed, 26 Jun 2024 03:06:57 -0300 Subject: [PATCH 636/757] Tests/add new test case weight_conversion (#11468) * add new test * add new test --- conversions/weight_conversion.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/conversions/weight_conversion.py b/conversions/weight_conversion.py index e8326e0b688f..0777aead9f02 100644 --- a/conversions/weight_conversion.py +++ b/conversions/weight_conversion.py @@ -297,6 +297,12 @@ def weight_conversion(from_type: str, to_type: str, value: float) -> float: 1.660540199e-23 >>> weight_conversion("atomic-mass-unit","atomic-mass-unit",2) 1.999999998903455 + >>> weight_conversion("slug", "kilogram", 1) + Traceback (most recent call last): + ... + ValueError: Invalid 'from_type' or 'to_type' value: 'slug', 'kilogram' + Supported values are: kilogram, gram, milligram, metric-ton, long-ton, short-ton, \ +pound, stone, ounce, carrat, atomic-mass-unit """ if to_type not in KILOGRAM_CHART or from_type not in WEIGHT_TYPE_CHART: msg = ( From 716bdeb68b1e81aafe886e382319c6dab882dacc Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 2 Jul 2024 07:02:29 +0200 Subject: [PATCH 637/757] [pre-commit.ci] pre-commit autoupdate (#11473) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/astral-sh/ruff-pre-commit: v0.4.10 → v0.5.0](https://github.com/astral-sh/ruff-pre-commit/compare/v0.4.10...v0.5.0) - [github.com/pre-commit/mirrors-mypy: v1.10.0 → v1.10.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.10.0...v1.10.1) * Fix ruff issues * Fix ruff issues --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- .pre-commit-config.yaml | 5 ++- backtracking/knight_tour.py | 6 +-- data_structures/binary_tree/is_sorted.py | 6 +-- data_structures/binary_tree/red_black_tree.py | 37 +++++-------------- docs/source/__init__.py | 0 graphs/graph_adjacency_matrix.py | 8 ++-- graphs/multi_heuristic_astar.py | 4 +- graphs/tarjans_scc.py | 2 +- hashes/md5.py | 4 +- maths/radix2_fft.py | 1 - project_euler/problem_034/__init__.py | 1 - project_euler/problem_035/__init__.py | 1 - project_euler/problem_037/__init__.py | 1 - project_euler/problem_037/sol1.py | 9 ++--- project_euler/problem_039/__init__.py | 1 - project_euler/problem_041/__init__.py | 1 - project_euler/problem_043/__init__.py | 1 - project_euler/problem_044/__init__.py | 1 - project_euler/problem_045/__init__.py | 1 - project_euler/problem_046/__init__.py | 1 - project_euler/problem_055/__init__.py | 1 - project_euler/problem_058/__init__.py | 1 - project_euler/problem_063/__init__.py | 1 - project_euler/problem_072/sol1.py | 2 +- project_euler/problem_089/__init__.py | 1 - project_euler/problem_097/__init__.py | 1 - searches/binary_tree_traversal.py | 6 +-- sorts/external_sort.py | 5 +-- source/__init__.py | 0 .../can_string_be_rearranged_as_palindrome.py | 4 +- strings/is_valid_email_address.py | 4 +- strings/text_justification.py | 12 +++--- 32 files changed, 44 insertions(+), 85 deletions(-) create mode 100644 docs/source/__init__.py create mode 100644 source/__init__.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1eddff7ab0e6..a3f5a5e51855 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.4.10 + rev: v0.5.0 hooks: - id: ruff - id: ruff-format @@ -47,10 +47,11 @@ repos: - id: validate-pyproject - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.10.0 + rev: v1.10.1 hooks: - id: mypy args: + - --explicit-package-bases - --ignore-missing-imports - --install-types # See mirrors-mypy README.md - --non-interactive diff --git a/backtracking/knight_tour.py b/backtracking/knight_tour.py index 5f7dee8d97bf..8906aaa1094c 100644 --- a/backtracking/knight_tour.py +++ b/backtracking/knight_tour.py @@ -24,10 +24,10 @@ def get_valid_pos(position: tuple[int, int], n: int) -> list[tuple[int, int]]: ] permissible_positions = [] - for position in positions: - y_test, x_test = position + for inner_position in positions: + y_test, x_test = inner_position if 0 <= y_test < n and 0 <= x_test < n: - permissible_positions.append(position) + permissible_positions.append(inner_position) return permissible_positions diff --git a/data_structures/binary_tree/is_sorted.py b/data_structures/binary_tree/is_sorted.py index 509a426611e5..91fc8ca82633 100644 --- a/data_structures/binary_tree/is_sorted.py +++ b/data_structures/binary_tree/is_sorted.py @@ -80,9 +80,9 @@ def is_sorted(self) -> bool: """ if self.left and (self.data < self.left.data or not self.left.is_sorted): return False - if self.right and (self.data > self.right.data or not self.right.is_sorted): - return False - return True + return not ( + self.right and (self.data > self.right.data or not self.right.is_sorted) + ) if __name__ == "__main__": diff --git a/data_structures/binary_tree/red_black_tree.py b/data_structures/binary_tree/red_black_tree.py index a9ecf897c701..752db1e7026c 100644 --- a/data_structures/binary_tree/red_black_tree.py +++ b/data_structures/binary_tree/red_black_tree.py @@ -1,8 +1,3 @@ -""" -psf/black : true -ruff : passed -""" - from __future__ import annotations from collections.abc import Iterator @@ -321,9 +316,7 @@ def check_coloring(self) -> bool: return False if self.left and not self.left.check_coloring(): return False - if self.right and not self.right.check_coloring(): - return False - return True + return not (self.right and not self.right.check_coloring()) def black_height(self) -> int | None: """Returns the number of black nodes from this node to the @@ -561,9 +554,7 @@ def test_rotations() -> bool: right_rot.right.right = RedBlackTree(10, parent=right_rot.right) right_rot.right.right.left = RedBlackTree(5, parent=right_rot.right.right) right_rot.right.right.right = RedBlackTree(20, parent=right_rot.right.right) - if tree != right_rot: - return False - return True + return tree == right_rot def test_insertion_speed() -> bool: @@ -606,13 +597,11 @@ def test_insert_and_search() -> bool: tree.insert(12) tree.insert(10) tree.insert(11) - if 5 in tree or -6 in tree or -10 in tree or 13 in tree: + if any(i in tree for i in (5, -6, -10, 13)): # Found something not in there return False - if not (11 in tree and 12 in tree and -8 in tree and 0 in tree): - # Didn't find something in there - return False - return True + # Find all these things in there + return all(i in tree for i in (11, 12, -8, 0)) def test_insert_delete() -> bool: @@ -634,9 +623,7 @@ def test_insert_delete() -> bool: tree = tree.remove(9) if not tree.check_color_properties(): return False - if list(tree.inorder_traverse()) != [-8, 0, 4, 8, 10, 11, 12]: - return False - return True + return list(tree.inorder_traverse()) == [-8, 0, 4, 8, 10, 11, 12] def test_floor_ceil() -> bool: @@ -664,9 +651,7 @@ def test_min_max() -> bool: tree.insert(24) tree.insert(20) tree.insert(22) - if tree.get_max() != 22 or tree.get_min() != -16: - return False - return True + return not (tree.get_max() != 22 or tree.get_min() != -16) def test_tree_traversal() -> bool: @@ -682,9 +667,7 @@ def test_tree_traversal() -> bool: return False if list(tree.preorder_traverse()) != [0, -16, 16, 8, 22, 20, 24]: return False - if list(tree.postorder_traverse()) != [-16, 8, 20, 24, 22, 16, 0]: - return False - return True + return list(tree.postorder_traverse()) == [-16, 8, 20, 24, 22, 16, 0] def test_tree_chaining() -> bool: @@ -695,9 +678,7 @@ def test_tree_chaining() -> bool: return False if list(tree.preorder_traverse()) != [0, -16, 16, 8, 22, 20, 24]: return False - if list(tree.postorder_traverse()) != [-16, 8, 20, 24, 22, 16, 0]: - return False - return True + return list(tree.postorder_traverse()) == [-16, 8, 20, 24, 22, 16, 0] def print_results(msg: str, passes: bool) -> None: diff --git a/docs/source/__init__.py b/docs/source/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/graphs/graph_adjacency_matrix.py b/graphs/graph_adjacency_matrix.py index 059a6aa9ffb5..568c84166e4b 100644 --- a/graphs/graph_adjacency_matrix.py +++ b/graphs/graph_adjacency_matrix.py @@ -156,9 +156,11 @@ def remove_vertex(self, vertex: T) -> None: self.vertex_to_index.pop(vertex) # decrement indices for vertices shifted by the deleted vertex in the adj matrix - for vertex in self.vertex_to_index: - if self.vertex_to_index[vertex] >= start_index: - self.vertex_to_index[vertex] = self.vertex_to_index[vertex] - 1 + for inner_vertex in self.vertex_to_index: + if self.vertex_to_index[inner_vertex] >= start_index: + self.vertex_to_index[inner_vertex] = ( + self.vertex_to_index[inner_vertex] - 1 + ) def contains_vertex(self, vertex: T) -> bool: """ diff --git a/graphs/multi_heuristic_astar.py b/graphs/multi_heuristic_astar.py index 6af9a187a4e9..47509beb8efb 100644 --- a/graphs/multi_heuristic_astar.py +++ b/graphs/multi_heuristic_astar.py @@ -123,9 +123,7 @@ def do_something(back_pointer, goal, start): def valid(p: TPos): if p[0] < 0 or p[0] > n - 1: return False - if p[1] < 0 or p[1] > n - 1: - return False - return True + return not (p[1] < 0 or p[1] > n - 1) def expand_state( diff --git a/graphs/tarjans_scc.py b/graphs/tarjans_scc.py index a75dc4d2ca95..b4a3bd5c4c35 100644 --- a/graphs/tarjans_scc.py +++ b/graphs/tarjans_scc.py @@ -103,4 +103,4 @@ def create_graph(n: int, edges: list[tuple[int, int]]) -> list[list[int]]: edges = list(zip(source, target)) g = create_graph(n_vertices, edges) - assert [[5], [6], [4], [3, 2, 1, 0]] == tarjan(g) + assert tarjan(g) == [[5], [6], [4], [3, 2, 1, 0]] diff --git a/hashes/md5.py b/hashes/md5.py index 2187006ec8a9..622a50d290e1 100644 --- a/hashes/md5.py +++ b/hashes/md5.py @@ -82,8 +82,8 @@ def reformat_hex(i: int) -> bytes: hex_rep = format(i, "08x")[-8:] little_endian_hex = b"" - for i in [3, 2, 1, 0]: - little_endian_hex += hex_rep[2 * i : 2 * i + 2].encode("utf-8") + for j in [3, 2, 1, 0]: + little_endian_hex += hex_rep[2 * j : 2 * j + 2].encode("utf-8") return little_endian_hex diff --git a/maths/radix2_fft.py b/maths/radix2_fft.py index 2c5cdc004d1d..d41dc82d5588 100644 --- a/maths/radix2_fft.py +++ b/maths/radix2_fft.py @@ -84,7 +84,6 @@ def __dft(self, which): # Corner case if len(dft) <= 1: return dft[0] - # next_ncol = self.c_max_length // 2 while next_ncol > 0: new_dft = [[] for i in range(next_ncol)] diff --git a/project_euler/problem_034/__init__.py b/project_euler/problem_034/__init__.py index 792d6005489e..e69de29bb2d1 100644 --- a/project_euler/problem_034/__init__.py +++ b/project_euler/problem_034/__init__.py @@ -1 +0,0 @@ -# diff --git a/project_euler/problem_035/__init__.py b/project_euler/problem_035/__init__.py index 792d6005489e..e69de29bb2d1 100644 --- a/project_euler/problem_035/__init__.py +++ b/project_euler/problem_035/__init__.py @@ -1 +0,0 @@ -# diff --git a/project_euler/problem_037/__init__.py b/project_euler/problem_037/__init__.py index 792d6005489e..e69de29bb2d1 100644 --- a/project_euler/problem_037/__init__.py +++ b/project_euler/problem_037/__init__.py @@ -1 +0,0 @@ -# diff --git a/project_euler/problem_037/sol1.py b/project_euler/problem_037/sol1.py index 9c09065f4bd0..c66eb9fb1735 100644 --- a/project_euler/problem_037/sol1.py +++ b/project_euler/problem_037/sol1.py @@ -85,11 +85,10 @@ def validate(n: int) -> bool: >>> validate(3797) True """ - if len(str(n)) > 3 and ( - not is_prime(int(str(n)[-3:])) or not is_prime(int(str(n)[:3])) - ): - return False - return True + return not ( + len(str(n)) > 3 + and (not is_prime(int(str(n)[-3:])) or not is_prime(int(str(n)[:3]))) + ) def compute_truncated_primes(count: int = 11) -> list[int]: diff --git a/project_euler/problem_039/__init__.py b/project_euler/problem_039/__init__.py index 792d6005489e..e69de29bb2d1 100644 --- a/project_euler/problem_039/__init__.py +++ b/project_euler/problem_039/__init__.py @@ -1 +0,0 @@ -# diff --git a/project_euler/problem_041/__init__.py b/project_euler/problem_041/__init__.py index 792d6005489e..e69de29bb2d1 100644 --- a/project_euler/problem_041/__init__.py +++ b/project_euler/problem_041/__init__.py @@ -1 +0,0 @@ -# diff --git a/project_euler/problem_043/__init__.py b/project_euler/problem_043/__init__.py index 792d6005489e..e69de29bb2d1 100644 --- a/project_euler/problem_043/__init__.py +++ b/project_euler/problem_043/__init__.py @@ -1 +0,0 @@ -# diff --git a/project_euler/problem_044/__init__.py b/project_euler/problem_044/__init__.py index 792d6005489e..e69de29bb2d1 100644 --- a/project_euler/problem_044/__init__.py +++ b/project_euler/problem_044/__init__.py @@ -1 +0,0 @@ -# diff --git a/project_euler/problem_045/__init__.py b/project_euler/problem_045/__init__.py index 792d6005489e..e69de29bb2d1 100644 --- a/project_euler/problem_045/__init__.py +++ b/project_euler/problem_045/__init__.py @@ -1 +0,0 @@ -# diff --git a/project_euler/problem_046/__init__.py b/project_euler/problem_046/__init__.py index 792d6005489e..e69de29bb2d1 100644 --- a/project_euler/problem_046/__init__.py +++ b/project_euler/problem_046/__init__.py @@ -1 +0,0 @@ -# diff --git a/project_euler/problem_055/__init__.py b/project_euler/problem_055/__init__.py index 792d6005489e..e69de29bb2d1 100644 --- a/project_euler/problem_055/__init__.py +++ b/project_euler/problem_055/__init__.py @@ -1 +0,0 @@ -# diff --git a/project_euler/problem_058/__init__.py b/project_euler/problem_058/__init__.py index 792d6005489e..e69de29bb2d1 100644 --- a/project_euler/problem_058/__init__.py +++ b/project_euler/problem_058/__init__.py @@ -1 +0,0 @@ -# diff --git a/project_euler/problem_063/__init__.py b/project_euler/problem_063/__init__.py index 792d6005489e..e69de29bb2d1 100644 --- a/project_euler/problem_063/__init__.py +++ b/project_euler/problem_063/__init__.py @@ -1 +0,0 @@ -# diff --git a/project_euler/problem_072/sol1.py b/project_euler/problem_072/sol1.py index 5a28be564556..f09db0673323 100644 --- a/project_euler/problem_072/sol1.py +++ b/project_euler/problem_072/sol1.py @@ -43,7 +43,7 @@ def solution(limit: int = 1_000_000) -> int: ind = np.arange(2 * i, limit + 1, i) # indexes for selection phi[ind] -= phi[ind] // i - return np.sum(phi[2 : limit + 1]) + return int(np.sum(phi[2 : limit + 1])) if __name__ == "__main__": diff --git a/project_euler/problem_089/__init__.py b/project_euler/problem_089/__init__.py index 792d6005489e..e69de29bb2d1 100644 --- a/project_euler/problem_089/__init__.py +++ b/project_euler/problem_089/__init__.py @@ -1 +0,0 @@ -# diff --git a/project_euler/problem_097/__init__.py b/project_euler/problem_097/__init__.py index 792d6005489e..e69de29bb2d1 100644 --- a/project_euler/problem_097/__init__.py +++ b/project_euler/problem_097/__init__.py @@ -1 +0,0 @@ -# diff --git a/searches/binary_tree_traversal.py b/searches/binary_tree_traversal.py index 4897ef17299c..47af57f7f94d 100644 --- a/searches/binary_tree_traversal.py +++ b/searches/binary_tree_traversal.py @@ -36,7 +36,7 @@ def build_tree() -> TreeNode: right_node = TreeNode(int(check)) node_found.right = right_node q.put(right_node) - raise + raise ValueError("Something went wrong") def pre_order(node: TreeNode) -> None: @@ -164,8 +164,8 @@ def level_order_actual(node: TreeNode) -> None: if node_dequeued.right: list_.append(node_dequeued.right) print() - for node in list_: - q.put(node) + for inner_node in list_: + q.put(inner_node) # iteration version diff --git a/sorts/external_sort.py b/sorts/external_sort.py index e6b0d47f79f5..3fa7cacc0592 100644 --- a/sorts/external_sort.py +++ b/sorts/external_sort.py @@ -77,10 +77,7 @@ def refresh(self): self.empty.add(i) self.files[i].close() - if len(self.empty) == self.num_buffers: - return False - - return True + return len(self.empty) != self.num_buffers def unshift(self, index): value = self.buffers[index] diff --git a/source/__init__.py b/source/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/strings/can_string_be_rearranged_as_palindrome.py b/strings/can_string_be_rearranged_as_palindrome.py index 21d653db1405..95cda8b72180 100644 --- a/strings/can_string_be_rearranged_as_palindrome.py +++ b/strings/can_string_be_rearranged_as_palindrome.py @@ -72,9 +72,7 @@ def can_string_be_rearranged_as_palindrome(input_str: str = "") -> bool: for character_count in character_freq_dict.values(): if character_count % 2: odd_char += 1 - if odd_char > 1: - return False - return True + return not odd_char > 1 def benchmark(input_str: str = "") -> None: diff --git a/strings/is_valid_email_address.py b/strings/is_valid_email_address.py index 205394f81297..c3bf7df7349d 100644 --- a/strings/is_valid_email_address.py +++ b/strings/is_valid_email_address.py @@ -101,9 +101,7 @@ def is_valid_email_address(email: str) -> bool: return False # (7.) Validate the placement of "." characters - if domain.startswith(".") or domain.endswith(".") or ".." in domain: - return False - return True + return not (domain.startswith(".") or domain.endswith(".") or ".." in domain) if __name__ == "__main__": diff --git a/strings/text_justification.py b/strings/text_justification.py index b0ef12231224..e025edcfe13f 100644 --- a/strings/text_justification.py +++ b/strings/text_justification.py @@ -67,19 +67,19 @@ def justify(line: list, width: int, max_width: int) -> str: answer = [] line: list[str] = [] width = 0 - for word in words: - if width + len(word) + len(line) <= max_width: + for inner_word in words: + if width + len(inner_word) + len(line) <= max_width: # keep adding words until we can fill out max_width # width = sum of length of all words (without overall_spaces_count) - # len(word) = length of current word + # len(inner_word) = length of current inner_word # len(line) = number of overall_spaces_count to insert between words - line.append(word) - width += len(word) + line.append(inner_word) + width += len(inner_word) else: # justify the line and add it to result answer.append(justify(line, width, max_width)) # reset new line and new width - line, width = [word], len(word) + line, width = [inner_word], len(inner_word) remaining_spaces = max_width - width - len(line) answer.append(" ".join(line) + (remaining_spaces + 1) * " ") return answer From c1dc8e97f7992c132c671da2da60da9d926d0fca Mon Sep 17 00:00:00 2001 From: Saurabh Mahapatra <98408932+its-100rabh@users.noreply.github.com> Date: Thu, 4 Jul 2024 23:46:24 +0530 Subject: [PATCH 638/757] Create count_vowels.py (#11474) * Create count_vowels.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- strings/count_vowels.py | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 strings/count_vowels.py diff --git a/strings/count_vowels.py b/strings/count_vowels.py new file mode 100644 index 000000000000..8a52b331c81b --- /dev/null +++ b/strings/count_vowels.py @@ -0,0 +1,34 @@ +def count_vowels(s: str) -> int: + """ + Count the number of vowels in a given string. + + :param s: Input string to count vowels in. + :return: Number of vowels in the input string. + + Examples: + >>> count_vowels("hello world") + 3 + >>> count_vowels("HELLO WORLD") + 3 + >>> count_vowels("123 hello world") + 3 + >>> count_vowels("") + 0 + >>> count_vowels("a quick brown fox") + 5 + >>> count_vowels("the quick BROWN fox") + 5 + >>> count_vowels("PYTHON") + 1 + """ + if not isinstance(s, str): + raise ValueError("Input must be a string") + + vowels = "aeiouAEIOU" + return sum(1 for char in s if char in vowels) + + +if __name__ == "__main__": + from doctest import testmod + + testmod() From 9190888f89c55d927881c7b08f6df361ab1b0af4 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 8 Jul 2024 22:55:30 +0200 Subject: [PATCH 639/757] [pre-commit.ci] pre-commit autoupdate (#11481) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/astral-sh/ruff-pre-commit: v0.5.0 → v0.5.1](https://github.com/astral-sh/ruff-pre-commit/compare/v0.5.0...v0.5.1) - [github.com/tox-dev/pyproject-fmt: 2.1.3 → 2.1.4](https://github.com/tox-dev/pyproject-fmt/compare/2.1.3...2.1.4) * updating DIRECTORY.md * grid = np.char.chararray((n, n)) --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] Co-authored-by: Christian Clauss --- .pre-commit-config.yaml | 4 ++-- DIRECTORY.md | 1 + graphs/multi_heuristic_astar.py | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a3f5a5e51855..7fd689adca3b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.5.0 + rev: v0.5.1 hooks: - id: ruff - id: ruff-format @@ -29,7 +29,7 @@ repos: - tomli - repo: https://github.com/tox-dev/pyproject-fmt - rev: "2.1.3" + rev: "2.1.4" hooks: - id: pyproject-fmt diff --git a/DIRECTORY.md b/DIRECTORY.md index 04551fad3685..54bb8f148c32 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -1260,6 +1260,7 @@ * [Can String Be Rearranged As Palindrome](strings/can_string_be_rearranged_as_palindrome.py) * [Capitalize](strings/capitalize.py) * [Check Anagrams](strings/check_anagrams.py) + * [Count Vowels](strings/count_vowels.py) * [Credit Card Validator](strings/credit_card_validator.py) * [Damerau Levenshtein Distance](strings/damerau_levenshtein_distance.py) * [Detecting English Programmatically](strings/detecting_english_programmatically.py) diff --git a/graphs/multi_heuristic_astar.py b/graphs/multi_heuristic_astar.py index 47509beb8efb..38b07e1ca675 100644 --- a/graphs/multi_heuristic_astar.py +++ b/graphs/multi_heuristic_astar.py @@ -79,7 +79,7 @@ def key(start: TPos, i: int, goal: TPos, g_function: dict[TPos, float]): def do_something(back_pointer, goal, start): - grid = np.chararray((n, n)) + grid = np.char.chararray((n, n)) for i in range(n): for j in range(n): grid[i][j] = "*" From 2d8f22ab615085d36c53346283528f33b18a3b6d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 15 Jul 2024 21:52:48 +0200 Subject: [PATCH 640/757] [pre-commit.ci] pre-commit autoupdate (#11489) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.5.1 → v0.5.2](https://github.com/astral-sh/ruff-pre-commit/compare/v0.5.1...v0.5.2) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7fd689adca3b..c72b55fdec44 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.5.1 + rev: v0.5.2 hooks: - id: ruff - id: ruff-format From d9ded0727a7a209bfcbf9bd81c5c75183cfd026f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 23 Jul 2024 10:40:10 +0200 Subject: [PATCH 641/757] [pre-commit.ci] pre-commit autoupdate (#11495) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/astral-sh/ruff-pre-commit: v0.5.2 → v0.5.4](https://github.com/astral-sh/ruff-pre-commit/compare/v0.5.2...v0.5.4) - [github.com/pre-commit/mirrors-mypy: v1.10.1 → v1.11.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.10.1...v1.11.0) * ruff rule PLR1714 Consider merging multiple comparisons * ruff rule RUF005 Consider `[*self.urls, "", "#"]` instead of concatenation * Update emails_from_url.py * Update emails_from_url.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- .pre-commit-config.yaml | 4 ++-- web_programming/emails_from_url.py | 7 +------ 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c72b55fdec44..e9f57a7b746a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.5.2 + rev: v0.5.4 hooks: - id: ruff - id: ruff-format @@ -47,7 +47,7 @@ repos: - id: validate-pyproject - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.10.1 + rev: v1.11.0 hooks: - id: mypy args: diff --git a/web_programming/emails_from_url.py b/web_programming/emails_from_url.py index 43fd78dcf5a4..d41dc4893608 100644 --- a/web_programming/emails_from_url.py +++ b/web_programming/emails_from_url.py @@ -31,12 +31,7 @@ def handle_starttag(self, tag: str, attrs: list[tuple[str, str | None]]) -> None # Check the list of defined attributes. for name, value in attrs: # If href is defined, not empty nor # print it and not already in urls. - if ( - name == "href" - and value != "#" - and value != "" - and value not in self.urls - ): + if name == "href" and value not in (*self.urls, "", "#"): url = parse.urljoin(self.domain, value) self.urls.append(url) From 146800307c5d2a4393d57b7c97c63b89a21abba1 Mon Sep 17 00:00:00 2001 From: Ihor Pryyma <83470037+Ihor-Pryyma@users.noreply.github.com> Date: Thu, 25 Jul 2024 18:56:31 +0300 Subject: [PATCH 642/757] Add doctests to interpolation_search.py (#11492) * Add doctests to interpolation_search.py * update docs * update tests * update tests 2 * clean code --- searches/interpolation_search.py | 139 ++++++++++++++++--------------- 1 file changed, 70 insertions(+), 69 deletions(-) diff --git a/searches/interpolation_search.py b/searches/interpolation_search.py index 0591788aa40b..cb3e0011d0da 100644 --- a/searches/interpolation_search.py +++ b/searches/interpolation_search.py @@ -3,13 +3,41 @@ """ -def interpolation_search(sorted_collection, item): - """Pure implementation of interpolation search algorithm in Python - Be careful collection must be ascending sorted, otherwise result will be - unpredictable - :param sorted_collection: some ascending sorted collection with comparable items - :param item: item value to search - :return: index of found item or None if item is not found +def interpolation_search(sorted_collection: list[int], item: int) -> int | None: + """ + Searches for an item in a sorted collection by interpolation search algorithm. + + Args: + sorted_collection: sorted list of integers + item: item value to search + + Returns: + int: The index of the found item, or None if the item is not found. + Examples: + >>> interpolation_search([1, 2, 3, 4, 5], 2) + 1 + >>> interpolation_search([1, 2, 3, 4, 5], 4) + 3 + >>> interpolation_search([1, 2, 3, 4, 5], 6) is None + True + >>> interpolation_search([], 1) is None + True + >>> interpolation_search([100], 100) + 0 + >>> interpolation_search([1, 2, 3, 4, 5], 0) is None + True + >>> interpolation_search([1, 2, 3, 4, 5], 7) is None + True + >>> interpolation_search([1, 2, 3, 4, 5], 2) + 1 + >>> interpolation_search([1, 2, 3, 4, 5], 0) is None + True + >>> interpolation_search([1, 2, 3, 4, 5], 7) is None + True + >>> interpolation_search([1, 2, 3, 4, 5], 2) + 1 + >>> interpolation_search([5, 5, 5, 5, 5], 3) is None + True """ left = 0 right = len(sorted_collection) - 1 @@ -19,8 +47,7 @@ def interpolation_search(sorted_collection, item): if sorted_collection[left] == sorted_collection[right]: if sorted_collection[left] == item: return left - else: - return None + return None point = left + ((item - sorted_collection[left]) * (right - left)) // ( sorted_collection[right] - sorted_collection[left] @@ -33,7 +60,7 @@ def interpolation_search(sorted_collection, item): current_item = sorted_collection[point] if current_item == item: return point - elif point < left: + if point < left: right = left left = point elif point > right: @@ -46,22 +73,42 @@ def interpolation_search(sorted_collection, item): return None -def interpolation_search_by_recursion(sorted_collection, item, left, right): +def interpolation_search_by_recursion( + sorted_collection: list[int], item: int, left: int = 0, right: int | None = None +) -> int | None: """Pure implementation of interpolation search algorithm in Python by recursion Be careful collection must be ascending sorted, otherwise result will be unpredictable First recursion should be started with left=0 and right=(len(sorted_collection)-1) - :param sorted_collection: some ascending sorted collection with comparable items - :param item: item value to search - :return: index of found item or None if item is not found - """ + Args: + sorted_collection: some sorted collection with comparable items + item: item value to search + left: left index in collection + right: right index in collection + + Returns: + index of item in collection or None if item is not present + + Examples: + >>> interpolation_search_by_recursion([0, 5, 7, 10, 15], 0) + 0 + >>> interpolation_search_by_recursion([0, 5, 7, 10, 15], 15) + 4 + >>> interpolation_search_by_recursion([0, 5, 7, 10, 15], 5) + 1 + >>> interpolation_search_by_recursion([0, 5, 7, 10, 15], 100) is None + True + >>> interpolation_search_by_recursion([5, 5, 5, 5, 5], 3) is None + True + """ + if right is None: + right = len(sorted_collection) - 1 # avoid divided by 0 during interpolation if sorted_collection[left] == sorted_collection[right]: if sorted_collection[left] == item: return left - else: - return None + return None point = left + ((item - sorted_collection[left]) * (right - left)) // ( sorted_collection[right] - sorted_collection[left] @@ -73,64 +120,18 @@ def interpolation_search_by_recursion(sorted_collection, item, left, right): if sorted_collection[point] == item: return point - elif point < left: + if point < left: return interpolation_search_by_recursion(sorted_collection, item, point, left) - elif point > right: + if point > right: return interpolation_search_by_recursion(sorted_collection, item, right, left) - elif sorted_collection[point] > item: + if sorted_collection[point] > item: return interpolation_search_by_recursion( sorted_collection, item, left, point - 1 ) - else: - return interpolation_search_by_recursion( - sorted_collection, item, point + 1, right - ) - - -def __assert_sorted(collection): - """Check if collection is ascending sorted, if not - raises :py:class:`ValueError` - :param collection: collection - :return: True if collection is ascending sorted - :raise: :py:class:`ValueError` if collection is not ascending sorted - Examples: - >>> __assert_sorted([0, 1, 2, 4]) - True - >>> __assert_sorted([10, -1, 5]) - Traceback (most recent call last): - ... - ValueError: Collection must be ascending sorted - """ - if collection != sorted(collection): - raise ValueError("Collection must be ascending sorted") - return True + return interpolation_search_by_recursion(sorted_collection, item, point + 1, right) if __name__ == "__main__": - import sys + import doctest - """ - user_input = input('Enter numbers separated by comma:\n').strip() - collection = [int(item) for item in user_input.split(',')] - try: - __assert_sorted(collection) - except ValueError: - sys.exit('Sequence must be ascending sorted to apply interpolation search') - - target_input = input('Enter a single number to be found in the list:\n') - target = int(target_input) - """ - - debug = 0 - if debug == 1: - collection = [10, 30, 40, 45, 50, 66, 77, 93] - try: - __assert_sorted(collection) - except ValueError: - sys.exit("Sequence must be ascending sorted to apply interpolation search") - target = 67 - - result = interpolation_search(collection, target) - if result is not None: - print(f"{target} found at positions: {result}") - else: - print("Not found") + doctest.testmod() From 240d1b7cd47df86d86b26f4d658b26e3656a27d9 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 29 Jul 2024 21:41:09 +0200 Subject: [PATCH 643/757] [pre-commit.ci] pre-commit autoupdate (#11500) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.5.4 → v0.5.5](https://github.com/astral-sh/ruff-pre-commit/compare/v0.5.4...v0.5.5) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e9f57a7b746a..09542dd7e255 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.5.4 + rev: v0.5.5 hooks: - id: ruff - id: ruff-format From dfe67954f7218703e3aadca1768a0ad4c97c73a1 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 6 Aug 2024 00:11:14 +0200 Subject: [PATCH 644/757] [pre-commit.ci] pre-commit autoupdate (#11507) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.5.5 → v0.5.6](https://github.com/astral-sh/ruff-pre-commit/compare/v0.5.5...v0.5.6) - [github.com/tox-dev/pyproject-fmt: 2.1.4 → 2.2.1](https://github.com/tox-dev/pyproject-fmt/compare/2.1.4...2.2.1) - [github.com/pre-commit/mirrors-mypy: v1.11.0 → v1.11.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.11.0...v1.11.1) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 09542dd7e255..c112b6d86da0 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.5.5 + rev: v0.5.6 hooks: - id: ruff - id: ruff-format @@ -29,7 +29,7 @@ repos: - tomli - repo: https://github.com/tox-dev/pyproject-fmt - rev: "2.1.4" + rev: "2.2.1" hooks: - id: pyproject-fmt @@ -47,7 +47,7 @@ repos: - id: validate-pyproject - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.11.0 + rev: v1.11.1 hooks: - id: mypy args: From ed1900f1b37234f25486cfb3223988b3295a5549 Mon Sep 17 00:00:00 2001 From: CarlosZamG <54159355+CarlosZamG@users.noreply.github.com> Date: Tue, 6 Aug 2024 02:44:58 -0600 Subject: [PATCH 645/757] Fix typo in integration_by_simpson_approx.py (#11501) --- maths/numerical_analysis/integration_by_simpson_approx.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maths/numerical_analysis/integration_by_simpson_approx.py b/maths/numerical_analysis/integration_by_simpson_approx.py index f77ae76135ee..934299997aac 100644 --- a/maths/numerical_analysis/integration_by_simpson_approx.py +++ b/maths/numerical_analysis/integration_by_simpson_approx.py @@ -4,7 +4,7 @@ Purpose : You have one function f(x) which takes float integer and returns float you have to integrate the function in limits a to b. -The approximation proposed by Thomas Simpsons in 1743 is one way to calculate +The approximation proposed by Thomas Simpson in 1743 is one way to calculate integration. ( read article : https://cp-algorithms.com/num_methods/simpson-integration.html ) From 31c424fc8654877d3731bdcb50dcc1ce5d6860ab Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 12 Aug 2024 22:55:46 +0200 Subject: [PATCH 646/757] [pre-commit.ci] pre-commit autoupdate (#11515) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.5.6 → v0.5.7](https://github.com/astral-sh/ruff-pre-commit/compare/v0.5.6...v0.5.7) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c112b6d86da0..c797af6c5088 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.5.6 + rev: v0.5.7 hooks: - id: ruff - id: ruff-format From 48418280b1331d1efaa14dc48da62d313dfcee43 Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Thu, 22 Aug 2024 09:42:40 -0700 Subject: [PATCH 647/757] Remove separate directory for `gaussian_elimination_pivoting.py` (#11445) * updating DIRECTORY.md * Remove separate directory for gaussian_elimination_pivoting.py Delete the directory linear_algebra/src/gaussian_elimination_pivoting/ and move its algorithm file, gaussian_elimination_pivoting.py, into the parent src/ directory. The gaussian_elimination_pivoting/ directory only exists because gaussian_elimination_pivoting.py reads an example numpy array from matrix.txt, but this input file and IO operation is entirely unnecessary because gaussian_elimination_pivoting.py already has the exact same array hard-coded into a variable. * updating DIRECTORY.md --------- Co-authored-by: tianyizheng02 --- DIRECTORY.md | 3 +- .../gaussian_elimination_pivoting.py | 33 ++++++++----------- .../gaussian_elimination_pivoting/__init__.py | 0 .../gaussian_elimination_pivoting/matrix.txt | 4 --- 4 files changed, 14 insertions(+), 26 deletions(-) rename linear_algebra/src/{gaussian_elimination_pivoting => }/gaussian_elimination_pivoting.py (83%) delete mode 100644 linear_algebra/src/gaussian_elimination_pivoting/__init__.py delete mode 100644 linear_algebra/src/gaussian_elimination_pivoting/matrix.txt diff --git a/DIRECTORY.md b/DIRECTORY.md index 54bb8f148c32..11de569a2c25 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -540,8 +540,7 @@ * [Lu Decomposition](linear_algebra/lu_decomposition.py) * Src * [Conjugate Gradient](linear_algebra/src/conjugate_gradient.py) - * Gaussian Elimination Pivoting - * [Gaussian Elimination Pivoting](linear_algebra/src/gaussian_elimination_pivoting/gaussian_elimination_pivoting.py) + * [Gaussian Elimination Pivoting](linear_algebra/src/gaussian_elimination_pivoting.py) * [Lib](linear_algebra/src/lib.py) * [Polynom For Points](linear_algebra/src/polynom_for_points.py) * [Power Iteration](linear_algebra/src/power_iteration.py) diff --git a/linear_algebra/src/gaussian_elimination_pivoting/gaussian_elimination_pivoting.py b/linear_algebra/src/gaussian_elimination_pivoting.py similarity index 83% rename from linear_algebra/src/gaussian_elimination_pivoting/gaussian_elimination_pivoting.py rename to linear_algebra/src/gaussian_elimination_pivoting.py index 2a86350e9fc6..ecaacce19a31 100644 --- a/linear_algebra/src/gaussian_elimination_pivoting/gaussian_elimination_pivoting.py +++ b/linear_algebra/src/gaussian_elimination_pivoting.py @@ -1,15 +1,5 @@ import numpy as np -matrix = np.array( - [ - [5.0, -5.0, -3.0, 4.0, -11.0], - [1.0, -4.0, 6.0, -4.0, -10.0], - [-2.0, -5.0, 4.0, -5.0, -12.0], - [-3.0, -3.0, 5.0, -5.0, 8.0], - ], - dtype=float, -) - def solve_linear_system(matrix: np.ndarray) -> np.ndarray: """ @@ -87,15 +77,18 @@ def solve_linear_system(matrix: np.ndarray) -> np.ndarray: if __name__ == "__main__": from doctest import testmod - from pathlib import Path testmod() - file_path = Path(__file__).parent / "matrix.txt" - try: - matrix = np.loadtxt(file_path) - except FileNotFoundError: - print(f"Error: {file_path} not found. Using default matrix instead.") - - # Example usage: - print(f"Matrix:\n{matrix}") - print(f"{solve_linear_system(matrix) = }") + + example_matrix = np.array( + [ + [5.0, -5.0, -3.0, 4.0, -11.0], + [1.0, -4.0, 6.0, -4.0, -10.0], + [-2.0, -5.0, 4.0, -5.0, -12.0], + [-3.0, -3.0, 5.0, -5.0, 8.0], + ], + dtype=float, + ) + + print(f"Matrix:\n{example_matrix}") + print(f"{solve_linear_system(example_matrix) = }") diff --git a/linear_algebra/src/gaussian_elimination_pivoting/__init__.py b/linear_algebra/src/gaussian_elimination_pivoting/__init__.py deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/linear_algebra/src/gaussian_elimination_pivoting/matrix.txt b/linear_algebra/src/gaussian_elimination_pivoting/matrix.txt deleted file mode 100644 index dd895ad856ee..000000000000 --- a/linear_algebra/src/gaussian_elimination_pivoting/matrix.txt +++ /dev/null @@ -1,4 +0,0 @@ -5.0 -5.0 -3.0 4.0 -11.0 -1.0 -4.0 6.0 -4.0 -10.0 --2.0 -5.0 4.0 -5.0 -12.0 --3.0 -3.0 5.0 -5.0 8.0 \ No newline at end of file From e3fa014a5ab4887f93aae7bb193b152bb155323a Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Sun, 25 Aug 2024 18:33:11 +0300 Subject: [PATCH 648/757] Fix ruff (#11527) * updating DIRECTORY.md * Fix ruff * Fix * Fix * Fix * Revert "Fix" This reverts commit 5bc3bf342208dd707da02dea7173c059317b6bc6. * find_max.py: noqa: PLR1730 --------- Co-authored-by: MaximSmolskiy Co-authored-by: Christian Clauss --- .pre-commit-config.yaml | 2 +- .../binary_tree/number_of_possible_binary_trees.py | 3 +-- divide_and_conquer/closest_pair_of_points.py | 6 ++---- graphs/kahns_algorithm_long.py | 3 +-- maths/find_max.py | 2 +- maths/special_numbers/bell_numbers.py | 3 +-- matrix/tests/test_matrix_operation.py | 12 ++++++------ project_euler/problem_008/sol1.py | 3 +-- project_euler/problem_009/sol2.py | 3 +-- project_euler/problem_011/sol1.py | 3 +-- project_euler/problem_011/sol2.py | 12 ++++-------- scheduling/highest_response_ratio_next.py | 3 +-- scheduling/shortest_job_first.py | 3 +-- 13 files changed, 22 insertions(+), 36 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c797af6c5088..06f8ba00494a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.5.7 + rev: v0.6.2 hooks: - id: ruff - id: ruff-format diff --git a/data_structures/binary_tree/number_of_possible_binary_trees.py b/data_structures/binary_tree/number_of_possible_binary_trees.py index 1c3dff37e7d9..b39cbafd0a61 100644 --- a/data_structures/binary_tree/number_of_possible_binary_trees.py +++ b/data_structures/binary_tree/number_of_possible_binary_trees.py @@ -31,8 +31,7 @@ def binomial_coefficient(n: int, k: int) -> int: """ result = 1 # To kept the Calculated Value # Since C(n, k) = C(n, n-k) - if k > (n - k): - k = n - k + k = min(k, n - k) # Calculate C(n,k) for i in range(k): result *= n - i diff --git a/divide_and_conquer/closest_pair_of_points.py b/divide_and_conquer/closest_pair_of_points.py index cb7fa00d1c8f..534cbba9b718 100644 --- a/divide_and_conquer/closest_pair_of_points.py +++ b/divide_and_conquer/closest_pair_of_points.py @@ -54,8 +54,7 @@ def dis_between_closest_pair(points, points_counts, min_dis=float("inf")): for i in range(points_counts - 1): for j in range(i + 1, points_counts): current_dis = euclidean_distance_sqr(points[i], points[j]) - if current_dis < min_dis: - min_dis = current_dis + min_dis = min(min_dis, current_dis) return min_dis @@ -76,8 +75,7 @@ def dis_between_closest_in_strip(points, points_counts, min_dis=float("inf")): for i in range(min(6, points_counts - 1), points_counts): for j in range(max(0, i - 6), i): current_dis = euclidean_distance_sqr(points[i], points[j]) - if current_dis < min_dis: - min_dis = current_dis + min_dis = min(min_dis, current_dis) return min_dis diff --git a/graphs/kahns_algorithm_long.py b/graphs/kahns_algorithm_long.py index 63cbeb909a8a..1f16b90c0745 100644 --- a/graphs/kahns_algorithm_long.py +++ b/graphs/kahns_algorithm_long.py @@ -17,8 +17,7 @@ def longest_distance(graph): for x in graph[vertex]: indegree[x] -= 1 - if long_dist[vertex] + 1 > long_dist[x]: - long_dist[x] = long_dist[vertex] + 1 + long_dist[x] = max(long_dist[x], long_dist[vertex] + 1) if indegree[x] == 0: queue.append(x) diff --git a/maths/find_max.py b/maths/find_max.py index 729a80ab421c..4765d300634e 100644 --- a/maths/find_max.py +++ b/maths/find_max.py @@ -20,7 +20,7 @@ def find_max_iterative(nums: list[int | float]) -> int | float: raise ValueError("find_max_iterative() arg is an empty sequence") max_num = nums[0] for x in nums: - if x > max_num: + if x > max_num: # noqa: PLR1730 max_num = x return max_num diff --git a/maths/special_numbers/bell_numbers.py b/maths/special_numbers/bell_numbers.py index 660ec6e6aa09..5d99334d7add 100644 --- a/maths/special_numbers/bell_numbers.py +++ b/maths/special_numbers/bell_numbers.py @@ -61,8 +61,7 @@ def _binomial_coefficient(total_elements: int, elements_to_choose: int) -> int: if elements_to_choose in {0, total_elements}: return 1 - if elements_to_choose > total_elements - elements_to_choose: - elements_to_choose = total_elements - elements_to_choose + elements_to_choose = min(elements_to_choose, total_elements - elements_to_choose) coefficient = 1 for i in range(elements_to_choose): diff --git a/matrix/tests/test_matrix_operation.py b/matrix/tests/test_matrix_operation.py index addc870ca205..21ed7e371fd8 100644 --- a/matrix/tests/test_matrix_operation.py +++ b/matrix/tests/test_matrix_operation.py @@ -31,7 +31,7 @@ logger.addHandler(stream_handler) -@pytest.mark.mat_ops() +@pytest.mark.mat_ops @pytest.mark.parametrize( ("mat1", "mat2"), [(mat_a, mat_b), (mat_c, mat_d), (mat_d, mat_e), (mat_f, mat_h)] ) @@ -51,7 +51,7 @@ def test_addition(mat1, mat2): matop.add(mat1, mat2) -@pytest.mark.mat_ops() +@pytest.mark.mat_ops @pytest.mark.parametrize( ("mat1", "mat2"), [(mat_a, mat_b), (mat_c, mat_d), (mat_d, mat_e), (mat_f, mat_h)] ) @@ -71,7 +71,7 @@ def test_subtraction(mat1, mat2): assert matop.subtract(mat1, mat2) -@pytest.mark.mat_ops() +@pytest.mark.mat_ops @pytest.mark.parametrize( ("mat1", "mat2"), [(mat_a, mat_b), (mat_c, mat_d), (mat_d, mat_e), (mat_f, mat_h)] ) @@ -93,21 +93,21 @@ def test_multiplication(mat1, mat2): assert matop.subtract(mat1, mat2) -@pytest.mark.mat_ops() +@pytest.mark.mat_ops def test_scalar_multiply(): act = (3.5 * np.array(mat_a)).tolist() theo = matop.scalar_multiply(mat_a, 3.5) assert theo == act -@pytest.mark.mat_ops() +@pytest.mark.mat_ops def test_identity(): act = (np.identity(5)).tolist() theo = matop.identity(5) assert theo == act -@pytest.mark.mat_ops() +@pytest.mark.mat_ops @pytest.mark.parametrize("mat", [mat_a, mat_b, mat_c, mat_d, mat_e, mat_f]) def test_transpose(mat): if (np.array(mat)).shape < (2, 2): diff --git a/project_euler/problem_008/sol1.py b/project_euler/problem_008/sol1.py index adbac8d5ad1f..a38b2045f996 100644 --- a/project_euler/problem_008/sol1.py +++ b/project_euler/problem_008/sol1.py @@ -75,8 +75,7 @@ def solution(n: str = N) -> int: product = 1 for j in range(13): product *= int(n[i + j]) - if product > largest_product: - largest_product = product + largest_product = max(largest_product, product) return largest_product diff --git a/project_euler/problem_009/sol2.py b/project_euler/problem_009/sol2.py index 722ad522ee45..443a529571cc 100644 --- a/project_euler/problem_009/sol2.py +++ b/project_euler/problem_009/sol2.py @@ -39,8 +39,7 @@ def solution(n: int = 1000) -> int: c = n - a - b if c * c == (a * a + b * b): candidate = a * b * c - if candidate >= product: - product = candidate + product = max(product, candidate) return product diff --git a/project_euler/problem_011/sol1.py b/project_euler/problem_011/sol1.py index ad45f0983a7c..3d3e864f927b 100644 --- a/project_euler/problem_011/sol1.py +++ b/project_euler/problem_011/sol1.py @@ -63,8 +63,7 @@ def largest_product(grid): max_product = max( vert_product, horz_product, lr_diag_product, rl_diag_product ) - if max_product > largest: - largest = max_product + largest = max(largest, max_product) return largest diff --git a/project_euler/problem_011/sol2.py b/project_euler/problem_011/sol2.py index 09bf315702c5..7637deafc3cb 100644 --- a/project_euler/problem_011/sol2.py +++ b/project_euler/problem_011/sol2.py @@ -45,15 +45,13 @@ def solution(): for i in range(20): for j in range(17): temp = grid[i][j] * grid[i][j + 1] * grid[i][j + 2] * grid[i][j + 3] - if temp > maximum: - maximum = temp + maximum = max(maximum, temp) # down for i in range(17): for j in range(20): temp = grid[i][j] * grid[i + 1][j] * grid[i + 2][j] * grid[i + 3][j] - if temp > maximum: - maximum = temp + maximum = max(maximum, temp) # diagonal 1 for i in range(17): @@ -64,8 +62,7 @@ def solution(): * grid[i + 2][j + 2] * grid[i + 3][j + 3] ) - if temp > maximum: - maximum = temp + maximum = max(maximum, temp) # diagonal 2 for i in range(17): @@ -76,8 +73,7 @@ def solution(): * grid[i + 2][j - 2] * grid[i + 3][j - 3] ) - if temp > maximum: - maximum = temp + maximum = max(maximum, temp) return maximum diff --git a/scheduling/highest_response_ratio_next.py b/scheduling/highest_response_ratio_next.py index b549835616bf..f858be2ee44a 100644 --- a/scheduling/highest_response_ratio_next.py +++ b/scheduling/highest_response_ratio_next.py @@ -46,8 +46,7 @@ def calculate_turn_around_time( i = 0 while finished_process[i] == 1: i += 1 - if current_time < arrival_time[i]: - current_time = arrival_time[i] + current_time = max(current_time, arrival_time[i]) response_ratio = 0 # Index showing the location of the process being performed diff --git a/scheduling/shortest_job_first.py b/scheduling/shortest_job_first.py index 6899ec87c591..91012ee3ac35 100644 --- a/scheduling/shortest_job_first.py +++ b/scheduling/shortest_job_first.py @@ -66,8 +66,7 @@ def calculate_waitingtime( finar = finish_time - arrival_time[short] waiting_time[short] = finar - burst_time[short] - if waiting_time[short] < 0: - waiting_time[short] = 0 + waiting_time[short] = max(waiting_time[short], 0) # Increment time increment_time += 1 From c8e131b86c35c8fa4ca14aa85edbd4a106575882 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 26 Aug 2024 21:49:42 +0200 Subject: [PATCH 649/757] [pre-commit.ci] pre-commit autoupdate (#11522) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/abravalheri/validate-pyproject: v0.18 → v0.19](https://github.com/abravalheri/validate-pyproject/compare/v0.18...v0.19) - [github.com/pre-commit/mirrors-mypy: v1.11.1 → v1.11.2](https://github.com/pre-commit/mirrors-mypy/compare/v1.11.1...v1.11.2) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 06f8ba00494a..2724dff230e7 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -42,12 +42,12 @@ repos: pass_filenames: false - repo: https://github.com/abravalheri/validate-pyproject - rev: v0.18 + rev: v0.19 hooks: - id: validate-pyproject - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.11.1 + rev: v1.11.2 hooks: - id: mypy args: From bd8085cfc18784a21d792a44dcd683e11e802c6b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 21:41:55 +0200 Subject: [PATCH 650/757] [pre-commit.ci] pre-commit autoupdate (#11535) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.6.2 → v0.6.3](https://github.com/astral-sh/ruff-pre-commit/compare/v0.6.2...v0.6.3) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2724dff230e7..e363197497ac 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.6.2 + rev: v0.6.3 hooks: - id: ruff - id: ruff-format From f16d38f26f13683cf3ea75caf0474dedde059b86 Mon Sep 17 00:00:00 2001 From: Ramy <126559907+Ramy-Badr-Ahmed@users.noreply.github.com> Date: Tue, 3 Sep 2024 14:39:09 +0200 Subject: [PATCH 651/757] kd tree data structure implementation (#11532) * Implemented KD-Tree Data Structure * Implemented KD-Tree Data Structure. updated DIRECTORY.md. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Create __init__.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Replaced legacy `np.random.rand` call with `np.random.Generator` in kd_tree/example_usage.py * Replaced legacy `np.random.rand` call with `np.random.Generator` in kd_tree/hypercube_points.py * added typehints and docstrings * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * docstring for search() * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Added tests. Updated docstrings/typehints * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * updated tests and used | for type annotations * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * E501 for build_kdtree.py, hypercube_points.py, nearest_neighbour_search.py * I001 for example_usage.py and test_kdtree.py * I001 for example_usage.py and test_kdtree.py * Update data_structures/kd_tree/build_kdtree.py Co-authored-by: Christian Clauss * Update data_structures/kd_tree/example/hypercube_points.py Co-authored-by: Christian Clauss * Update data_structures/kd_tree/example/hypercube_points.py Co-authored-by: Christian Clauss * Added new test cases requested in Review. Refactored the test_build_kdtree() to include various checks. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Considered ruff errors * Considered ruff errors * Apply suggestions from code review * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update kd_node.py * imported annotations from __future__ * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- DIRECTORY.md | 6 ++ data_structures/kd_tree/__init__.py | 0 data_structures/kd_tree/build_kdtree.py | 35 ++++++ data_structures/kd_tree/example/__init__.py | 0 .../kd_tree/example/example_usage.py | 38 +++++++ .../kd_tree/example/hypercube_points.py | 21 ++++ data_structures/kd_tree/kd_node.py | 30 ++++++ .../kd_tree/nearest_neighbour_search.py | 71 +++++++++++++ data_structures/kd_tree/tests/__init__.py | 0 data_structures/kd_tree/tests/test_kdtree.py | 100 ++++++++++++++++++ 10 files changed, 301 insertions(+) create mode 100644 data_structures/kd_tree/__init__.py create mode 100644 data_structures/kd_tree/build_kdtree.py create mode 100644 data_structures/kd_tree/example/__init__.py create mode 100644 data_structures/kd_tree/example/example_usage.py create mode 100644 data_structures/kd_tree/example/hypercube_points.py create mode 100644 data_structures/kd_tree/kd_node.py create mode 100644 data_structures/kd_tree/nearest_neighbour_search.py create mode 100644 data_structures/kd_tree/tests/__init__.py create mode 100644 data_structures/kd_tree/tests/test_kdtree.py diff --git a/DIRECTORY.md b/DIRECTORY.md index 11de569a2c25..1ca537b991c8 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -285,6 +285,12 @@ * Trie * [Radix Tree](data_structures/trie/radix_tree.py) * [Trie](data_structures/trie/trie.py) + * KD Tree + * [KD Tree Node](data_structures/kd_tree/kd_node.py) + * [Build KD Tree](data_structures/kd_tree/build_kdtree.py) + * [Nearest Neighbour Search](data_structures/kd_tree/nearest_neighbour_search.py) + * [Hypercibe Points](data_structures/kd_tree/example/hypercube_points.py) + * [Example Usage](data_structures/kd_tree/example/example_usage.py) ## Digital Image Processing * [Change Brightness](digital_image_processing/change_brightness.py) diff --git a/data_structures/kd_tree/__init__.py b/data_structures/kd_tree/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/data_structures/kd_tree/build_kdtree.py b/data_structures/kd_tree/build_kdtree.py new file mode 100644 index 000000000000..c5b800a2c992 --- /dev/null +++ b/data_structures/kd_tree/build_kdtree.py @@ -0,0 +1,35 @@ +from data_structures.kd_tree.kd_node import KDNode + + +def build_kdtree(points: list[list[float]], depth: int = 0) -> KDNode | None: + """ + Builds a KD-Tree from a list of points. + + Args: + points: The list of points to build the KD-Tree from. + depth: The current depth in the tree + (used to determine axis for splitting). + + Returns: + The root node of the KD-Tree, + or None if no points are provided. + """ + if not points: + return None + + k = len(points[0]) # Dimensionality of the points + axis = depth % k + + # Sort point list and choose median as pivot element + points.sort(key=lambda point: point[axis]) + median_idx = len(points) // 2 + + # Create node and construct subtrees + left_points = points[:median_idx] + right_points = points[median_idx + 1 :] + + return KDNode( + point=points[median_idx], + left=build_kdtree(left_points, depth + 1), + right=build_kdtree(right_points, depth + 1), + ) diff --git a/data_structures/kd_tree/example/__init__.py b/data_structures/kd_tree/example/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/data_structures/kd_tree/example/example_usage.py b/data_structures/kd_tree/example/example_usage.py new file mode 100644 index 000000000000..e270f0cdd245 --- /dev/null +++ b/data_structures/kd_tree/example/example_usage.py @@ -0,0 +1,38 @@ +import numpy as np + +from data_structures.kd_tree.build_kdtree import build_kdtree +from data_structures.kd_tree.example.hypercube_points import hypercube_points +from data_structures.kd_tree.nearest_neighbour_search import nearest_neighbour_search + + +def main() -> None: + """ + Demonstrates the use of KD-Tree by building it from random points + in a 10-dimensional hypercube and performing a nearest neighbor search. + """ + num_points: int = 5000 + cube_size: float = 10.0 # Size of the hypercube (edge length) + num_dimensions: int = 10 + + # Generate random points within the hypercube + points: np.ndarray = hypercube_points(num_points, cube_size, num_dimensions) + hypercube_kdtree = build_kdtree(points.tolist()) + + # Generate a random query point within the same space + rng = np.random.default_rng() + query_point: list[float] = rng.random(num_dimensions).tolist() + + # Perform nearest neighbor search + nearest_point, nearest_dist, nodes_visited = nearest_neighbour_search( + hypercube_kdtree, query_point + ) + + # Print the results + print(f"Query point: {query_point}") + print(f"Nearest point: {nearest_point}") + print(f"Distance: {nearest_dist:.4f}") + print(f"Nodes visited: {nodes_visited}") + + +if __name__ == "__main__": + main() diff --git a/data_structures/kd_tree/example/hypercube_points.py b/data_structures/kd_tree/example/hypercube_points.py new file mode 100644 index 000000000000..2d8800ac9338 --- /dev/null +++ b/data_structures/kd_tree/example/hypercube_points.py @@ -0,0 +1,21 @@ +import numpy as np + + +def hypercube_points( + num_points: int, hypercube_size: float, num_dimensions: int +) -> np.ndarray: + """ + Generates random points uniformly distributed within an n-dimensional hypercube. + + Args: + num_points: Number of points to generate. + hypercube_size: Size of the hypercube. + num_dimensions: Number of dimensions of the hypercube. + + Returns: + An array of shape (num_points, num_dimensions) + with generated points. + """ + rng = np.random.default_rng() + shape = (num_points, num_dimensions) + return hypercube_size * rng.random(shape) diff --git a/data_structures/kd_tree/kd_node.py b/data_structures/kd_tree/kd_node.py new file mode 100644 index 000000000000..e1011027938d --- /dev/null +++ b/data_structures/kd_tree/kd_node.py @@ -0,0 +1,30 @@ +from __future__ import annotations + + +class KDNode: + """ + Represents a node in a KD-Tree. + + Attributes: + point: The point stored in this node. + left: The left child node. + right: The right child node. + """ + + def __init__( + self, + point: list[float], + left: KDNode | None = None, + right: KDNode | None = None, + ) -> None: + """ + Initializes a KDNode with the given point and child nodes. + + Args: + point (list[float]): The point stored in this node. + left (Optional[KDNode]): The left child node. + right (Optional[KDNode]): The right child node. + """ + self.point = point + self.left = left + self.right = right diff --git a/data_structures/kd_tree/nearest_neighbour_search.py b/data_structures/kd_tree/nearest_neighbour_search.py new file mode 100644 index 000000000000..d9727736f21c --- /dev/null +++ b/data_structures/kd_tree/nearest_neighbour_search.py @@ -0,0 +1,71 @@ +from data_structures.kd_tree.kd_node import KDNode + + +def nearest_neighbour_search( + root: KDNode | None, query_point: list[float] +) -> tuple[list[float] | None, float, int]: + """ + Performs a nearest neighbor search in a KD-Tree for a given query point. + + Args: + root (KDNode | None): The root node of the KD-Tree. + query_point (list[float]): The point for which the nearest neighbor + is being searched. + + Returns: + tuple[list[float] | None, float, int]: + - The nearest point found in the KD-Tree to the query point, + or None if no point is found. + - The squared distance to the nearest point. + - The number of nodes visited during the search. + """ + nearest_point: list[float] | None = None + nearest_dist: float = float("inf") + nodes_visited: int = 0 + + def search(node: KDNode | None, depth: int = 0) -> None: + """ + Recursively searches for the nearest neighbor in the KD-Tree. + + Args: + node: The current node in the KD-Tree. + depth: The current depth in the KD-Tree. + """ + nonlocal nearest_point, nearest_dist, nodes_visited + if node is None: + return + + nodes_visited += 1 + + # Calculate the current distance (squared distance) + current_point = node.point + current_dist = sum( + (query_coord - point_coord) ** 2 + for query_coord, point_coord in zip(query_point, current_point) + ) + + # Update nearest point if the current node is closer + if nearest_point is None or current_dist < nearest_dist: + nearest_point = current_point + nearest_dist = current_dist + + # Determine which subtree to search first (based on axis and query point) + k = len(query_point) # Dimensionality of points + axis = depth % k + + if query_point[axis] <= current_point[axis]: + nearer_subtree = node.left + further_subtree = node.right + else: + nearer_subtree = node.right + further_subtree = node.left + + # Search the nearer subtree first + search(nearer_subtree, depth + 1) + + # If the further subtree has a closer point + if (query_point[axis] - current_point[axis]) ** 2 < nearest_dist: + search(further_subtree, depth + 1) + + search(root, 0) + return nearest_point, nearest_dist, nodes_visited diff --git a/data_structures/kd_tree/tests/__init__.py b/data_structures/kd_tree/tests/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/data_structures/kd_tree/tests/test_kdtree.py b/data_structures/kd_tree/tests/test_kdtree.py new file mode 100644 index 000000000000..81f2cc990074 --- /dev/null +++ b/data_structures/kd_tree/tests/test_kdtree.py @@ -0,0 +1,100 @@ +import numpy as np +import pytest + +from data_structures.kd_tree.build_kdtree import build_kdtree +from data_structures.kd_tree.example.hypercube_points import hypercube_points +from data_structures.kd_tree.kd_node import KDNode +from data_structures.kd_tree.nearest_neighbour_search import nearest_neighbour_search + + +@pytest.mark.parametrize( + ("num_points", "cube_size", "num_dimensions", "depth", "expected_result"), + [ + (0, 10.0, 2, 0, None), # Empty points list + (10, 10.0, 2, 2, KDNode), # Depth = 2, 2D points + (10, 10.0, 3, -2, KDNode), # Depth = -2, 3D points + ], +) +def test_build_kdtree(num_points, cube_size, num_dimensions, depth, expected_result): + """ + Test that KD-Tree is built correctly. + + Cases: + - Empty points list. + - Positive depth value. + - Negative depth value. + """ + points = ( + hypercube_points(num_points, cube_size, num_dimensions).tolist() + if num_points > 0 + else [] + ) + + kdtree = build_kdtree(points, depth=depth) + + if expected_result is None: + # Empty points list case + assert kdtree is None, f"Expected None for empty points list, got {kdtree}" + else: + # Check if root node is not None + assert kdtree is not None, "Expected a KDNode, got None" + + # Check if root has correct dimensions + assert ( + len(kdtree.point) == num_dimensions + ), f"Expected point dimension {num_dimensions}, got {len(kdtree.point)}" + + # Check that the tree is balanced to some extent (simplistic check) + assert isinstance( + kdtree, KDNode + ), f"Expected KDNode instance, got {type(kdtree)}" + + +def test_nearest_neighbour_search(): + """ + Test the nearest neighbor search function. + """ + num_points = 10 + cube_size = 10.0 + num_dimensions = 2 + points = hypercube_points(num_points, cube_size, num_dimensions) + kdtree = build_kdtree(points.tolist()) + + rng = np.random.default_rng() + query_point = rng.random(num_dimensions).tolist() + + nearest_point, nearest_dist, nodes_visited = nearest_neighbour_search( + kdtree, query_point + ) + + # Check that nearest point is not None + assert nearest_point is not None + + # Check that distance is a non-negative number + assert nearest_dist >= 0 + + # Check that nodes visited is a non-negative integer + assert nodes_visited >= 0 + + +def test_edge_cases(): + """ + Test edge cases such as an empty KD-Tree. + """ + empty_kdtree = build_kdtree([]) + query_point = [0.0] * 2 # Using a default 2D query point + + nearest_point, nearest_dist, nodes_visited = nearest_neighbour_search( + empty_kdtree, query_point + ) + + # With an empty KD-Tree, nearest_point should be None + assert nearest_point is None + assert nearest_dist == float("inf") + assert nodes_visited == 0 + + +if __name__ == "__main__": + import pytest + + pytest.main() From 729c1f923bb621ed246983a5d3309135c3b1fc8c Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2024 22:15:17 +0200 Subject: [PATCH 652/757] [pre-commit.ci] pre-commit autoupdate (#11557) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/astral-sh/ruff-pre-commit: v0.6.3 → v0.6.4](https://github.com/astral-sh/ruff-pre-commit/compare/v0.6.3...v0.6.4) - [github.com/tox-dev/pyproject-fmt: 2.2.1 → 2.2.3](https://github.com/tox-dev/pyproject-fmt/compare/2.2.1...2.2.3) * updating DIRECTORY.md --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] --- .pre-commit-config.yaml | 4 ++-- DIRECTORY.md | 15 +++++++++------ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e363197497ac..ff76e87a3aa1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.6.3 + rev: v0.6.4 hooks: - id: ruff - id: ruff-format @@ -29,7 +29,7 @@ repos: - tomli - repo: https://github.com/tox-dev/pyproject-fmt - rev: "2.2.1" + rev: "2.2.3" hooks: - id: pyproject-fmt diff --git a/DIRECTORY.md b/DIRECTORY.md index 1ca537b991c8..e965d3b32ccf 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -243,6 +243,15 @@ * [Min Heap](data_structures/heap/min_heap.py) * [Randomized Heap](data_structures/heap/randomized_heap.py) * [Skew Heap](data_structures/heap/skew_heap.py) + * Kd Tree + * [Build Kdtree](data_structures/kd_tree/build_kdtree.py) + * Example + * [Example Usage](data_structures/kd_tree/example/example_usage.py) + * [Hypercube Points](data_structures/kd_tree/example/hypercube_points.py) + * [Kd Node](data_structures/kd_tree/kd_node.py) + * [Nearest Neighbour Search](data_structures/kd_tree/nearest_neighbour_search.py) + * Tests + * [Test Kdtree](data_structures/kd_tree/tests/test_kdtree.py) * Linked List * [Circular Linked List](data_structures/linked_list/circular_linked_list.py) * [Deque Doubly](data_structures/linked_list/deque_doubly.py) @@ -285,12 +294,6 @@ * Trie * [Radix Tree](data_structures/trie/radix_tree.py) * [Trie](data_structures/trie/trie.py) - * KD Tree - * [KD Tree Node](data_structures/kd_tree/kd_node.py) - * [Build KD Tree](data_structures/kd_tree/build_kdtree.py) - * [Nearest Neighbour Search](data_structures/kd_tree/nearest_neighbour_search.py) - * [Hypercibe Points](data_structures/kd_tree/example/hypercube_points.py) - * [Example Usage](data_structures/kd_tree/example/example_usage.py) ## Digital Image Processing * [Change Brightness](digital_image_processing/change_brightness.py) From 77bbe584216c0925e249e0baab77fef34561ecaa Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 17 Sep 2024 00:14:55 +0200 Subject: [PATCH 653/757] [pre-commit.ci] pre-commit autoupdate (#11568) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.6.4 → v0.6.5](https://github.com/astral-sh/ruff-pre-commit/compare/v0.6.4...v0.6.5) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ff76e87a3aa1..a4a45686537d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.6.4 + rev: v0.6.5 hooks: - id: ruff - id: ruff-format From 50cc00bb2da26fd234dabdfa7f93c96d6b7d72d5 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 23 Sep 2024 21:45:14 +0200 Subject: [PATCH 654/757] [pre-commit.ci] pre-commit autoupdate (#11579) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.6.5 → v0.6.7](https://github.com/astral-sh/ruff-pre-commit/compare/v0.6.5...v0.6.7) - [github.com/tox-dev/pyproject-fmt: 2.2.3 → 2.2.4](https://github.com/tox-dev/pyproject-fmt/compare/2.2.3...2.2.4) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a4a45686537d..7b219597f7b6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.6.5 + rev: v0.6.7 hooks: - id: ruff - id: ruff-format @@ -29,7 +29,7 @@ repos: - tomli - repo: https://github.com/tox-dev/pyproject-fmt - rev: "2.2.3" + rev: "2.2.4" hooks: - id: pyproject-fmt From 9b5641d2d333d04eb474ecbcb15c40ccf18a3d7b Mon Sep 17 00:00:00 2001 From: apples53 Date: Tue, 24 Sep 2024 13:00:36 +0530 Subject: [PATCH 655/757] balance parenthesis (add closing bracket) (#11563) * balance parenthesis (add closing bracket) * Apply suggestions from code review --------- Co-authored-by: Tianyi Zheng --- fuzzy_logic/fuzzy_operations.py.DISABLED.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzzy_logic/fuzzy_operations.py.DISABLED.txt b/fuzzy_logic/fuzzy_operations.py.DISABLED.txt index 0786ef8b0c67..67fd587f4baf 100644 --- a/fuzzy_logic/fuzzy_operations.py.DISABLED.txt +++ b/fuzzy_logic/fuzzy_operations.py.DISABLED.txt @@ -28,7 +28,7 @@ if __name__ == "__main__": union = fuzz.fuzzy_or(X, young, X, middle_aged)[1] # 2. Intersection = min(µA(x), µB(x)) intersection = fuzz.fuzzy_and(X, young, X, middle_aged)[1] - # 3. Complement (A) = (1- min(µA(x)) + # 3. Complement (A) = (1 - min(µA(x))) complement_a = fuzz.fuzzy_not(young) # 4. Difference (A/B) = min(µA(x),(1- µB(x))) difference = fuzz.fuzzy_and(X, young, X, fuzz.fuzzy_not(middle_aged)[1])[1] From 976e385c1d9df92c075575125475b22c423205b9 Mon Sep 17 00:00:00 2001 From: Ramy Date: Sat, 28 Sep 2024 15:37:00 +0200 Subject: [PATCH 656/757] Implemented Suffix Tree Data Structure (#11554) * Implemented KD-Tree Data Structure * Implemented KD-Tree Data Structure. updated DIRECTORY.md. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Create __init__.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Replaced legacy `np.random.rand` call with `np.random.Generator` in kd_tree/example_usage.py * Replaced legacy `np.random.rand` call with `np.random.Generator` in kd_tree/hypercube_points.py * added typehints and docstrings * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * docstring for search() * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Added tests. Updated docstrings/typehints * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * updated tests and used | for type annotations * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * E501 for build_kdtree.py, hypercube_points.py, nearest_neighbour_search.py * I001 for example_usage.py and test_kdtree.py * I001 for example_usage.py and test_kdtree.py * Update data_structures/kd_tree/build_kdtree.py Co-authored-by: Christian Clauss * Update data_structures/kd_tree/example/hypercube_points.py Co-authored-by: Christian Clauss * Update data_structures/kd_tree/example/hypercube_points.py Co-authored-by: Christian Clauss * Added new test cases requested in Review. Refactored the test_build_kdtree() to include various checks. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Considered ruff errors * Considered ruff errors * Apply suggestions from code review * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update kd_node.py * imported annotations from __future__ * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Implementation of the suffix tree data structure * Adding data to DIRECTORY.md * Minor file renaming * minor correction * renaming in DIRECTORY.md * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Considering ruff part-1 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Considering ruff part-2 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Considering ruff part-3 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Considering ruff part-4 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Considering ruff part-5 * Implemented Suffix Tree Data Structure. Added some comments to my files in #11532, #11554. * updating DIRECTORY.md * Implemented Suffix Tree Data Structure. Added some comments to my files in #11532, #11554. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss Co-authored-by: Ramy-Badr-Ahmed --- DIRECTORY.md | 7 ++ data_structures/kd_tree/build_kdtree.py | 8 +++ .../kd_tree/example/example_usage.py | 8 +++ .../kd_tree/example/hypercube_points.py | 8 +++ data_structures/kd_tree/kd_node.py | 8 +++ .../kd_tree/nearest_neighbour_search.py | 8 +++ data_structures/kd_tree/tests/test_kdtree.py | 8 +++ data_structures/suffix_tree/__init__.py | 0 .../suffix_tree/example/__init__.py | 0 .../suffix_tree/example/example_usage.py | 37 +++++++++++ data_structures/suffix_tree/suffix_tree.py | 66 +++++++++++++++++++ .../suffix_tree/suffix_tree_node.py | 36 ++++++++++ data_structures/suffix_tree/tests/__init__.py | 0 .../suffix_tree/tests/test_suffix_tree.py | 59 +++++++++++++++++ 14 files changed, 253 insertions(+) create mode 100644 data_structures/suffix_tree/__init__.py create mode 100644 data_structures/suffix_tree/example/__init__.py create mode 100644 data_structures/suffix_tree/example/example_usage.py create mode 100644 data_structures/suffix_tree/suffix_tree.py create mode 100644 data_structures/suffix_tree/suffix_tree_node.py create mode 100644 data_structures/suffix_tree/tests/__init__.py create mode 100644 data_structures/suffix_tree/tests/test_suffix_tree.py diff --git a/DIRECTORY.md b/DIRECTORY.md index e965d3b32ccf..955001e2aa23 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -291,6 +291,13 @@ * [Stack With Doubly Linked List](data_structures/stacks/stack_with_doubly_linked_list.py) * [Stack With Singly Linked List](data_structures/stacks/stack_with_singly_linked_list.py) * [Stock Span Problem](data_structures/stacks/stock_span_problem.py) + * Suffix Tree + * Example + * [Example Usage](data_structures/suffix_tree/example/example_usage.py) + * [Suffix Tree](data_structures/suffix_tree/suffix_tree.py) + * [Suffix Tree Node](data_structures/suffix_tree/suffix_tree_node.py) + * Tests + * [Test Suffix Tree](data_structures/suffix_tree/tests/test_suffix_tree.py) * Trie * [Radix Tree](data_structures/trie/radix_tree.py) * [Trie](data_structures/trie/trie.py) diff --git a/data_structures/kd_tree/build_kdtree.py b/data_structures/kd_tree/build_kdtree.py index c5b800a2c992..074a5dac4d42 100644 --- a/data_structures/kd_tree/build_kdtree.py +++ b/data_structures/kd_tree/build_kdtree.py @@ -1,3 +1,11 @@ +# Created by: Ramy-Badr-Ahmed (https://github.com/Ramy-Badr-Ahmed) +# in Pull Request: #11532 +# https://github.com/TheAlgorithms/Python/pull/11532 +# +# Please mention me (@Ramy-Badr-Ahmed) in any issue or pull request +# addressing bugs/corrections to this file. +# Thank you! + from data_structures.kd_tree.kd_node import KDNode diff --git a/data_structures/kd_tree/example/example_usage.py b/data_structures/kd_tree/example/example_usage.py index e270f0cdd245..892c3b8c4a2a 100644 --- a/data_structures/kd_tree/example/example_usage.py +++ b/data_structures/kd_tree/example/example_usage.py @@ -1,3 +1,11 @@ +# Created by: Ramy-Badr-Ahmed (https://github.com/Ramy-Badr-Ahmed) +# in Pull Request: #11532 +# https://github.com/TheAlgorithms/Python/pull/11532 +# +# Please mention me (@Ramy-Badr-Ahmed) in any issue or pull request +# addressing bugs/corrections to this file. +# Thank you! + import numpy as np from data_structures.kd_tree.build_kdtree import build_kdtree diff --git a/data_structures/kd_tree/example/hypercube_points.py b/data_structures/kd_tree/example/hypercube_points.py index 2d8800ac9338..66744856e6d5 100644 --- a/data_structures/kd_tree/example/hypercube_points.py +++ b/data_structures/kd_tree/example/hypercube_points.py @@ -1,3 +1,11 @@ +# Created by: Ramy-Badr-Ahmed (https://github.com/Ramy-Badr-Ahmed) +# in Pull Request: #11532 +# https://github.com/TheAlgorithms/Python/pull/11532 +# +# Please mention me (@Ramy-Badr-Ahmed) in any issue or pull request +# addressing bugs/corrections to this file. +# Thank you! + import numpy as np diff --git a/data_structures/kd_tree/kd_node.py b/data_structures/kd_tree/kd_node.py index e1011027938d..5a22ef609077 100644 --- a/data_structures/kd_tree/kd_node.py +++ b/data_structures/kd_tree/kd_node.py @@ -1,3 +1,11 @@ +# Created by: Ramy-Badr-Ahmed (https://github.com/Ramy-Badr-Ahmed) +# in Pull Request: #11532 +# https://github.com/TheAlgorithms/Python/pull/11532 +# +# Please mention me (@Ramy-Badr-Ahmed) in any issue or pull request +# addressing bugs/corrections to this file. +# Thank you! + from __future__ import annotations diff --git a/data_structures/kd_tree/nearest_neighbour_search.py b/data_structures/kd_tree/nearest_neighbour_search.py index d9727736f21c..8104944c08f0 100644 --- a/data_structures/kd_tree/nearest_neighbour_search.py +++ b/data_structures/kd_tree/nearest_neighbour_search.py @@ -1,3 +1,11 @@ +# Created by: Ramy-Badr-Ahmed (https://github.com/Ramy-Badr-Ahmed) +# in Pull Request: #11532 +# https://github.com/TheAlgorithms/Python/pull/11532 +# +# Please mention me (@Ramy-Badr-Ahmed) in any issue or pull request +# addressing bugs/corrections to this file. +# Thank you! + from data_structures.kd_tree.kd_node import KDNode diff --git a/data_structures/kd_tree/tests/test_kdtree.py b/data_structures/kd_tree/tests/test_kdtree.py index 81f2cc990074..dce5e4f34ff4 100644 --- a/data_structures/kd_tree/tests/test_kdtree.py +++ b/data_structures/kd_tree/tests/test_kdtree.py @@ -1,3 +1,11 @@ +# Created by: Ramy-Badr-Ahmed (https://github.com/Ramy-Badr-Ahmed) +# in Pull Request: #11532 +# https://github.com/TheAlgorithms/Python/pull/11532 +# +# Please mention me (@Ramy-Badr-Ahmed) in any issue or pull request +# addressing bugs/corrections to this file. +# Thank you! + import numpy as np import pytest diff --git a/data_structures/suffix_tree/__init__.py b/data_structures/suffix_tree/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/data_structures/suffix_tree/example/__init__.py b/data_structures/suffix_tree/example/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/data_structures/suffix_tree/example/example_usage.py b/data_structures/suffix_tree/example/example_usage.py new file mode 100644 index 000000000000..724ac57e8bfb --- /dev/null +++ b/data_structures/suffix_tree/example/example_usage.py @@ -0,0 +1,37 @@ +# Created by: Ramy-Badr-Ahmed (https://github.com/Ramy-Badr-Ahmed) +# in Pull Request: #11554 +# https://github.com/TheAlgorithms/Python/pull/11554 +# +# Please mention me (@Ramy-Badr-Ahmed) in any issue or pull request +# addressing bugs/corrections to this file. +# Thank you! + +from data_structures.suffix_tree.suffix_tree import SuffixTree + + +def main() -> None: + """ + Demonstrate the usage of the SuffixTree class. + + - Initializes a SuffixTree with a predefined text. + - Defines a list of patterns to search for within the suffix tree. + - Searches for each pattern in the suffix tree. + + Patterns tested: + - "ana" (found) --> True + - "ban" (found) --> True + - "na" (found) --> True + - "xyz" (not found) --> False + - "mon" (found) --> True + """ + text = "monkey banana" + suffix_tree = SuffixTree(text) + + patterns = ["ana", "ban", "na", "xyz", "mon"] + for pattern in patterns: + found = suffix_tree.search(pattern) + print(f"Pattern '{pattern}' found: {found}") + + +if __name__ == "__main__": + main() diff --git a/data_structures/suffix_tree/suffix_tree.py b/data_structures/suffix_tree/suffix_tree.py new file mode 100644 index 000000000000..ad54fb0ba009 --- /dev/null +++ b/data_structures/suffix_tree/suffix_tree.py @@ -0,0 +1,66 @@ +# Created by: Ramy-Badr-Ahmed (https://github.com/Ramy-Badr-Ahmed) +# in Pull Request: #11554 +# https://github.com/TheAlgorithms/Python/pull/11554 +# +# Please mention me (@Ramy-Badr-Ahmed) in any issue or pull request +# addressing bugs/corrections to this file. +# Thank you! + +from data_structures.suffix_tree.suffix_tree_node import SuffixTreeNode + + +class SuffixTree: + def __init__(self, text: str) -> None: + """ + Initializes the suffix tree with the given text. + + Args: + text (str): The text for which the suffix tree is to be built. + """ + self.text: str = text + self.root: SuffixTreeNode = SuffixTreeNode() + self.build_suffix_tree() + + def build_suffix_tree(self) -> None: + """ + Builds the suffix tree for the given text by adding all suffixes. + """ + text = self.text + n = len(text) + for i in range(n): + suffix = text[i:] + self._add_suffix(suffix, i) + + def _add_suffix(self, suffix: str, index: int) -> None: + """ + Adds a suffix to the suffix tree. + + Args: + suffix (str): The suffix to add. + index (int): The starting index of the suffix in the original text. + """ + node = self.root + for char in suffix: + if char not in node.children: + node.children[char] = SuffixTreeNode() + node = node.children[char] + node.is_end_of_string = True + node.start = index + node.end = index + len(suffix) - 1 + + def search(self, pattern: str) -> bool: + """ + Searches for a pattern in the suffix tree. + + Args: + pattern (str): The pattern to search for. + + Returns: + bool: True if the pattern is found, False otherwise. + """ + node = self.root + for char in pattern: + if char not in node.children: + return False + node = node.children[char] + return True diff --git a/data_structures/suffix_tree/suffix_tree_node.py b/data_structures/suffix_tree/suffix_tree_node.py new file mode 100644 index 000000000000..e5b628645063 --- /dev/null +++ b/data_structures/suffix_tree/suffix_tree_node.py @@ -0,0 +1,36 @@ +# Created by: Ramy-Badr-Ahmed (https://github.com/Ramy-Badr-Ahmed) +# in Pull Request: #11554 +# https://github.com/TheAlgorithms/Python/pull/11554 +# +# Please mention me (@Ramy-Badr-Ahmed) in any issue or pull request +# addressing bugs/corrections to this file. +# Thank you! + +from __future__ import annotations + + +class SuffixTreeNode: + def __init__( + self, + children: dict[str, SuffixTreeNode] | None = None, + is_end_of_string: bool = False, + start: int | None = None, + end: int | None = None, + suffix_link: SuffixTreeNode | None = None, + ) -> None: + """ + Initializes a suffix tree node. + + Parameters: + children (dict[str, SuffixTreeNode] | None): The children of this node. + is_end_of_string (bool): Indicates if this node represents + the end of a string. + start (int | None): The start index of the suffix in the text. + end (int | None): The end index of the suffix in the text. + suffix_link (SuffixTreeNode | None): Link to another suffix tree node. + """ + self.children = children or {} + self.is_end_of_string = is_end_of_string + self.start = start + self.end = end + self.suffix_link = suffix_link diff --git a/data_structures/suffix_tree/tests/__init__.py b/data_structures/suffix_tree/tests/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/data_structures/suffix_tree/tests/test_suffix_tree.py b/data_structures/suffix_tree/tests/test_suffix_tree.py new file mode 100644 index 000000000000..45c6790ac48a --- /dev/null +++ b/data_structures/suffix_tree/tests/test_suffix_tree.py @@ -0,0 +1,59 @@ +# Created by: Ramy-Badr-Ahmed (https://github.com/Ramy-Badr-Ahmed) +# in Pull Request: #11554 +# https://github.com/TheAlgorithms/Python/pull/11554 +# +# Please mention me (@Ramy-Badr-Ahmed) in any issue or pull request +# addressing bugs/corrections to this file. +# Thank you! + +import unittest + +from data_structures.suffix_tree.suffix_tree import SuffixTree + + +class TestSuffixTree(unittest.TestCase): + def setUp(self) -> None: + """Set up the initial conditions for each test.""" + self.text = "banana" + self.suffix_tree = SuffixTree(self.text) + + def test_search_existing_patterns(self) -> None: + """Test searching for patterns that exist in the suffix tree.""" + patterns = ["ana", "ban", "na"] + for pattern in patterns: + with self.subTest(pattern=pattern): + assert self.suffix_tree.search( + pattern + ), f"Pattern '{pattern}' should be found." + + def test_search_non_existing_patterns(self) -> None: + """Test searching for patterns that do not exist in the suffix tree.""" + patterns = ["xyz", "apple", "cat"] + for pattern in patterns: + with self.subTest(pattern=pattern): + assert not self.suffix_tree.search( + pattern + ), f"Pattern '{pattern}' should not be found." + + def test_search_empty_pattern(self) -> None: + """Test searching for an empty pattern.""" + assert self.suffix_tree.search(""), "An empty pattern should be found." + + def test_search_full_text(self) -> None: + """Test searching for the full text.""" + assert self.suffix_tree.search( + self.text + ), "The full text should be found in the suffix tree." + + def test_search_substrings(self) -> None: + """Test searching for substrings of the full text.""" + substrings = ["ban", "ana", "a", "na"] + for substring in substrings: + with self.subTest(substring=substring): + assert self.suffix_tree.search( + substring + ), f"Substring '{substring}' should be found." + + +if __name__ == "__main__": + unittest.main() From a9ca110d6b6e4921119fdcca3b2a01e7f649f1ed Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Mon, 30 Sep 2024 12:49:31 +0200 Subject: [PATCH 657/757] Scripts for closing pull requests for Hacktoberfest (#11587) * Scripts for closing pull requests for Hacktoberfest * --limit=500 * Lose 2024 --- ...ose_pull_requests_with_awaiting_changes.sh | 22 +++++++++++++++++++ .../close_pull_requests_with_failing_tests.sh | 22 +++++++++++++++++++ ...requests_with_require_descriptive_names.sh | 21 ++++++++++++++++++ .../close_pull_requests_with_require_tests.sh | 22 +++++++++++++++++++ ...e_pull_requests_with_require_type_hints.sh | 21 ++++++++++++++++++ 5 files changed, 108 insertions(+) create mode 100755 scripts/close_pull_requests_with_awaiting_changes.sh create mode 100755 scripts/close_pull_requests_with_failing_tests.sh create mode 100755 scripts/close_pull_requests_with_require_descriptive_names.sh create mode 100755 scripts/close_pull_requests_with_require_tests.sh create mode 100755 scripts/close_pull_requests_with_require_type_hints.sh diff --git a/scripts/close_pull_requests_with_awaiting_changes.sh b/scripts/close_pull_requests_with_awaiting_changes.sh new file mode 100755 index 000000000000..55e19c980596 --- /dev/null +++ b/scripts/close_pull_requests_with_awaiting_changes.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +# List all open pull requests +prs=$(gh pr list --state open --json number,title,labels --limit 500) + +# Loop through each pull request +echo "$prs" | jq -c '.[]' | while read -r pr; do + pr_number=$(echo "$pr" | jq -r '.number') + pr_title=$(echo "$pr" | jq -r '.title') + pr_labels=$(echo "$pr" | jq -r '.labels') + + # Check if the "awaiting changes" label is present + awaiting_changes=$(echo "$pr_labels" | jq -r '.[] | select(.name == "awaiting changes")') + echo "Checking PR #$pr_number $pr_title ($awaiting_changes) ($pr_labels)" + + # If awaiting_changes, close the pull request + if [[ -n "$awaiting_changes" ]]; then + echo "Closing PR #$pr_number $pr_title due to awaiting_changes label" + gh pr close "$pr_number" --comment "Closing awaiting_changes PRs to prepare for Hacktoberfest" + sleep 2 + fi +done diff --git a/scripts/close_pull_requests_with_failing_tests.sh b/scripts/close_pull_requests_with_failing_tests.sh new file mode 100755 index 000000000000..3ec5960aed27 --- /dev/null +++ b/scripts/close_pull_requests_with_failing_tests.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +# List all open pull requests +prs=$(gh pr list --state open --json number,title,labels --limit 500) + +# Loop through each pull request +echo "$prs" | jq -c '.[]' | while read -r pr; do + pr_number=$(echo "$pr" | jq -r '.number') + pr_title=$(echo "$pr" | jq -r '.title') + pr_labels=$(echo "$pr" | jq -r '.labels') + + # Check if the "tests are failing" label is present + tests_are_failing=$(echo "$pr_labels" | jq -r '.[] | select(.name == "tests are failing")') + echo "Checking PR #$pr_number $pr_title ($tests_are_failing) ($pr_labels)" + + # If there are failing tests, close the pull request + if [[ -n "$tests_are_failing" ]]; then + echo "Closing PR #$pr_number $pr_title due to tests_are_failing label" + gh pr close "$pr_number" --comment "Closing tests_are_failing PRs to prepare for Hacktoberfest" + sleep 2 + fi +done diff --git a/scripts/close_pull_requests_with_require_descriptive_names.sh b/scripts/close_pull_requests_with_require_descriptive_names.sh new file mode 100755 index 000000000000..0fc3cec1d247 --- /dev/null +++ b/scripts/close_pull_requests_with_require_descriptive_names.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +# List all open pull requests +prs=$(gh pr list --state open --json number,title,labels --limit 500) + +# Loop through each pull request +echo "$prs" | jq -c '.[]' | while read -r pr; do + pr_number=$(echo "$pr" | jq -r '.number') + pr_title=$(echo "$pr" | jq -r '.title') + pr_labels=$(echo "$pr" | jq -r '.labels') + + # Check if the "require descriptive names" label is present + require_descriptive_names=$(echo "$pr_labels" | jq -r '.[] | select(.name == "require descriptive names")') + echo "Checking PR #$pr_number $pr_title ($require_descriptive_names) ($pr_labels)" + + # If there are require_descriptive_names, close the pull request + if [[ -n "$require_descriptive_names" ]]; then + echo "Closing PR #$pr_number $pr_title due to require_descriptive_names label" + gh pr close "$pr_number" --comment "Closing require_descriptive_names PRs to prepare for Hacktoberfest" + fi +done diff --git a/scripts/close_pull_requests_with_require_tests.sh b/scripts/close_pull_requests_with_require_tests.sh new file mode 100755 index 000000000000..89a54996b584 --- /dev/null +++ b/scripts/close_pull_requests_with_require_tests.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +# List all open pull requests +prs=$(gh pr list --state open --json number,title,labels --limit 500) + +# Loop through each pull request +echo "$prs" | jq -c '.[]' | while read -r pr; do + pr_number=$(echo "$pr" | jq -r '.number') + pr_title=$(echo "$pr" | jq -r '.title') + pr_labels=$(echo "$pr" | jq -r '.labels') + + # Check if the "require_tests" label is present + require_tests=$(echo "$pr_labels" | jq -r '.[] | select(.name == "require tests")') + echo "Checking PR #$pr_number $pr_title ($require_tests) ($pr_labels)" + + # If there require tests, close the pull request + if [[ -n "$require_tests" ]]; then + echo "Closing PR #$pr_number $pr_title due to require_tests label" + gh pr close "$pr_number" --comment "Closing require_tests PRs to prepare for Hacktoberfest" + # sleep 2 + fi +done diff --git a/scripts/close_pull_requests_with_require_type_hints.sh b/scripts/close_pull_requests_with_require_type_hints.sh new file mode 100755 index 000000000000..df5d88289cf0 --- /dev/null +++ b/scripts/close_pull_requests_with_require_type_hints.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +# List all open pull requests +prs=$(gh pr list --state open --json number,title,labels --limit 500) + +# Loop through each pull request +echo "$prs" | jq -c '.[]' | while read -r pr; do + pr_number=$(echo "$pr" | jq -r '.number') + pr_title=$(echo "$pr" | jq -r '.title') + pr_labels=$(echo "$pr" | jq -r '.labels') + + # Check if the "require type hints" label is present + require_type_hints=$(echo "$pr_labels" | jq -r '.[] | select(.name == "require type hints")') + echo "Checking PR #$pr_number $pr_title ($require_type_hints) ($pr_labels)" + + # If require_type_hints, close the pull request + if [[ -n "$require_type_hints" ]]; then + echo "Closing PR #$pr_number $pr_title due to require_type_hints label" + gh pr close "$pr_number" --comment "Closing require_type_hints PRs to prepare for Hacktoberfest" + fi +done From a7bfa224554f277ed68be9e4ef3f6d1cd89008af Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 30 Sep 2024 22:16:17 +0200 Subject: [PATCH 658/757] [pre-commit.ci] pre-commit autoupdate (#11594) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.6.7 → v0.6.8](https://github.com/astral-sh/ruff-pre-commit/compare/v0.6.7...v0.6.8) - [github.com/abravalheri/validate-pyproject: v0.19 → v0.20.2](https://github.com/abravalheri/validate-pyproject/compare/v0.19...v0.20.2) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7b219597f7b6..8a8e5c1f6ad9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.6.7 + rev: v0.6.8 hooks: - id: ruff - id: ruff-format @@ -42,7 +42,7 @@ repos: pass_filenames: false - repo: https://github.com/abravalheri/validate-pyproject - rev: v0.19 + rev: v0.20.2 hooks: - id: validate-pyproject From 0177ae1cd596f4f3c0ee7490666d74504deb0298 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Mon, 30 Sep 2024 23:01:15 +0200 Subject: [PATCH 659/757] Upgrade to Python 3.13 (#11588) --- .github/workflows/build.yml | 6 ++- DIRECTORY.md | 1 - computer_vision/haralick_descriptors.py | 8 ++-- data_structures/heap/binomial_heap.py | 6 +-- electronics/circular_convolution.py | 6 +-- fractals/julia_sets.py | 18 ++++----- graphics/bezier_curve.py | 8 ++-- graphs/dijkstra_binary_grid.py | 2 +- linear_algebra/src/power_iteration.py | 2 +- linear_programming/simplex.py | 32 +++++++-------- machine_learning/decision_tree.py | 8 ++-- machine_learning/forecasting/run.py | 8 ++-- machine_learning/k_nearest_neighbours.py | 2 +- machine_learning/logistic_regression.py | 4 +- machine_learning/loss_functions.py | 40 +++++++++---------- machine_learning/mfcc.py | 13 +++--- .../multilayer_perceptron_classifier.py | 2 +- machine_learning/scoring_functions.py | 22 +++++----- machine_learning/similarity_search.py | 2 +- machine_learning/support_vector_machines.py | 6 +-- maths/euclidean_distance.py | 8 ++-- maths/euler_method.py | 2 +- maths/euler_modified.py | 4 +- maths/gaussian.py | 16 ++++---- maths/minkowski_distance.py | 2 +- maths/numerical_analysis/adams_bashforth.py | 8 ++-- maths/numerical_analysis/runge_kutta.py | 2 +- .../runge_kutta_fehlberg_45.py | 4 +- maths/numerical_analysis/runge_kutta_gills.py | 2 +- maths/softmax.py | 2 +- .../two_hidden_layers_neural_network.py | 6 +-- other/bankers_algorithm.py | 8 ++-- physics/in_static_equilibrium.py | 2 +- requirements.txt | 4 +- ..._tweets.py => get_user_tweets.py.DISABLED} | 0 35 files changed, 135 insertions(+), 131 deletions(-) rename web_programming/{get_user_tweets.py => get_user_tweets.py.DISABLED} (100%) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a113b4608678..dad2b2fac086 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,7 +12,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: - python-version: 3.12 + python-version: 3.13 allow-prereleases: true - uses: actions/cache@v4 with: @@ -26,6 +26,10 @@ jobs: # TODO: #8818 Re-enable quantum tests run: pytest --ignore=quantum/q_fourier_transform.py + --ignore=computer_vision/cnn_classification.py + --ignore=dynamic_programming/k_means_clustering_tensorflow.py + --ignore=machine_learning/lstm/lstm_prediction.py + --ignore=neural_network/input_data.py --ignore=project_euler/ --ignore=scripts/validate_solutions.py --cov-report=term-missing:skip-covered diff --git a/DIRECTORY.md b/DIRECTORY.md index 955001e2aa23..56ab8377f16b 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -1343,7 +1343,6 @@ * [Get Ip Geolocation](web_programming/get_ip_geolocation.py) * [Get Top Billionaires](web_programming/get_top_billionaires.py) * [Get Top Hn Posts](web_programming/get_top_hn_posts.py) - * [Get User Tweets](web_programming/get_user_tweets.py) * [Giphy](web_programming/giphy.py) * [Instagram Crawler](web_programming/instagram_crawler.py) * [Instagram Pic](web_programming/instagram_pic.py) diff --git a/computer_vision/haralick_descriptors.py b/computer_vision/haralick_descriptors.py index 634f0495797b..54632160dcf2 100644 --- a/computer_vision/haralick_descriptors.py +++ b/computer_vision/haralick_descriptors.py @@ -19,7 +19,7 @@ def root_mean_square_error(original: np.ndarray, reference: np.ndarray) -> float >>> root_mean_square_error(np.array([1, 2, 3]), np.array([6, 4, 2])) 3.1622776601683795 """ - return np.sqrt(((original - reference) ** 2).mean()) + return float(np.sqrt(((original - reference) ** 2).mean())) def normalize_image( @@ -273,7 +273,7 @@ def haralick_descriptors(matrix: np.ndarray) -> list[float]: >>> morphological = opening_filter(binary) >>> mask_1 = binary_mask(gray, morphological)[0] >>> concurrency = matrix_concurrency(mask_1, (0, 1)) - >>> haralick_descriptors(concurrency) + >>> [float(f) for f in haralick_descriptors(concurrency)] [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] """ # Function np.indices could be used for bigger input types, @@ -335,7 +335,7 @@ def get_descriptors( return np.concatenate(descriptors, axis=None) -def euclidean(point_1: np.ndarray, point_2: np.ndarray) -> np.float32: +def euclidean(point_1: np.ndarray, point_2: np.ndarray) -> float: """ Simple method for calculating the euclidean distance between two points, with type np.ndarray. @@ -346,7 +346,7 @@ def euclidean(point_1: np.ndarray, point_2: np.ndarray) -> np.float32: >>> euclidean(a, b) 3.3166247903554 """ - return np.sqrt(np.sum(np.square(point_1 - point_2))) + return float(np.sqrt(np.sum(np.square(point_1 - point_2)))) def get_distances(descriptors: np.ndarray, base: int) -> list[tuple[int, float]]: diff --git a/data_structures/heap/binomial_heap.py b/data_structures/heap/binomial_heap.py index 099bd2871023..9cfdf0c12fe0 100644 --- a/data_structures/heap/binomial_heap.py +++ b/data_structures/heap/binomial_heap.py @@ -73,7 +73,7 @@ class BinomialHeap: 30 Deleting - delete() test - >>> [first_heap.delete_min() for _ in range(20)] + >>> [int(first_heap.delete_min()) for _ in range(20)] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] Create a new Heap @@ -118,7 +118,7 @@ class BinomialHeap: values in merged heap; (merge is inplace) >>> results = [] >>> while not first_heap.is_empty(): - ... results.append(first_heap.delete_min()) + ... results.append(int(first_heap.delete_min())) >>> results [17, 20, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 31, 34] """ @@ -354,7 +354,7 @@ def delete_min(self): # Merge heaps self.merge_heaps(new_heap) - return min_value + return int(min_value) def pre_order(self): """ diff --git a/electronics/circular_convolution.py b/electronics/circular_convolution.py index 768f2ad941bc..d06e76be759b 100644 --- a/electronics/circular_convolution.py +++ b/electronics/circular_convolution.py @@ -39,7 +39,7 @@ def circular_convolution(self) -> list[float]: Usage: >>> convolution = CircularConvolution() >>> convolution.circular_convolution() - [10, 10, 6, 14] + [10.0, 10.0, 6.0, 14.0] >>> convolution.first_signal = [0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6] >>> convolution.second_signal = [0.1, 0.3, 0.5, 0.7, 0.9, 1.1, 1.3, 1.5] @@ -54,7 +54,7 @@ def circular_convolution(self) -> list[float]: >>> convolution.first_signal = [1, -1, 2, 3, -1] >>> convolution.second_signal = [1, 2, 3] >>> convolution.circular_convolution() - [8, -2, 3, 4, 11] + [8.0, -2.0, 3.0, 4.0, 11.0] """ @@ -91,7 +91,7 @@ def circular_convolution(self) -> list[float]: final_signal = np.matmul(np.transpose(matrix), np.transpose(self.first_signal)) # rounding-off to two decimal places - return [round(i, 2) for i in final_signal] + return [float(round(i, 2)) for i in final_signal] if __name__ == "__main__": diff --git a/fractals/julia_sets.py b/fractals/julia_sets.py index 1eef4573ba19..bea599d44339 100644 --- a/fractals/julia_sets.py +++ b/fractals/julia_sets.py @@ -40,11 +40,11 @@ def eval_exponential(c_parameter: complex, z_values: np.ndarray) -> np.ndarray: """ Evaluate $e^z + c$. - >>> eval_exponential(0, 0) + >>> float(eval_exponential(0, 0)) 1.0 - >>> abs(eval_exponential(1, np.pi*1.j)) < 1e-15 + >>> bool(abs(eval_exponential(1, np.pi*1.j)) < 1e-15) True - >>> abs(eval_exponential(1.j, 0)-1-1.j) < 1e-15 + >>> bool(abs(eval_exponential(1.j, 0)-1-1.j) < 1e-15) True """ return np.exp(z_values) + c_parameter @@ -98,20 +98,20 @@ def iterate_function( >>> iterate_function(eval_quadratic_polynomial, 0, 3, np.array([0,1,2])).shape (3,) - >>> np.round(iterate_function(eval_quadratic_polynomial, + >>> complex(np.round(iterate_function(eval_quadratic_polynomial, ... 0, ... 3, - ... np.array([0,1,2]))[0]) + ... np.array([0,1,2]))[0])) 0j - >>> np.round(iterate_function(eval_quadratic_polynomial, + >>> complex(np.round(iterate_function(eval_quadratic_polynomial, ... 0, ... 3, - ... np.array([0,1,2]))[1]) + ... np.array([0,1,2]))[1])) (1+0j) - >>> np.round(iterate_function(eval_quadratic_polynomial, + >>> complex(np.round(iterate_function(eval_quadratic_polynomial, ... 0, ... 3, - ... np.array([0,1,2]))[2]) + ... np.array([0,1,2]))[2])) (256+0j) """ diff --git a/graphics/bezier_curve.py b/graphics/bezier_curve.py index 9d906f179c92..6c7dcd4f06e7 100644 --- a/graphics/bezier_curve.py +++ b/graphics/bezier_curve.py @@ -30,9 +30,9 @@ def basis_function(self, t: float) -> list[float]: returns the x, y values of basis function at time t >>> curve = BezierCurve([(1,1), (1,2)]) - >>> curve.basis_function(0) + >>> [float(x) for x in curve.basis_function(0)] [1.0, 0.0] - >>> curve.basis_function(1) + >>> [float(x) for x in curve.basis_function(1)] [0.0, 1.0] """ assert 0 <= t <= 1, "Time t must be between 0 and 1." @@ -55,9 +55,9 @@ def bezier_curve_function(self, t: float) -> tuple[float, float]: The last point in the curve is when t = 1. >>> curve = BezierCurve([(1,1), (1,2)]) - >>> curve.bezier_curve_function(0) + >>> tuple(float(x) for x in curve.bezier_curve_function(0)) (1.0, 1.0) - >>> curve.bezier_curve_function(1) + >>> tuple(float(x) for x in curve.bezier_curve_function(1)) (1.0, 2.0) """ diff --git a/graphs/dijkstra_binary_grid.py b/graphs/dijkstra_binary_grid.py index c23d8234328a..06293a87da2d 100644 --- a/graphs/dijkstra_binary_grid.py +++ b/graphs/dijkstra_binary_grid.py @@ -69,7 +69,7 @@ def dijkstra( x, y = predecessors[x, y] path.append(source) # add the source manually path.reverse() - return matrix[destination], path + return float(matrix[destination]), path for i in range(len(dx)): nx, ny = x + dx[i], y + dy[i] diff --git a/linear_algebra/src/power_iteration.py b/linear_algebra/src/power_iteration.py index 24fbd9a5e002..83c2ce48c3a0 100644 --- a/linear_algebra/src/power_iteration.py +++ b/linear_algebra/src/power_iteration.py @@ -78,7 +78,7 @@ def power_iteration( if is_complex: lambda_ = np.real(lambda_) - return lambda_, vector + return float(lambda_), vector def test_power_iteration() -> None: diff --git a/linear_programming/simplex.py b/linear_programming/simplex.py index dc171bacd3a2..a8affe1b72d2 100644 --- a/linear_programming/simplex.py +++ b/linear_programming/simplex.py @@ -107,8 +107,8 @@ def generate_col_titles(self) -> list[str]: def find_pivot(self) -> tuple[Any, Any]: """Finds the pivot row and column. - >>> Tableau(np.array([[-2,1,0,0,0], [3,1,1,0,6], [1,2,0,1,7.]]), - ... 2, 0).find_pivot() + >>> tuple(int(x) for x in Tableau(np.array([[-2,1,0,0,0], [3,1,1,0,6], + ... [1,2,0,1,7.]]), 2, 0).find_pivot()) (1, 0) """ objective = self.objectives[-1] @@ -215,8 +215,8 @@ def run_simplex(self) -> dict[Any, Any]: Max: x1 + x2 ST: x1 + 3x2 <= 4 3x1 + x2 <= 4 - >>> Tableau(np.array([[-1,-1,0,0,0],[1,3,1,0,4],[3,1,0,1,4.]]), - ... 2, 0).run_simplex() + >>> {key: float(value) for key, value in Tableau(np.array([[-1,-1,0,0,0], + ... [1,3,1,0,4],[3,1,0,1,4.]]), 2, 0).run_simplex().items()} {'P': 2.0, 'x1': 1.0, 'x2': 1.0} # Standard linear program with 3 variables: @@ -224,21 +224,21 @@ def run_simplex(self) -> dict[Any, Any]: ST: 2x1 + x2 + x3 ≤ 2 x1 + 2x2 + 3x3 ≤ 5 2x1 + 2x2 + x3 ≤ 6 - >>> Tableau(np.array([ + >>> {key: float(value) for key, value in Tableau(np.array([ ... [-3,-1,-3,0,0,0,0], ... [2,1,1,1,0,0,2], ... [1,2,3,0,1,0,5], ... [2,2,1,0,0,1,6.] - ... ]),3,0).run_simplex() # doctest: +ELLIPSIS + ... ]),3,0).run_simplex().items()} # doctest: +ELLIPSIS {'P': 5.4, 'x1': 0.199..., 'x3': 1.6} # Optimal tableau input: - >>> Tableau(np.array([ + >>> {key: float(value) for key, value in Tableau(np.array([ ... [0, 0, 0.25, 0.25, 2], ... [0, 1, 0.375, -0.125, 1], ... [1, 0, -0.125, 0.375, 1] - ... ]), 2, 0).run_simplex() + ... ]), 2, 0).run_simplex().items()} {'P': 2.0, 'x1': 1.0, 'x2': 1.0} # Non-standard: >= constraints @@ -246,25 +246,25 @@ def run_simplex(self) -> dict[Any, Any]: ST: x1 + x2 + x3 <= 40 2x1 + x2 - x3 >= 10 - x2 + x3 >= 10 - >>> Tableau(np.array([ + >>> {key: float(value) for key, value in Tableau(np.array([ ... [2, 0, 0, 0, -1, -1, 0, 0, 20], ... [-2, -3, -1, 0, 0, 0, 0, 0, 0], ... [1, 1, 1, 1, 0, 0, 0, 0, 40], ... [2, 1, -1, 0, -1, 0, 1, 0, 10], ... [0, -1, 1, 0, 0, -1, 0, 1, 10.] - ... ]), 3, 2).run_simplex() + ... ]), 3, 2).run_simplex().items()} {'P': 70.0, 'x1': 10.0, 'x2': 10.0, 'x3': 20.0} # Non standard: minimisation and equalities Min: x1 + x2 ST: 2x1 + x2 = 12 6x1 + 5x2 = 40 - >>> Tableau(np.array([ + >>> {key: float(value) for key, value in Tableau(np.array([ ... [8, 6, 0, 0, 52], ... [1, 1, 0, 0, 0], ... [2, 1, 1, 0, 12], ... [6, 5, 0, 1, 40.], - ... ]), 2, 2).run_simplex() + ... ]), 2, 2).run_simplex().items()} {'P': 7.0, 'x1': 5.0, 'x2': 2.0} @@ -275,7 +275,7 @@ def run_simplex(self) -> dict[Any, Any]: 2x1 + 4x2 <= 48 x1 + x2 >= 10 x1 >= 2 - >>> Tableau(np.array([ + >>> {key: float(value) for key, value in Tableau(np.array([ ... [2, 1, 0, 0, 0, -1, -1, 0, 0, 12.0], ... [-8, -6, 0, 0, 0, 0, 0, 0, 0, 0.0], ... [1, 3, 1, 0, 0, 0, 0, 0, 0, 33.0], @@ -283,7 +283,7 @@ def run_simplex(self) -> dict[Any, Any]: ... [2, 4, 0, 0, 1, 0, 0, 0, 0, 48.0], ... [1, 1, 0, 0, 0, -1, 0, 1, 0, 10.0], ... [1, 0, 0, 0, 0, 0, -1, 0, 1, 2.0] - ... ]), 2, 2).run_simplex() # doctest: +ELLIPSIS + ... ]), 2, 2).run_simplex().items()} # doctest: +ELLIPSIS {'P': 132.0, 'x1': 12.000... 'x2': 5.999...} """ # Stop simplex algorithm from cycling. @@ -307,11 +307,11 @@ def run_simplex(self) -> dict[Any, Any]: def interpret_tableau(self) -> dict[str, float]: """Given the final tableau, add the corresponding values of the basic decision variables to the `output_dict` - >>> Tableau(np.array([ + >>> {key: float(value) for key, value in Tableau(np.array([ ... [0,0,0.875,0.375,5], ... [0,1,0.375,-0.125,1], ... [1,0,-0.125,0.375,1] - ... ]),2, 0).interpret_tableau() + ... ]),2, 0).interpret_tableau().items()} {'P': 5.0, 'x1': 1.0, 'x2': 1.0} """ # P = RHS of final tableau diff --git a/machine_learning/decision_tree.py b/machine_learning/decision_tree.py index d0bd6ab0b555..72970431c3fc 100644 --- a/machine_learning/decision_tree.py +++ b/machine_learning/decision_tree.py @@ -26,15 +26,15 @@ def mean_squared_error(self, labels, prediction): >>> tester = DecisionTree() >>> test_labels = np.array([1,2,3,4,5,6,7,8,9,10]) >>> test_prediction = float(6) - >>> tester.mean_squared_error(test_labels, test_prediction) == ( + >>> bool(tester.mean_squared_error(test_labels, test_prediction) == ( ... TestDecisionTree.helper_mean_squared_error_test(test_labels, - ... test_prediction)) + ... test_prediction))) True >>> test_labels = np.array([1,2,3]) >>> test_prediction = float(2) - >>> tester.mean_squared_error(test_labels, test_prediction) == ( + >>> bool(tester.mean_squared_error(test_labels, test_prediction) == ( ... TestDecisionTree.helper_mean_squared_error_test(test_labels, - ... test_prediction)) + ... test_prediction))) True """ if labels.ndim != 1: diff --git a/machine_learning/forecasting/run.py b/machine_learning/forecasting/run.py index dbb86caf8568..9d81b03cd09e 100644 --- a/machine_learning/forecasting/run.py +++ b/machine_learning/forecasting/run.py @@ -28,7 +28,7 @@ def linear_regression_prediction( input : training data (date, total_user, total_event) in list of float output : list of total user prediction in float >>> n = linear_regression_prediction([2,3,4,5], [5,3,4,6], [3,1,2,4], [2,1], [2,2]) - >>> abs(n - 5.0) < 1e-6 # Checking precision because of floating point errors + >>> bool(abs(n - 5.0) < 1e-6) # Checking precision because of floating point errors True """ x = np.array([[1, item, train_mtch[i]] for i, item in enumerate(train_dt)]) @@ -56,7 +56,7 @@ def sarimax_predictor(train_user: list, train_match: list, test_match: list) -> ) model_fit = model.fit(disp=False, maxiter=600, method="nm") result = model_fit.predict(1, len(test_match), exog=[test_match]) - return result[0] + return float(result[0]) def support_vector_regressor(x_train: list, x_test: list, train_user: list) -> float: @@ -75,7 +75,7 @@ def support_vector_regressor(x_train: list, x_test: list, train_user: list) -> f regressor = SVR(kernel="rbf", C=1, gamma=0.1, epsilon=0.1) regressor.fit(x_train, train_user) y_pred = regressor.predict(x_test) - return y_pred[0] + return float(y_pred[0]) def interquartile_range_checker(train_user: list) -> float: @@ -92,7 +92,7 @@ def interquartile_range_checker(train_user: list) -> float: q3 = np.percentile(train_user, 75) iqr = q3 - q1 low_lim = q1 - (iqr * 0.1) - return low_lim + return float(low_lim) def data_safety_checker(list_vote: list, actual_result: float) -> bool: diff --git a/machine_learning/k_nearest_neighbours.py b/machine_learning/k_nearest_neighbours.py index a43757c5c20e..fbc1b8bd227e 100644 --- a/machine_learning/k_nearest_neighbours.py +++ b/machine_learning/k_nearest_neighbours.py @@ -42,7 +42,7 @@ def _euclidean_distance(a: np.ndarray[float], b: np.ndarray[float]) -> float: >>> KNN._euclidean_distance(np.array([1, 2, 3]), np.array([1, 8, 11])) 10.0 """ - return np.linalg.norm(a - b) + return float(np.linalg.norm(a - b)) def classify(self, pred_point: np.ndarray[float], k: int = 5) -> str: """ diff --git a/machine_learning/logistic_regression.py b/machine_learning/logistic_regression.py index 090af5382185..496026631fbe 100644 --- a/machine_learning/logistic_regression.py +++ b/machine_learning/logistic_regression.py @@ -45,7 +45,7 @@ def sigmoid_function(z: float | np.ndarray) -> float | np.ndarray: @returns: returns value in the range 0 to 1 Examples: - >>> sigmoid_function(4) + >>> float(sigmoid_function(4)) 0.9820137900379085 >>> sigmoid_function(np.array([-3, 3])) array([0.04742587, 0.95257413]) @@ -100,7 +100,7 @@ def cost_function(h: np.ndarray, y: np.ndarray) -> float: References: - https://en.wikipedia.org/wiki/Logistic_regression """ - return (-y * np.log(h) - (1 - y) * np.log(1 - h)).mean() + return float((-y * np.log(h) - (1 - y) * np.log(1 - h)).mean()) def log_likelihood(x, y, weights): diff --git a/machine_learning/loss_functions.py b/machine_learning/loss_functions.py index 150035661eb7..0bd9aa8b5401 100644 --- a/machine_learning/loss_functions.py +++ b/machine_learning/loss_functions.py @@ -22,7 +22,7 @@ def binary_cross_entropy( >>> true_labels = np.array([0, 1, 1, 0, 1]) >>> predicted_probs = np.array([0.2, 0.7, 0.9, 0.3, 0.8]) - >>> binary_cross_entropy(true_labels, predicted_probs) + >>> float(binary_cross_entropy(true_labels, predicted_probs)) 0.2529995012327421 >>> true_labels = np.array([0, 1, 1, 0, 1]) >>> predicted_probs = np.array([0.3, 0.8, 0.9, 0.2]) @@ -68,7 +68,7 @@ def binary_focal_cross_entropy( >>> true_labels = np.array([0, 1, 1, 0, 1]) >>> predicted_probs = np.array([0.2, 0.7, 0.9, 0.3, 0.8]) - >>> binary_focal_cross_entropy(true_labels, predicted_probs) + >>> float(binary_focal_cross_entropy(true_labels, predicted_probs)) 0.008257977659239775 >>> true_labels = np.array([0, 1, 1, 0, 1]) >>> predicted_probs = np.array([0.3, 0.8, 0.9, 0.2]) @@ -108,7 +108,7 @@ def categorical_cross_entropy( >>> true_labels = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]]) >>> pred_probs = np.array([[0.9, 0.1, 0.0], [0.2, 0.7, 0.1], [0.0, 0.1, 0.9]]) - >>> categorical_cross_entropy(true_labels, pred_probs) + >>> float(categorical_cross_entropy(true_labels, pred_probs)) 0.567395975254385 >>> true_labels = np.array([[1, 0], [0, 1]]) >>> pred_probs = np.array([[0.9, 0.1, 0.0], [0.2, 0.7, 0.1]]) @@ -179,13 +179,13 @@ def categorical_focal_cross_entropy( >>> true_labels = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]]) >>> pred_probs = np.array([[0.9, 0.1, 0.0], [0.2, 0.7, 0.1], [0.0, 0.1, 0.9]]) >>> alpha = np.array([0.6, 0.2, 0.7]) - >>> categorical_focal_cross_entropy(true_labels, pred_probs, alpha) + >>> float(categorical_focal_cross_entropy(true_labels, pred_probs, alpha)) 0.0025966118981496423 >>> true_labels = np.array([[0, 1, 0], [0, 0, 1]]) >>> pred_probs = np.array([[0.05, 0.95, 0], [0.1, 0.8, 0.1]]) >>> alpha = np.array([0.25, 0.25, 0.25]) - >>> categorical_focal_cross_entropy(true_labels, pred_probs, alpha) + >>> float(categorical_focal_cross_entropy(true_labels, pred_probs, alpha)) 0.23315276982014324 >>> true_labels = np.array([[1, 0], [0, 1]]) @@ -265,7 +265,7 @@ def hinge_loss(y_true: np.ndarray, y_pred: np.ndarray) -> float: >>> true_labels = np.array([-1, 1, 1, -1, 1]) >>> pred = np.array([-4, -0.3, 0.7, 5, 10]) - >>> hinge_loss(true_labels, pred) + >>> float(hinge_loss(true_labels, pred)) 1.52 >>> true_labels = np.array([-1, 1, 1, -1, 1, 1]) >>> pred = np.array([-4, -0.3, 0.7, 5, 10]) @@ -309,11 +309,11 @@ def huber_loss(y_true: np.ndarray, y_pred: np.ndarray, delta: float) -> float: >>> true_values = np.array([0.9, 10.0, 2.0, 1.0, 5.2]) >>> predicted_values = np.array([0.8, 2.1, 2.9, 4.2, 5.2]) - >>> np.isclose(huber_loss(true_values, predicted_values, 1.0), 2.102) + >>> bool(np.isclose(huber_loss(true_values, predicted_values, 1.0), 2.102)) True >>> true_labels = np.array([11.0, 21.0, 3.32, 4.0, 5.0]) >>> predicted_probs = np.array([8.3, 20.8, 2.9, 11.2, 5.0]) - >>> np.isclose(huber_loss(true_labels, predicted_probs, 1.0), 1.80164) + >>> bool(np.isclose(huber_loss(true_labels, predicted_probs, 1.0), 1.80164)) True >>> true_labels = np.array([11.0, 21.0, 3.32, 4.0]) >>> predicted_probs = np.array([8.3, 20.8, 2.9, 11.2, 5.0]) @@ -347,7 +347,7 @@ def mean_squared_error(y_true: np.ndarray, y_pred: np.ndarray) -> float: >>> true_values = np.array([1.0, 2.0, 3.0, 4.0, 5.0]) >>> predicted_values = np.array([0.8, 2.1, 2.9, 4.2, 5.2]) - >>> np.isclose(mean_squared_error(true_values, predicted_values), 0.028) + >>> bool(np.isclose(mean_squared_error(true_values, predicted_values), 0.028)) True >>> true_labels = np.array([1.0, 2.0, 3.0, 4.0, 5.0]) >>> predicted_probs = np.array([0.3, 0.8, 0.9, 0.2]) @@ -381,11 +381,11 @@ def mean_absolute_error(y_true: np.ndarray, y_pred: np.ndarray) -> float: >>> true_values = np.array([1.0, 2.0, 3.0, 4.0, 5.0]) >>> predicted_values = np.array([0.8, 2.1, 2.9, 4.2, 5.2]) - >>> np.isclose(mean_absolute_error(true_values, predicted_values), 0.16) + >>> bool(np.isclose(mean_absolute_error(true_values, predicted_values), 0.16)) True >>> true_values = np.array([1.0, 2.0, 3.0, 4.0, 5.0]) >>> predicted_values = np.array([0.8, 2.1, 2.9, 4.2, 5.2]) - >>> np.isclose(mean_absolute_error(true_values, predicted_values), 2.16) + >>> bool(np.isclose(mean_absolute_error(true_values, predicted_values), 2.16)) False >>> true_labels = np.array([1.0, 2.0, 3.0, 4.0, 5.0]) >>> predicted_probs = np.array([0.3, 0.8, 0.9, 5.2]) @@ -420,7 +420,7 @@ def mean_squared_logarithmic_error(y_true: np.ndarray, y_pred: np.ndarray) -> fl >>> true_values = np.array([1.0, 2.0, 3.0, 4.0, 5.0]) >>> predicted_values = np.array([0.8, 2.1, 2.9, 4.2, 5.2]) - >>> mean_squared_logarithmic_error(true_values, predicted_values) + >>> float(mean_squared_logarithmic_error(true_values, predicted_values)) 0.0030860877925181344 >>> true_labels = np.array([1.0, 2.0, 3.0, 4.0, 5.0]) >>> predicted_probs = np.array([0.3, 0.8, 0.9, 0.2]) @@ -459,17 +459,17 @@ def mean_absolute_percentage_error( Examples: >>> y_true = np.array([10, 20, 30, 40]) >>> y_pred = np.array([12, 18, 33, 45]) - >>> mean_absolute_percentage_error(y_true, y_pred) + >>> float(mean_absolute_percentage_error(y_true, y_pred)) 0.13125 >>> y_true = np.array([1, 2, 3, 4]) >>> y_pred = np.array([2, 3, 4, 5]) - >>> mean_absolute_percentage_error(y_true, y_pred) + >>> float(mean_absolute_percentage_error(y_true, y_pred)) 0.5208333333333333 >>> y_true = np.array([34, 37, 44, 47, 48, 48, 46, 43, 32, 27, 26, 24]) >>> y_pred = np.array([37, 40, 46, 44, 46, 50, 45, 44, 34, 30, 22, 23]) - >>> mean_absolute_percentage_error(y_true, y_pred) + >>> float(mean_absolute_percentage_error(y_true, y_pred)) 0.064671076436071 """ if len(y_true) != len(y_pred): @@ -511,7 +511,7 @@ def perplexity_loss( ... [[0.03, 0.26, 0.21, 0.18, 0.30], ... [0.28, 0.10, 0.33, 0.15, 0.12]]] ... ) - >>> perplexity_loss(y_true, y_pred) + >>> float(perplexity_loss(y_true, y_pred)) 5.0247347775367945 >>> y_true = np.array([[1, 4], [2, 3]]) >>> y_pred = np.array( @@ -600,17 +600,17 @@ def smooth_l1_loss(y_true: np.ndarray, y_pred: np.ndarray, beta: float = 1.0) -> >>> y_true = np.array([3, 5, 2, 7]) >>> y_pred = np.array([2.9, 4.8, 2.1, 7.2]) - >>> smooth_l1_loss(y_true, y_pred, 1.0) + >>> float(smooth_l1_loss(y_true, y_pred, 1.0)) 0.012500000000000022 >>> y_true = np.array([2, 4, 6]) >>> y_pred = np.array([1, 5, 7]) - >>> smooth_l1_loss(y_true, y_pred, 1.0) + >>> float(smooth_l1_loss(y_true, y_pred, 1.0)) 0.5 >>> y_true = np.array([1, 3, 5, 7]) >>> y_pred = np.array([1, 3, 5, 7]) - >>> smooth_l1_loss(y_true, y_pred, 1.0) + >>> float(smooth_l1_loss(y_true, y_pred, 1.0)) 0.0 >>> y_true = np.array([1, 3, 5]) @@ -647,7 +647,7 @@ def kullback_leibler_divergence(y_true: np.ndarray, y_pred: np.ndarray) -> float >>> true_labels = np.array([0.2, 0.3, 0.5]) >>> predicted_probs = np.array([0.3, 0.3, 0.4]) - >>> kullback_leibler_divergence(true_labels, predicted_probs) + >>> float(kullback_leibler_divergence(true_labels, predicted_probs)) 0.030478754035472025 >>> true_labels = np.array([0.2, 0.3, 0.5]) >>> predicted_probs = np.array([0.3, 0.3, 0.4, 0.5]) diff --git a/machine_learning/mfcc.py b/machine_learning/mfcc.py index a1e99ce4ad40..dcc3151d5a1a 100644 --- a/machine_learning/mfcc.py +++ b/machine_learning/mfcc.py @@ -162,9 +162,9 @@ def normalize(audio: np.ndarray) -> np.ndarray: Examples: >>> audio = np.array([1, 2, 3, 4, 5]) >>> normalized_audio = normalize(audio) - >>> np.max(normalized_audio) + >>> float(np.max(normalized_audio)) 1.0 - >>> np.min(normalized_audio) + >>> float(np.min(normalized_audio)) 0.2 """ # Divide the entire audio signal by the maximum absolute value @@ -229,7 +229,8 @@ def calculate_fft(audio_windowed: np.ndarray, ftt_size: int = 1024) -> np.ndarra Examples: >>> audio_windowed = np.array([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]) >>> audio_fft = calculate_fft(audio_windowed, ftt_size=4) - >>> np.allclose(audio_fft[0], np.array([6.0+0.j, -1.5+0.8660254j, -1.5-0.8660254j])) + >>> bool(np.allclose(audio_fft[0], np.array([6.0+0.j, -1.5+0.8660254j, + ... -1.5-0.8660254j]))) True """ # Transpose the audio data to have time in rows and channels in columns @@ -281,7 +282,7 @@ def freq_to_mel(freq: float) -> float: The frequency in mel scale. Examples: - >>> round(freq_to_mel(1000), 2) + >>> float(round(freq_to_mel(1000), 2)) 999.99 """ # Use the formula to convert frequency to the mel scale @@ -321,7 +322,7 @@ def mel_spaced_filterbank( Mel-spaced filter bank. Examples: - >>> round(mel_spaced_filterbank(8000, 10, 1024)[0][1], 10) + >>> float(round(mel_spaced_filterbank(8000, 10, 1024)[0][1], 10)) 0.0004603981 """ freq_min = 0 @@ -438,7 +439,7 @@ def discrete_cosine_transform(dct_filter_num: int, filter_num: int) -> np.ndarra The DCT basis matrix. Examples: - >>> round(discrete_cosine_transform(3, 5)[0][0], 5) + >>> float(round(discrete_cosine_transform(3, 5)[0][0], 5)) 0.44721 """ basis = np.empty((dct_filter_num, filter_num)) diff --git a/machine_learning/multilayer_perceptron_classifier.py b/machine_learning/multilayer_perceptron_classifier.py index e99a4131e972..40f998c7dfa2 100644 --- a/machine_learning/multilayer_perceptron_classifier.py +++ b/machine_learning/multilayer_perceptron_classifier.py @@ -17,7 +17,7 @@ def wrapper(y): """ - >>> wrapper(Y) + >>> [int(x) for x in wrapper(Y)] [0, 0, 1] """ return list(y) diff --git a/machine_learning/scoring_functions.py b/machine_learning/scoring_functions.py index 08b969a95c3b..f6b685f4f98a 100644 --- a/machine_learning/scoring_functions.py +++ b/machine_learning/scoring_functions.py @@ -20,11 +20,11 @@ def mae(predict, actual): """ Examples(rounded for precision): >>> actual = [1,2,3];predict = [1,4,3] - >>> np.around(mae(predict,actual),decimals = 2) + >>> float(np.around(mae(predict,actual),decimals = 2)) 0.67 >>> actual = [1,1,1];predict = [1,1,1] - >>> mae(predict,actual) + >>> float(mae(predict,actual)) 0.0 """ predict = np.array(predict) @@ -41,11 +41,11 @@ def mse(predict, actual): """ Examples(rounded for precision): >>> actual = [1,2,3];predict = [1,4,3] - >>> np.around(mse(predict,actual),decimals = 2) + >>> float(np.around(mse(predict,actual),decimals = 2)) 1.33 >>> actual = [1,1,1];predict = [1,1,1] - >>> mse(predict,actual) + >>> float(mse(predict,actual)) 0.0 """ predict = np.array(predict) @@ -63,11 +63,11 @@ def rmse(predict, actual): """ Examples(rounded for precision): >>> actual = [1,2,3];predict = [1,4,3] - >>> np.around(rmse(predict,actual),decimals = 2) + >>> float(np.around(rmse(predict,actual),decimals = 2)) 1.15 >>> actual = [1,1,1];predict = [1,1,1] - >>> rmse(predict,actual) + >>> float(rmse(predict,actual)) 0.0 """ predict = np.array(predict) @@ -84,12 +84,10 @@ def rmse(predict, actual): def rmsle(predict, actual): """ Examples(rounded for precision): - >>> actual = [10,10,30];predict = [10,2,30] - >>> np.around(rmsle(predict,actual),decimals = 2) + >>> float(np.around(rmsle(predict=[10, 2, 30], actual=[10, 10, 30]), decimals=2)) 0.75 - >>> actual = [1,1,1];predict = [1,1,1] - >>> rmsle(predict,actual) + >>> float(rmsle(predict=[1, 1, 1], actual=[1, 1, 1])) 0.0 """ predict = np.array(predict) @@ -117,12 +115,12 @@ def mbd(predict, actual): Here the model overpredicts >>> actual = [1,2,3];predict = [2,3,4] - >>> np.around(mbd(predict,actual),decimals = 2) + >>> float(np.around(mbd(predict,actual),decimals = 2)) 50.0 Here the model underpredicts >>> actual = [1,2,3];predict = [0,1,1] - >>> np.around(mbd(predict,actual),decimals = 2) + >>> float(np.around(mbd(predict,actual),decimals = 2)) -66.67 """ predict = np.array(predict) diff --git a/machine_learning/similarity_search.py b/machine_learning/similarity_search.py index 0bc3b17d7e5a..c8a573796882 100644 --- a/machine_learning/similarity_search.py +++ b/machine_learning/similarity_search.py @@ -153,7 +153,7 @@ def cosine_similarity(input_a: np.ndarray, input_b: np.ndarray) -> float: >>> cosine_similarity(np.array([1, 2]), np.array([6, 32])) 0.9615239476408232 """ - return np.dot(input_a, input_b) / (norm(input_a) * norm(input_b)) + return float(np.dot(input_a, input_b) / (norm(input_a) * norm(input_b))) if __name__ == "__main__": diff --git a/machine_learning/support_vector_machines.py b/machine_learning/support_vector_machines.py index 24046115ebc4..d17c9044a3e9 100644 --- a/machine_learning/support_vector_machines.py +++ b/machine_learning/support_vector_machines.py @@ -14,11 +14,11 @@ def norm_squared(vector: ndarray) -> float: Returns: float: squared second norm of vector - >>> norm_squared([1, 2]) + >>> int(norm_squared([1, 2])) 5 - >>> norm_squared(np.asarray([1, 2])) + >>> int(norm_squared(np.asarray([1, 2]))) 5 - >>> norm_squared([0, 0]) + >>> int(norm_squared([0, 0])) 0 """ return np.dot(vector, vector) diff --git a/maths/euclidean_distance.py b/maths/euclidean_distance.py index 9b29b37b0ce6..aa7f3efc7684 100644 --- a/maths/euclidean_distance.py +++ b/maths/euclidean_distance.py @@ -13,13 +13,13 @@ def euclidean_distance(vector_1: Vector, vector_2: Vector) -> VectorOut: """ Calculate the distance between the two endpoints of two vectors. A vector is defined as a list, tuple, or numpy 1D array. - >>> euclidean_distance((0, 0), (2, 2)) + >>> float(euclidean_distance((0, 0), (2, 2))) 2.8284271247461903 - >>> euclidean_distance(np.array([0, 0, 0]), np.array([2, 2, 2])) + >>> float(euclidean_distance(np.array([0, 0, 0]), np.array([2, 2, 2]))) 3.4641016151377544 - >>> euclidean_distance(np.array([1, 2, 3, 4]), np.array([5, 6, 7, 8])) + >>> float(euclidean_distance(np.array([1, 2, 3, 4]), np.array([5, 6, 7, 8]))) 8.0 - >>> euclidean_distance([1, 2, 3, 4], [5, 6, 7, 8]) + >>> float(euclidean_distance([1, 2, 3, 4], [5, 6, 7, 8])) 8.0 """ return np.sqrt(np.sum((np.asarray(vector_1) - np.asarray(vector_2)) ** 2)) diff --git a/maths/euler_method.py b/maths/euler_method.py index 30f193e6daa5..c6adb07e2d3d 100644 --- a/maths/euler_method.py +++ b/maths/euler_method.py @@ -26,7 +26,7 @@ def explicit_euler( ... return y >>> y0 = 1 >>> y = explicit_euler(f, y0, 0.0, 0.01, 5) - >>> y[-1] + >>> float(y[-1]) 144.77277243257308 """ n = int(np.ceil((x_end - x0) / step_size)) diff --git a/maths/euler_modified.py b/maths/euler_modified.py index d02123e1e2fb..bb282e9f0ab9 100644 --- a/maths/euler_modified.py +++ b/maths/euler_modified.py @@ -24,13 +24,13 @@ def euler_modified( >>> def f1(x, y): ... return -2*x*(y**2) >>> y = euler_modified(f1, 1.0, 0.0, 0.2, 1.0) - >>> y[-1] + >>> float(y[-1]) 0.503338255442106 >>> import math >>> def f2(x, y): ... return -2*y + (x**3)*math.exp(-2*x) >>> y = euler_modified(f2, 1.0, 0.0, 0.1, 0.3) - >>> y[-1] + >>> float(y[-1]) 0.5525976431951775 """ n = int(np.ceil((x_end - x0) / step_size)) diff --git a/maths/gaussian.py b/maths/gaussian.py index 0e02010a9c67..b1e62ea77fe2 100644 --- a/maths/gaussian.py +++ b/maths/gaussian.py @@ -5,18 +5,18 @@ from numpy import exp, pi, sqrt -def gaussian(x, mu: float = 0.0, sigma: float = 1.0) -> int: +def gaussian(x, mu: float = 0.0, sigma: float = 1.0) -> float: """ - >>> gaussian(1) + >>> float(gaussian(1)) 0.24197072451914337 - >>> gaussian(24) + >>> float(gaussian(24)) 3.342714441794458e-126 - >>> gaussian(1, 4, 2) + >>> float(gaussian(1, 4, 2)) 0.06475879783294587 - >>> gaussian(1, 5, 3) + >>> float(gaussian(1, 5, 3)) 0.05467002489199788 Supports NumPy Arrays @@ -29,7 +29,7 @@ def gaussian(x, mu: float = 0.0, sigma: float = 1.0) -> int: 5.05227108e-15, 1.02797736e-18, 7.69459863e-23, 2.11881925e-27, 2.14638374e-32, 7.99882776e-38, 1.09660656e-43]) - >>> gaussian(15) + >>> float(gaussian(15)) 5.530709549844416e-50 >>> gaussian([1,2, 'string']) @@ -47,10 +47,10 @@ def gaussian(x, mu: float = 0.0, sigma: float = 1.0) -> int: ... OverflowError: (34, 'Result too large') - >>> gaussian(10**-326) + >>> float(gaussian(10**-326)) 0.3989422804014327 - >>> gaussian(2523, mu=234234, sigma=3425) + >>> float(gaussian(2523, mu=234234, sigma=3425)) 0.0 """ return 1 / sqrt(2 * pi * sigma**2) * exp(-((x - mu) ** 2) / (2 * sigma**2)) diff --git a/maths/minkowski_distance.py b/maths/minkowski_distance.py index 3237124e8d36..99f02e31e417 100644 --- a/maths/minkowski_distance.py +++ b/maths/minkowski_distance.py @@ -19,7 +19,7 @@ def minkowski_distance( >>> minkowski_distance([1.0, 2.0, 3.0, 4.0], [5.0, 6.0, 7.0, 8.0], 2) 8.0 >>> import numpy as np - >>> np.isclose(5.0, minkowski_distance([5.0], [0.0], 3)) + >>> bool(np.isclose(5.0, minkowski_distance([5.0], [0.0], 3))) True >>> minkowski_distance([1.0], [2.0], -1) Traceback (most recent call last): diff --git a/maths/numerical_analysis/adams_bashforth.py b/maths/numerical_analysis/adams_bashforth.py index fb406171098a..26244a58552f 100644 --- a/maths/numerical_analysis/adams_bashforth.py +++ b/maths/numerical_analysis/adams_bashforth.py @@ -102,7 +102,7 @@ def step_3(self) -> np.ndarray: >>> def f(x, y): ... return x + y >>> y = AdamsBashforth(f, [0, 0.2, 0.4], [0, 0, 0.04], 0.2, 1).step_3() - >>> y[3] + >>> float(y[3]) 0.15533333333333332 >>> AdamsBashforth(f, [0, 0.2], [0, 0], 0.2, 1).step_3() @@ -140,9 +140,9 @@ def step_4(self) -> np.ndarray: ... return x + y >>> y = AdamsBashforth( ... f, [0, 0.2, 0.4, 0.6], [0, 0, 0.04, 0.128], 0.2, 1).step_4() - >>> y[4] + >>> float(y[4]) 0.30699999999999994 - >>> y[5] + >>> float(y[5]) 0.5771083333333333 >>> AdamsBashforth(f, [0, 0.2, 0.4], [0, 0, 0.04], 0.2, 1).step_4() @@ -185,7 +185,7 @@ def step_5(self) -> np.ndarray: >>> y = AdamsBashforth( ... f, [0, 0.2, 0.4, 0.6, 0.8], [0, 0.02140, 0.02140, 0.22211, 0.42536], ... 0.2, 1).step_5() - >>> y[-1] + >>> float(y[-1]) 0.05436839444444452 >>> AdamsBashforth(f, [0, 0.2, 0.4], [0, 0, 0.04], 0.2, 1).step_5() diff --git a/maths/numerical_analysis/runge_kutta.py b/maths/numerical_analysis/runge_kutta.py index 4cac017ee89e..3a25b0fb0173 100644 --- a/maths/numerical_analysis/runge_kutta.py +++ b/maths/numerical_analysis/runge_kutta.py @@ -19,7 +19,7 @@ def runge_kutta(f, y0, x0, h, x_end): ... return y >>> y0 = 1 >>> y = runge_kutta(f, y0, 0.0, 0.01, 5) - >>> y[-1] + >>> float(y[-1]) 148.41315904125113 """ n = int(np.ceil((x_end - x0) / h)) diff --git a/maths/numerical_analysis/runge_kutta_fehlberg_45.py b/maths/numerical_analysis/runge_kutta_fehlberg_45.py index 8181fe3015fc..0fbd60a35c1a 100644 --- a/maths/numerical_analysis/runge_kutta_fehlberg_45.py +++ b/maths/numerical_analysis/runge_kutta_fehlberg_45.py @@ -34,12 +34,12 @@ def runge_kutta_fehlberg_45( >>> def f(x, y): ... return 1 + y**2 >>> y = runge_kutta_fehlberg_45(f, 0, 0, 0.2, 1) - >>> y[1] + >>> float(y[1]) 0.2027100937470787 >>> def f(x,y): ... return x >>> y = runge_kutta_fehlberg_45(f, -1, 0, 0.2, 0) - >>> y[1] + >>> float(y[1]) -0.18000000000000002 >>> y = runge_kutta_fehlberg_45(5, 0, 0, 0.1, 1) Traceback (most recent call last): diff --git a/maths/numerical_analysis/runge_kutta_gills.py b/maths/numerical_analysis/runge_kutta_gills.py index 451cde4cb935..5d9672679813 100644 --- a/maths/numerical_analysis/runge_kutta_gills.py +++ b/maths/numerical_analysis/runge_kutta_gills.py @@ -34,7 +34,7 @@ def runge_kutta_gills( >>> def f(x, y): ... return (x-y)/2 >>> y = runge_kutta_gills(f, 0, 3, 0.2, 5) - >>> y[-1] + >>> float(y[-1]) 3.4104259225717537 >>> def f(x,y): diff --git a/maths/softmax.py b/maths/softmax.py index 04cf77525420..95c95e66f59e 100644 --- a/maths/softmax.py +++ b/maths/softmax.py @@ -28,7 +28,7 @@ def softmax(vector): The softmax vector adds up to one. We need to ceil to mitigate for precision - >>> np.ceil(np.sum(softmax([1,2,3,4]))) + >>> float(np.ceil(np.sum(softmax([1,2,3,4])))) 1.0 >>> vec = np.array([5,5]) diff --git a/neural_network/two_hidden_layers_neural_network.py b/neural_network/two_hidden_layers_neural_network.py index d488de590cc2..1b7c0beed3ba 100644 --- a/neural_network/two_hidden_layers_neural_network.py +++ b/neural_network/two_hidden_layers_neural_network.py @@ -64,7 +64,7 @@ def feedforward(self) -> np.ndarray: >>> nn = TwoHiddenLayerNeuralNetwork(input_val, output_val) >>> res = nn.feedforward() >>> array_sum = np.sum(res) - >>> np.isnan(array_sum) + >>> bool(np.isnan(array_sum)) False """ # Layer_between_input_and_first_hidden_layer is the layer connecting the @@ -105,7 +105,7 @@ def back_propagation(self) -> None: >>> res = nn.feedforward() >>> nn.back_propagation() >>> updated_weights = nn.second_hidden_layer_and_output_layer_weights - >>> (res == updated_weights).all() + >>> bool((res == updated_weights).all()) False """ @@ -171,7 +171,7 @@ def train(self, output: np.ndarray, iterations: int, give_loss: bool) -> None: >>> first_iteration_weights = nn.feedforward() >>> nn.back_propagation() >>> updated_weights = nn.second_hidden_layer_and_output_layer_weights - >>> (first_iteration_weights == updated_weights).all() + >>> bool((first_iteration_weights == updated_weights).all()) False """ for iteration in range(1, iterations + 1): diff --git a/other/bankers_algorithm.py b/other/bankers_algorithm.py index 858eb0b2c524..d4254f479a4f 100644 --- a/other/bankers_algorithm.py +++ b/other/bankers_algorithm.py @@ -87,9 +87,11 @@ 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" Return: {0: [a: int, b: int], 1: [c: int, d: int]} - >>> (BankersAlgorithm(test_claim_vector, test_allocated_res_table, - ... test_maximum_claim_table)._BankersAlgorithm__need_index_manager() - ... ) # doctest: +NORMALIZE_WHITESPACE + >>> index_control = BankersAlgorithm( + ... test_claim_vector, test_allocated_res_table, test_maximum_claim_table + ... )._BankersAlgorithm__need_index_manager() + >>> {key: [int(x) for x in value] for key, value + ... in index_control.items()} # doctest: +NORMALIZE_WHITESPACE {0: [1, 2, 0, 3], 1: [0, 1, 3, 1], 2: [1, 1, 0, 2], 3: [1, 3, 2, 0], 4: [2, 0, 0, 3]} """ diff --git a/physics/in_static_equilibrium.py b/physics/in_static_equilibrium.py index e3c2f9d07aed..fb5a9b5fff66 100644 --- a/physics/in_static_equilibrium.py +++ b/physics/in_static_equilibrium.py @@ -53,7 +53,7 @@ def in_static_equilibrium( # summation of moments is zero moments: NDArray[float64] = cross(location, forces) sum_moments: float = sum(moments) - return abs(sum_moments) < eps + return bool(abs(sum_moments) < eps) if __name__ == "__main__": diff --git a/requirements.txt b/requirements.txt index bb3d671393b9..afbf25ba6edc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ beautifulsoup4 fake_useragent imageio -keras ; python_version < '3.12' +keras lxml matplotlib numpy @@ -17,7 +17,7 @@ rich scikit-learn statsmodels sympy -tensorflow +tensorflow ; python_version < '3.13' tweepy # yulewalker # uncomment once audio_filters/equal_loudness_filter.py is fixed typing_extensions diff --git a/web_programming/get_user_tweets.py b/web_programming/get_user_tweets.py.DISABLED similarity index 100% rename from web_programming/get_user_tweets.py rename to web_programming/get_user_tweets.py.DISABLED From 0abeeab39f4a612968a10b0541f630239b78f34f Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Tue, 1 Oct 2024 17:32:31 +0200 Subject: [PATCH 660/757] Drop six from our GitHub Actions (#11621) Drop https://six.readthedocs.io --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index dad2b2fac086..f54cc982d1ec 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -20,7 +20,7 @@ jobs: key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }} - name: Install dependencies run: | - python -m pip install --upgrade pip setuptools six wheel + python -m pip install --upgrade pip setuptools wheel python -m pip install pytest-cov -r requirements.txt - name: Run tests # TODO: #8818 Re-enable quantum tests From 43a47e01eb2c2b681fa377b02150edba5cc76e32 Mon Sep 17 00:00:00 2001 From: Hardik Pawar <97388607+Hardvan@users.noreply.github.com> Date: Wed, 2 Oct 2024 08:18:17 +0530 Subject: [PATCH 661/757] Add word ladder algorithm in backtracking (#11590) * Add word ladder algorithm in backtracking * Improve comments and implement ruff checks * updating DIRECTORY.md * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Change BFS to Backtracking * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Incorporate PR Changes * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Add type hints for backtrack function * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: Hardvan Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- DIRECTORY.md | 1 + backtracking/word_ladder.py | 100 ++++++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+) create mode 100644 backtracking/word_ladder.py diff --git a/DIRECTORY.md b/DIRECTORY.md index 56ab8377f16b..cdbbac684fd2 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -22,6 +22,7 @@ * [Rat In Maze](backtracking/rat_in_maze.py) * [Sudoku](backtracking/sudoku.py) * [Sum Of Subsets](backtracking/sum_of_subsets.py) + * [Word Ladder](backtracking/word_ladder.py) * [Word Search](backtracking/word_search.py) ## Bit Manipulation diff --git a/backtracking/word_ladder.py b/backtracking/word_ladder.py new file mode 100644 index 000000000000..7d9fd00f6669 --- /dev/null +++ b/backtracking/word_ladder.py @@ -0,0 +1,100 @@ +""" +Word Ladder is a classic problem in computer science. +The problem is to transform a start word into an end word +by changing one letter at a time. +Each intermediate word must be a valid word from a given list of words. +The goal is to find a transformation sequence +from the start word to the end word. + +Wikipedia: https://en.wikipedia.org/wiki/Word_ladder +""" + +import string + + +def backtrack( + current_word: str, path: list[str], end_word: str, word_set: set[str] +) -> list[str]: + """ + Helper function to perform backtracking to find the transformation + from the current_word to the end_word. + + Parameters: + current_word (str): The current word in the transformation sequence. + path (list[str]): The list of transformations from begin_word to current_word. + end_word (str): The target word for transformation. + word_set (set[str]): The set of valid words for transformation. + + Returns: + list[str]: The list of transformations from begin_word to end_word. + Returns an empty list if there is no valid + transformation from current_word to end_word. + + Example: + >>> backtrack("hit", ["hit"], "cog", {"hot", "dot", "dog", "lot", "log", "cog"}) + ['hit', 'hot', 'dot', 'lot', 'log', 'cog'] + + >>> backtrack("hit", ["hit"], "cog", {"hot", "dot", "dog", "lot", "log"}) + [] + + >>> backtrack("lead", ["lead"], "gold", {"load", "goad", "gold", "lead", "lord"}) + ['lead', 'lead', 'load', 'goad', 'gold'] + + >>> backtrack("game", ["game"], "code", {"came", "cage", "code", "cade", "gave"}) + ['game', 'came', 'cade', 'code'] + """ + + # Base case: If the current word is the end word, return the path + if current_word == end_word: + return path + + # Try all possible single-letter transformations + for i in range(len(current_word)): + for c in string.ascii_lowercase: # Try changing each letter + transformed_word = current_word[:i] + c + current_word[i + 1 :] + if transformed_word in word_set: + word_set.remove(transformed_word) + # Recur with the new word added to the path + result = backtrack( + transformed_word, [*path, transformed_word], end_word, word_set + ) + if result: # valid transformation found + return result + word_set.add(transformed_word) # backtrack + + return [] # No valid transformation found + + +def word_ladder(begin_word: str, end_word: str, word_set: set[str]) -> list[str]: + """ + Solve the Word Ladder problem using Backtracking and return + the list of transformations from begin_word to end_word. + + Parameters: + begin_word (str): The word from which the transformation starts. + end_word (str): The target word for transformation. + word_list (list[str]): The list of valid words for transformation. + + Returns: + list[str]: The list of transformations from begin_word to end_word. + Returns an empty list if there is no valid transformation. + + Example: + >>> word_ladder("hit", "cog", ["hot", "dot", "dog", "lot", "log", "cog"]) + ['hit', 'hot', 'dot', 'lot', 'log', 'cog'] + + >>> word_ladder("hit", "cog", ["hot", "dot", "dog", "lot", "log"]) + [] + + >>> word_ladder("lead", "gold", ["load", "goad", "gold", "lead", "lord"]) + ['lead', 'lead', 'load', 'goad', 'gold'] + + >>> word_ladder("game", "code", ["came", "cage", "code", "cade", "gave"]) + ['game', 'came', 'cade', 'code'] + """ + + if end_word not in word_set: # no valid transformation possible + return [] + + # Perform backtracking starting from the begin_word + return backtrack(begin_word, [begin_word], end_word, word_set) From 00e9d862248a27281d4de24c8c7eb2d7b018531c Mon Sep 17 00:00:00 2001 From: Hardik Pawar <97388607+Hardvan@users.noreply.github.com> Date: Wed, 2 Oct 2024 08:24:12 +0530 Subject: [PATCH 662/757] Improve comments, add doctests in symmetric_tree.py (#11619) --- data_structures/binary_tree/symmetric_tree.py | 67 +++++++++++++++++-- 1 file changed, 62 insertions(+), 5 deletions(-) diff --git a/data_structures/binary_tree/symmetric_tree.py b/data_structures/binary_tree/symmetric_tree.py index 98a766cab988..2bfeac98b2c9 100644 --- a/data_structures/binary_tree/symmetric_tree.py +++ b/data_structures/binary_tree/symmetric_tree.py @@ -13,7 +13,21 @@ @dataclass class Node: """ - A Node has data variable and pointers to Nodes to its left and right. + A Node represents an element of a binary tree, which contains: + + Attributes: + data: The value stored in the node (int). + left: Pointer to the left child node (Node or None). + right: Pointer to the right child node (Node or None). + + Example: + >>> node = Node(1, Node(2), Node(3)) + >>> node.data + 1 + >>> node.left.data + 2 + >>> node.right.data + 3 """ data: int @@ -24,12 +38,25 @@ class Node: def make_symmetric_tree() -> Node: r""" Create a symmetric tree for testing. + The tree looks like this: 1 / \ 2 2 / \ / \ 3 4 4 3 + + Returns: + Node: Root node of a symmetric tree. + + Example: + >>> tree = make_symmetric_tree() + >>> tree.data + 1 + >>> tree.left.data == tree.right.data + True + >>> tree.left.left.data == tree.right.right.data + True """ root = Node(1) root.left = Node(2) @@ -43,13 +70,26 @@ def make_symmetric_tree() -> Node: def make_asymmetric_tree() -> Node: r""" - Create a asymmetric tree for testing. + Create an asymmetric tree for testing. + The tree looks like this: 1 / \ 2 2 / \ / \ 3 4 3 4 + + Returns: + Node: Root node of an asymmetric tree. + + Example: + >>> tree = make_asymmetric_tree() + >>> tree.data + 1 + >>> tree.left.data == tree.right.data + True + >>> tree.left.left.data == tree.right.right.data + False """ root = Node(1) root.left = Node(2) @@ -63,7 +103,15 @@ def make_asymmetric_tree() -> Node: def is_symmetric_tree(tree: Node) -> bool: """ - Test cases for is_symmetric_tree function + Check if a binary tree is symmetric (i.e., a mirror of itself). + + Parameters: + tree: The root node of the binary tree. + + Returns: + bool: True if the tree is symmetric, False otherwise. + + Example: >>> is_symmetric_tree(make_symmetric_tree()) True >>> is_symmetric_tree(make_asymmetric_tree()) @@ -76,8 +124,17 @@ def is_symmetric_tree(tree: Node) -> bool: def is_mirror(left: Node | None, right: Node | None) -> bool: """ + Check if two subtrees are mirror images of each other. + + Parameters: + left: The root node of the left subtree. + right: The root node of the right subtree. + + Returns: + bool: True if the two subtrees are mirrors of each other, False otherwise. + + Example: >>> tree1 = make_symmetric_tree() - >>> tree1.right.right = Node(3) >>> is_mirror(tree1.left, tree1.right) True >>> tree2 = make_asymmetric_tree() @@ -91,7 +148,7 @@ def is_mirror(left: Node | None, right: Node | None) -> bool: # One side is empty while the other is not, which is not symmetric. return False if left.data == right.data: - # The values match, so check the subtree + # The values match, so check the subtrees recursively. return is_mirror(left.left, right.right) and is_mirror(left.right, right.left) return False From 918fa8bb8ae1f052921fffd188d229d4713c73c9 Mon Sep 17 00:00:00 2001 From: 1227haran <68032825+1227haran@users.noreply.github.com> Date: Wed, 2 Oct 2024 23:37:07 +0530 Subject: [PATCH 663/757] Optimized O(n) to O(1) (#11669) --- data_structures/linked_list/has_loop.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data_structures/linked_list/has_loop.py b/data_structures/linked_list/has_loop.py index bc06ffe150e8..f49e01579adc 100644 --- a/data_structures/linked_list/has_loop.py +++ b/data_structures/linked_list/has_loop.py @@ -14,11 +14,11 @@ def __init__(self, data: Any) -> None: def __iter__(self): node = self - visited = [] + visited = set() while node: if node in visited: raise ContainsLoopError - visited.append(node) + visited.add(node) yield node.data node = node.next_node From f4b4ac159a17e0621e7f37141b165d58ca655b81 Mon Sep 17 00:00:00 2001 From: Ali Rashid <110668489+alirashidAR@users.noreply.github.com> Date: Thu, 3 Oct 2024 05:24:56 +0530 Subject: [PATCH 664/757] Adding Doctests to floyd_warshall.py (#11690) * Ruff test resolution * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- dynamic_programming/floyd_warshall.py | 47 +++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/dynamic_programming/floyd_warshall.py b/dynamic_programming/floyd_warshall.py index 2331f3e65483..b92c6667fb5c 100644 --- a/dynamic_programming/floyd_warshall.py +++ b/dynamic_programming/floyd_warshall.py @@ -12,19 +12,58 @@ def __init__(self, n=0): # a graph with Node 0,1,...,N-1 ] # dp[i][j] stores minimum distance from i to j def add_edge(self, u, v, w): + """ + Adds a directed edge from node u + to node v with weight w. + + >>> g = Graph(3) + >>> g.add_edge(0, 1, 5) + >>> g.dp[0][1] + 5 + """ self.dp[u][v] = w def floyd_warshall(self): + """ + Computes the shortest paths between all pairs of + nodes using the Floyd-Warshall algorithm. + + >>> g = Graph(3) + >>> g.add_edge(0, 1, 1) + >>> g.add_edge(1, 2, 2) + >>> g.floyd_warshall() + >>> g.show_min(0, 2) + 3 + >>> g.show_min(2, 0) + inf + """ for k in range(self.n): for i in range(self.n): for j in range(self.n): self.dp[i][j] = min(self.dp[i][j], self.dp[i][k] + self.dp[k][j]) def show_min(self, u, v): + """ + Returns the minimum distance from node u to node v. + + >>> g = Graph(3) + >>> g.add_edge(0, 1, 3) + >>> g.add_edge(1, 2, 4) + >>> g.floyd_warshall() + >>> g.show_min(0, 2) + 7 + >>> g.show_min(1, 0) + inf + """ return self.dp[u][v] if __name__ == "__main__": + import doctest + + doctest.testmod() + + # Example usage graph = Graph(5) graph.add_edge(0, 2, 9) graph.add_edge(0, 4, 10) @@ -38,5 +77,9 @@ def show_min(self, u, v): graph.add_edge(4, 2, 4) graph.add_edge(4, 3, 9) graph.floyd_warshall() - graph.show_min(1, 4) - graph.show_min(0, 3) + print( + graph.show_min(1, 4) + ) # Should output the minimum distance from node 1 to node 4 + print( + graph.show_min(0, 3) + ) # Should output the minimum distance from node 0 to node 3 From 080e7903a06765808c12c0c9c0b242f485cb9ce7 Mon Sep 17 00:00:00 2001 From: Aswin P Kumar <118362715+AswinPKumar01@users.noreply.github.com> Date: Thu, 3 Oct 2024 05:33:48 +0530 Subject: [PATCH 665/757] Add Word Break algorithm (#11687) * Add Word Break algorithm * Add Word Break algorithm * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- backtracking/word_break.py | 71 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 backtracking/word_break.py diff --git a/backtracking/word_break.py b/backtracking/word_break.py new file mode 100644 index 000000000000..1f2ab073f499 --- /dev/null +++ b/backtracking/word_break.py @@ -0,0 +1,71 @@ +""" +Word Break Problem is a well-known problem in computer science. +Given a string and a dictionary of words, the task is to determine if +the string can be segmented into a sequence of one or more dictionary words. + +Wikipedia: https://en.wikipedia.org/wiki/Word_break_problem +""" + + +def backtrack(input_string: str, word_dict: set[str], start: int) -> bool: + """ + Helper function that uses backtracking to determine if a valid + word segmentation is possible starting from index 'start'. + + Parameters: + input_string (str): The input string to be segmented. + word_dict (set[str]): A set of valid dictionary words. + start (int): The starting index of the substring to be checked. + + Returns: + bool: True if a valid segmentation is possible, otherwise False. + + Example: + >>> backtrack("leetcode", {"leet", "code"}, 0) + True + + >>> backtrack("applepenapple", {"apple", "pen"}, 0) + True + + >>> backtrack("catsandog", {"cats", "dog", "sand", "and", "cat"}, 0) + False + """ + + # Base case: if the starting index has reached the end of the string + if start == len(input_string): + return True + + # Try every possible substring from 'start' to 'end' + for end in range(start + 1, len(input_string) + 1): + if input_string[start:end] in word_dict and backtrack( + input_string, word_dict, end + ): + return True + + return False + + +def word_break(input_string: str, word_dict: set[str]) -> bool: + """ + Determines if the input string can be segmented into a sequence of + valid dictionary words using backtracking. + + Parameters: + input_string (str): The input string to segment. + word_dict (set[str]): The set of valid words. + + Returns: + bool: True if the string can be segmented into valid words, otherwise False. + + Example: + >>> word_break("leetcode", {"leet", "code"}) + True + + >>> word_break("applepenapple", {"apple", "pen"}) + True + + >>> word_break("catsandog", {"cats", "dog", "sand", "and", "cat"}) + False + """ + + return backtrack(input_string, word_dict, 0) From 40f65e8150045dc82a7a58fe7cff6bfb353999f2 Mon Sep 17 00:00:00 2001 From: JeevaRamanathan <64531160+JeevaRamanathan@users.noreply.github.com> Date: Thu, 3 Oct 2024 05:48:01 +0530 Subject: [PATCH 666/757] Improve comments, docstrings in next_greatest_element.py (#11685) * Improve comments in next_greatest_element.py Signed-off-by: JeevaRamanathan * few changes Signed-off-by: JeevaRamanathan * updated descriptions of the functions parameters Signed-off-by: JeevaRamanathan --------- Signed-off-by: JeevaRamanathan --- .../stacks/next_greater_element.py | 60 ++++++++++++++----- 1 file changed, 46 insertions(+), 14 deletions(-) diff --git a/data_structures/stacks/next_greater_element.py b/data_structures/stacks/next_greater_element.py index 7d76d1f47dfa..216850b4b894 100644 --- a/data_structures/stacks/next_greater_element.py +++ b/data_structures/stacks/next_greater_element.py @@ -6,9 +6,20 @@ def next_greatest_element_slow(arr: list[float]) -> list[float]: """ - Get the Next Greatest Element (NGE) for all elements in a list. - Maximum element present after the current one which is also greater than the - current one. + Get the Next Greatest Element (NGE) for each element in the array + by checking all subsequent elements to find the next greater one. + + This is a brute-force implementation, and it has a time complexity + of O(n^2), where n is the size of the array. + + Args: + arr: List of numbers for which the NGE is calculated. + + Returns: + List containing the next greatest elements. If no + greater element is found, -1 is placed in the result. + + Example: >>> next_greatest_element_slow(arr) == expect True """ @@ -28,9 +39,21 @@ def next_greatest_element_slow(arr: list[float]) -> list[float]: def next_greatest_element_fast(arr: list[float]) -> list[float]: """ - Like next_greatest_element_slow() but changes the loops to use - enumerate() instead of range(len()) for the outer loop and - for in a slice of arr for the inner loop. + Find the Next Greatest Element (NGE) for each element in the array + using a more readable approach. This implementation utilizes + enumerate() for the outer loop and slicing for the inner loop. + + While this improves readability over next_greatest_element_slow(), + it still has a time complexity of O(n^2). + + Args: + arr: List of numbers for which the NGE is calculated. + + Returns: + List containing the next greatest elements. If no + greater element is found, -1 is placed in the result. + + Example: >>> next_greatest_element_fast(arr) == expect True """ @@ -47,14 +70,23 @@ def next_greatest_element_fast(arr: list[float]) -> list[float]: def next_greatest_element(arr: list[float]) -> list[float]: """ - Get the Next Greatest Element (NGE) for all elements in a list. - Maximum element present after the current one which is also greater than the - current one. - - A naive way to solve this is to take two loops and check for the next bigger - number but that will make the time complexity as O(n^2). The better way to solve - this would be to use a stack to keep track of maximum number giving a linear time - solution. + Efficient solution to find the Next Greatest Element (NGE) for all elements + using a stack. The time complexity is reduced to O(n), making it suitable + for larger arrays. + + The stack keeps track of elements for which the next greater element hasn't + been found yet. By iterating through the array in reverse (from the last + element to the first), the stack is used to efficiently determine the next + greatest element for each element. + + Args: + arr: List of numbers for which the NGE is calculated. + + Returns: + List containing the next greatest elements. If no + greater element is found, -1 is placed in the result. + + Example: >>> next_greatest_element(arr) == expect True """ From e20b503b24fc271321a23584772ad8f0db17daf2 Mon Sep 17 00:00:00 2001 From: Hardik Pawar <97388607+Hardvan@users.noreply.github.com> Date: Fri, 4 Oct 2024 14:36:08 +0530 Subject: [PATCH 667/757] Improve comments, add doctests for kahns_algorithm_topo.py (#11668) * Improve comments, add doctests for kahns_algorithm_topo.py * Improve function docstring * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Rename variables, remove print --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- graphs/kahns_algorithm_topo.py | 67 +++++++++++++++++++++++----------- 1 file changed, 46 insertions(+), 21 deletions(-) diff --git a/graphs/kahns_algorithm_topo.py b/graphs/kahns_algorithm_topo.py index b1260bd5bd9b..c956cf9f48fd 100644 --- a/graphs/kahns_algorithm_topo.py +++ b/graphs/kahns_algorithm_topo.py @@ -1,36 +1,61 @@ -def topological_sort(graph): +def topological_sort(graph: dict[int, list[int]]) -> list[int] | None: """ - Kahn's Algorithm is used to find Topological ordering of Directed Acyclic Graph - using BFS + Perform topological sorting of a Directed Acyclic Graph (DAG) + using Kahn's Algorithm via Breadth-First Search (BFS). + + Topological sorting is a linear ordering of vertices in a graph such that for + every directed edge u → v, vertex u comes before vertex v in the ordering. + + Parameters: + graph: Adjacency list representing the directed graph where keys are + vertices, and values are lists of adjacent vertices. + + Returns: + The topologically sorted order of vertices if the graph is a DAG. + Returns None if the graph contains a cycle. + + Example: + >>> graph = {0: [1, 2], 1: [3], 2: [3], 3: [4, 5], 4: [], 5: []} + >>> topological_sort(graph) + [0, 1, 2, 3, 4, 5] + + >>> graph_with_cycle = {0: [1], 1: [2], 2: [0]} + >>> topological_sort(graph_with_cycle) """ + indegree = [0] * len(graph) queue = [] - topo = [] - cnt = 0 + topo_order = [] + processed_vertices_count = 0 + # Calculate the indegree of each vertex for values in graph.values(): for i in values: indegree[i] += 1 + # Add all vertices with 0 indegree to the queue for i in range(len(indegree)): if indegree[i] == 0: queue.append(i) + # Perform BFS while queue: vertex = queue.pop(0) - cnt += 1 - topo.append(vertex) - for x in graph[vertex]: - indegree[x] -= 1 - if indegree[x] == 0: - queue.append(x) - - if cnt != len(graph): - print("Cycle exists") - else: - print(topo) - - -# Adjacency List of Graph -graph = {0: [1, 2], 1: [3], 2: [3], 3: [4, 5], 4: [], 5: []} -topological_sort(graph) + processed_vertices_count += 1 + topo_order.append(vertex) + + # Traverse neighbors + for neighbor in graph[vertex]: + indegree[neighbor] -= 1 + if indegree[neighbor] == 0: + queue.append(neighbor) + + if processed_vertices_count != len(graph): + return None # no topological ordering exists due to cycle + return topo_order # valid topological ordering + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 917ad62105dc829e45c0732d9ac2aae7ef358627 Mon Sep 17 00:00:00 2001 From: Sai Aswin Madhavan Date: Fri, 4 Oct 2024 14:58:50 +0530 Subject: [PATCH 668/757] Removed incorrect type hints (#11711) --- strings/min_cost_string_conversion.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/strings/min_cost_string_conversion.py b/strings/min_cost_string_conversion.py index d147a9d7954c..40d54f0e8420 100644 --- a/strings/min_cost_string_conversion.py +++ b/strings/min_cost_string_conversion.py @@ -21,7 +21,6 @@ def compute_transform_tables( destination_seq = list(destination_string) len_source_seq = len(source_seq) len_destination_seq = len(destination_seq) - costs = [ [0 for _ in range(len_destination_seq + 1)] for _ in range(len_source_seq + 1) ] @@ -31,28 +30,28 @@ def compute_transform_tables( for i in range(1, len_source_seq + 1): costs[i][0] = i * delete_cost - ops[i][0] = f"D{source_seq[i - 1]:c}" + ops[i][0] = f"D{source_seq[i - 1]}" for i in range(1, len_destination_seq + 1): costs[0][i] = i * insert_cost - ops[0][i] = f"I{destination_seq[i - 1]:c}" + ops[0][i] = f"I{destination_seq[i - 1]}" for i in range(1, len_source_seq + 1): for j in range(1, len_destination_seq + 1): if source_seq[i - 1] == destination_seq[j - 1]: costs[i][j] = costs[i - 1][j - 1] + copy_cost - ops[i][j] = f"C{source_seq[i - 1]:c}" + ops[i][j] = f"C{source_seq[i - 1]}" else: costs[i][j] = costs[i - 1][j - 1] + replace_cost - ops[i][j] = f"R{source_seq[i - 1]:c}" + str(destination_seq[j - 1]) + ops[i][j] = f"R{source_seq[i - 1]}" + str(destination_seq[j - 1]) if costs[i - 1][j] + delete_cost < costs[i][j]: costs[i][j] = costs[i - 1][j] + delete_cost - ops[i][j] = f"D{source_seq[i - 1]:c}" + ops[i][j] = f"D{source_seq[i - 1]}" if costs[i][j - 1] + insert_cost < costs[i][j]: costs[i][j] = costs[i][j - 1] + insert_cost - ops[i][j] = f"I{destination_seq[j - 1]:c}" + ops[i][j] = f"I{destination_seq[j - 1]}" return costs, ops From 59ff87dc55b704dc7d3683bb6fabc7c4dc0afade Mon Sep 17 00:00:00 2001 From: Lonercode <91500485+Lonercode@users.noreply.github.com> Date: Fri, 4 Oct 2024 10:36:14 +0100 Subject: [PATCH 669/757] Added doctests to min_cost_string_conversion.py and removed :c specifier (#11721) * Added doctests to min_cost_string_conversion.py and removed :c specifier * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * resolved line length issues based on ruff requirements * modified in compliance with ruff for line length * Update strings/min_cost_string_conversion.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Tianyi Zheng --- strings/min_cost_string_conversion.py | 35 +++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/strings/min_cost_string_conversion.py b/strings/min_cost_string_conversion.py index 40d54f0e8420..a5a3c4a4e3f8 100644 --- a/strings/min_cost_string_conversion.py +++ b/strings/min_cost_string_conversion.py @@ -17,6 +17,23 @@ def compute_transform_tables( delete_cost: int, insert_cost: int, ) -> tuple[list[list[int]], list[list[str]]]: + """ + Finds the most cost efficient sequence + for converting one string into another. + + >>> costs, operations = compute_transform_tables("cat", "cut", 1, 2, 3, 3) + >>> costs[0][:4] + [0, 3, 6, 9] + >>> costs[2][:4] + [6, 4, 3, 6] + >>> operations[0][:4] + ['0', 'Ic', 'Iu', 'It'] + >>> operations[3][:4] + ['Dt', 'Dt', 'Rtu', 'Ct'] + + >>> compute_transform_tables("", "", 1, 2, 3, 3) + ([[0]], [['0']]) + """ source_seq = list(source_string) destination_seq = list(destination_string) len_source_seq = len(source_seq) @@ -57,6 +74,24 @@ def compute_transform_tables( def assemble_transformation(ops: list[list[str]], i: int, j: int) -> list[str]: + """ + Assembles the transformations based on the ops table. + + >>> ops = [['0', 'Ic', 'Iu', 'It'], + ... ['Dc', 'Cc', 'Iu', 'It'], + ... ['Da', 'Da', 'Rau', 'Rat'], + ... ['Dt', 'Dt', 'Rtu', 'Ct']] + >>> x = len(ops) - 1 + >>> y = len(ops[0]) - 1 + >>> assemble_transformation(ops, x, y) + ['Cc', 'Rau', 'Ct'] + + >>> ops1 = [['0']] + >>> x1 = len(ops1) - 1 + >>> y1 = len(ops1[0]) - 1 + >>> assemble_transformation(ops1, x1, y1) + [] + """ if i == 0 and j == 0: return [] elif ops[i][j][0] in {"C", "R"}: From 9a572dec2b6011e7c2c0d82f50989b3a404ea426 Mon Sep 17 00:00:00 2001 From: ARNAV RAJ <126798788+Acuspeedster@users.noreply.github.com> Date: Fri, 4 Oct 2024 21:59:39 +0530 Subject: [PATCH 670/757] feat: Implemented Matrix Exponentiation Method (#11747) * feat: add Matrix Exponentiation method docs: updated the header documentation and added new documentation for the new function. * feat: added new function matrix exponetiation method * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * feat: This function uses the tail-recursive form of the Euclidean algorithm to calculate * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * reduced the number of characters per line in the comments * removed unwanted code * feat: Implemented a new function to swaap numbers without dummy variable * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * removed previos code * Done with the required changes * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Done with the required changes * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Done with the required changes * Done with the required changes * Done with the required changes * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update maths/fibonacci.py Co-authored-by: Tianyi Zheng * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Done with the required changes * Done with the required changes * Done with the required changes * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Tianyi Zheng --- maths/fibonacci.py | 88 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/maths/fibonacci.py b/maths/fibonacci.py index 927700b0418e..24b2d7ae449e 100644 --- a/maths/fibonacci.py +++ b/maths/fibonacci.py @@ -7,6 +7,8 @@ NOTE 2: the Binet's formula function is much more limited in the size of inputs that it can handle due to the size limitations of Python floats +NOTE 3: the matrix function is the fastest and most memory efficient for large n + See benchmark numbers in __main__ for performance comparisons/ https://en.wikipedia.org/wiki/Fibonacci_number for more information @@ -17,6 +19,9 @@ from math import sqrt from time import time +import numpy as np +from numpy import ndarray + def time_func(func, *args, **kwargs): """ @@ -230,6 +235,88 @@ def fib_binet(n: int) -> list[int]: return [round(phi**i / sqrt_5) for i in range(n + 1)] +def matrix_pow_np(m: ndarray, power: int) -> ndarray: + """ + Raises a matrix to the power of 'power' using binary exponentiation. + + Args: + m: Matrix as a numpy array. + power: The power to which the matrix is to be raised. + + Returns: + The matrix raised to the power. + + Raises: + ValueError: If power is negative. + + >>> m = np.array([[1, 1], [1, 0]], dtype=int) + >>> matrix_pow_np(m, 0) # Identity matrix when raised to the power of 0 + array([[1, 0], + [0, 1]]) + + >>> matrix_pow_np(m, 1) # Same matrix when raised to the power of 1 + array([[1, 1], + [1, 0]]) + + >>> matrix_pow_np(m, 5) + array([[8, 5], + [5, 3]]) + + >>> matrix_pow_np(m, -1) + Traceback (most recent call last): + ... + ValueError: power is negative + """ + result = np.array([[1, 0], [0, 1]], dtype=int) # Identity Matrix + base = m + if power < 0: # Negative power is not allowed + raise ValueError("power is negative") + while power: + if power % 2 == 1: + result = np.dot(result, base) + base = np.dot(base, base) + power //= 2 + return result + + +def fib_matrix_np(n: int) -> int: + """ + Calculates the n-th Fibonacci number using matrix exponentiation. + https://www.nayuki.io/page/fast-fibonacci-algorithms#:~:text= + Summary:%20The%20two%20fast%20Fibonacci%20algorithms%20are%20matrix + + Args: + n: Fibonacci sequence index + + Returns: + The n-th Fibonacci number. + + Raises: + ValueError: If n is negative. + + >>> fib_matrix_np(0) + 0 + >>> fib_matrix_np(1) + 1 + >>> fib_matrix_np(5) + 5 + >>> fib_matrix_np(10) + 55 + >>> fib_matrix_np(-1) + Traceback (most recent call last): + ... + ValueError: n is negative + """ + if n < 0: + raise ValueError("n is negative") + if n == 0: + return 0 + + m = np.array([[1, 1], [1, 0]], dtype=int) + result = matrix_pow_np(m, n - 1) + return int(result[0, 0]) + + if __name__ == "__main__": from doctest import testmod @@ -242,3 +329,4 @@ def fib_binet(n: int) -> list[int]: time_func(fib_memoization, num) # 0.0100 ms time_func(fib_recursive_cached, num) # 0.0153 ms time_func(fib_recursive, num) # 257.0910 ms + time_func(fib_matrix_np, num) # 0.0000 ms From 5a8655d306d872085112d965067fcdc440286928 Mon Sep 17 00:00:00 2001 From: 1227haran <68032825+1227haran@users.noreply.github.com> Date: Sat, 5 Oct 2024 22:49:58 +0530 Subject: [PATCH 671/757] Added new algorithm to generate numbers in lexicographical order (#11674) * Added algorithm to generate numbers in lexicographical order * Removed the test cases * Updated camelcase to snakecase * Added doctest * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Added descriptive name for n * Reduced the number of letters * Updated the return type * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Updated import statement * Updated return type to Iterator[int] * removed parentheses --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .../stacks/lexicographical_numbers.py | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 data_structures/stacks/lexicographical_numbers.py diff --git a/data_structures/stacks/lexicographical_numbers.py b/data_structures/stacks/lexicographical_numbers.py new file mode 100644 index 000000000000..6a174e7d9e95 --- /dev/null +++ b/data_structures/stacks/lexicographical_numbers.py @@ -0,0 +1,38 @@ +from collections.abc import Iterator + + +def lexical_order(max_number: int) -> Iterator[int]: + """ + Generate numbers in lexical order from 1 to max_number. + + >>> " ".join(map(str, lexical_order(13))) + '1 10 11 12 13 2 3 4 5 6 7 8 9' + >>> list(lexical_order(1)) + [1] + >>> " ".join(map(str, lexical_order(20))) + '1 10 11 12 13 14 15 16 17 18 19 2 20 3 4 5 6 7 8 9' + >>> " ".join(map(str, lexical_order(25))) + '1 10 11 12 13 14 15 16 17 18 19 2 20 21 22 23 24 25 3 4 5 6 7 8 9' + >>> list(lexical_order(12)) + [1, 10, 11, 12, 2, 3, 4, 5, 6, 7, 8, 9] + """ + + stack = [1] + + while stack: + num = stack.pop() + if num > max_number: + continue + + yield num + if (num % 10) != 9: + stack.append(num + 1) + + stack.append(num * 10) + + +if __name__ == "__main__": + from doctest import testmod + + testmod() + print(f"Numbers from 1 to 25 in lexical order: {list(lexical_order(26))}") From 50aca04c67315ef7de7ef03e51a018075d8d026b Mon Sep 17 00:00:00 2001 From: Jeel Rupapara Date: Sat, 5 Oct 2024 22:51:43 +0530 Subject: [PATCH 672/757] feat: increase test coverage of longest_common_subsequence to 75% (#11777) --- .../longest_common_subsequence.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/dynamic_programming/longest_common_subsequence.py b/dynamic_programming/longest_common_subsequence.py index 9a98b1736ed5..4a6c880aff61 100644 --- a/dynamic_programming/longest_common_subsequence.py +++ b/dynamic_programming/longest_common_subsequence.py @@ -28,6 +28,24 @@ def longest_common_subsequence(x: str, y: str): (2, 'ph') >>> longest_common_subsequence("computer", "food") (1, 'o') + >>> longest_common_subsequence("", "abc") # One string is empty + (0, '') + >>> longest_common_subsequence("abc", "") # Other string is empty + (0, '') + >>> longest_common_subsequence("", "") # Both strings are empty + (0, '') + >>> longest_common_subsequence("abc", "def") # No common subsequence + (0, '') + >>> longest_common_subsequence("abc", "abc") # Identical strings + (3, 'abc') + >>> longest_common_subsequence("a", "a") # Single character match + (1, 'a') + >>> longest_common_subsequence("a", "b") # Single character no match + (0, '') + >>> longest_common_subsequence("abcdef", "ace") # Interleaved subsequence + (3, 'ace') + >>> longest_common_subsequence("ABCD", "ACBD") # No repeated characters + (3, 'ABD') """ # find the length of strings From ad6395d3408b9d80a0bef4d180d1e7613a55d807 Mon Sep 17 00:00:00 2001 From: Andrey Ivanov <97749666+ivnvxd@users.noreply.github.com> Date: Sat, 5 Oct 2024 18:24:58 +0100 Subject: [PATCH 673/757] Update ruff usage example in CONTRIBUTING.md (#11772) * Update ruff usage example * Update CONTRIBUTING.md Co-authored-by: Tianyi Zheng --------- Co-authored-by: Tianyi Zheng --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 096582e45afa..b5113212929a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -96,7 +96,7 @@ We want your work to be readable by others; therefore, we encourage you to note ```bash python3 -m pip install ruff # only required the first time - ruff . + ruff check ``` - Original code submission require docstrings or comments to describe your work. From fcf82a1eda21dcf36254a8fcaadc913f6a94c8da Mon Sep 17 00:00:00 2001 From: Vineet Kumar <108144301+whyvineet@users.noreply.github.com> Date: Sat, 5 Oct 2024 23:04:48 +0530 Subject: [PATCH 674/757] =?UTF-8?q?Implemented=20Exponential=20Search=20wi?= =?UTF-8?q?th=20binary=20search=20for=20improved=20perfor=E2=80=A6=20(#116?= =?UTF-8?q?66)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Implemented Exponential Search with binary search for improved performance on large sorted arrays. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Added type hints and doctests for binary_search and exponential_search functions. Improved code documentation and ensured testability. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update and rename Exponential_Search.py to exponential_search.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- searches/exponential_search.py | 113 +++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 searches/exponential_search.py diff --git a/searches/exponential_search.py b/searches/exponential_search.py new file mode 100644 index 000000000000..ed09b14e101c --- /dev/null +++ b/searches/exponential_search.py @@ -0,0 +1,113 @@ +#!/usr/bin/env python3 + +""" +Pure Python implementation of exponential search algorithm + +For more information, see the Wikipedia page: +https://en.wikipedia.org/wiki/Exponential_search + +For doctests run the following command: +python3 -m doctest -v exponential_search.py + +For manual testing run: +python3 exponential_search.py +""" + +from __future__ import annotations + + +def binary_search_by_recursion( + sorted_collection: list[int], item: int, left: int = 0, right: int = -1 +) -> int: + """Pure implementation of binary search algorithm in Python using recursion + + Be careful: the collection must be ascending sorted otherwise, the result will be + unpredictable. + + :param sorted_collection: some ascending sorted collection with comparable items + :param item: item value to search + :param left: starting index for the search + :param right: ending index for the search + :return: index of the found item or -1 if the item is not found + + Examples: + >>> binary_search_by_recursion([0, 5, 7, 10, 15], 0, 0, 4) + 0 + >>> binary_search_by_recursion([0, 5, 7, 10, 15], 15, 0, 4) + 4 + >>> binary_search_by_recursion([0, 5, 7, 10, 15], 5, 0, 4) + 1 + >>> binary_search_by_recursion([0, 5, 7, 10, 15], 6, 0, 4) + -1 + """ + if right < 0: + right = len(sorted_collection) - 1 + if list(sorted_collection) != sorted(sorted_collection): + raise ValueError("sorted_collection must be sorted in ascending order") + if right < left: + return -1 + + midpoint = left + (right - left) // 2 + + if sorted_collection[midpoint] == item: + return midpoint + elif sorted_collection[midpoint] > item: + return binary_search_by_recursion(sorted_collection, item, left, midpoint - 1) + else: + return binary_search_by_recursion(sorted_collection, item, midpoint + 1, right) + + +def exponential_search(sorted_collection: list[int], item: int) -> int: + """ + Pure implementation of an exponential search algorithm in Python. + For more information, refer to: + https://en.wikipedia.org/wiki/Exponential_search + + Be careful: the collection must be ascending sorted, otherwise the result will be + unpredictable. + + :param sorted_collection: some ascending sorted collection with comparable items + :param item: item value to search + :return: index of the found item or -1 if the item is not found + + The time complexity of this algorithm is O(log i) where i is the index of the item. + + Examples: + >>> exponential_search([0, 5, 7, 10, 15], 0) + 0 + >>> exponential_search([0, 5, 7, 10, 15], 15) + 4 + >>> exponential_search([0, 5, 7, 10, 15], 5) + 1 + >>> exponential_search([0, 5, 7, 10, 15], 6) + -1 + """ + if list(sorted_collection) != sorted(sorted_collection): + raise ValueError("sorted_collection must be sorted in ascending order") + + if sorted_collection[0] == item: + return 0 + + bound = 1 + while bound < len(sorted_collection) and sorted_collection[bound] < item: + bound *= 2 + + left = bound // 2 + right = min(bound, len(sorted_collection) - 1) + return binary_search_by_recursion(sorted_collection, item, left, right) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + + # Manual testing + user_input = input("Enter numbers separated by commas: ").strip() + collection = sorted(int(item) for item in user_input.split(",")) + target = int(input("Enter a number to search for: ")) + result = exponential_search(sorted_collection=collection, item=target) + if result == -1: + print(f"{target} was not found in {collection}.") + else: + print(f"{target} was found at index {result} in {collection}.") From 3422ebc75bda6aba9b234eb217a79f25bec65f21 Mon Sep 17 00:00:00 2001 From: Jeel Rupapara Date: Mon, 7 Oct 2024 12:00:11 +0530 Subject: [PATCH 675/757] feat: add testcase of polynom_for_points (#11811) * feat: add testcase of polynom_for_points * fix: remove the print from the testcase of points_to_polynomial * fix: remove print statement from old test cases --- linear_algebra/src/polynom_for_points.py | 42 ++++++++++++++---------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/linear_algebra/src/polynom_for_points.py b/linear_algebra/src/polynom_for_points.py index a9a9a8117c18..452f3edd4aee 100644 --- a/linear_algebra/src/polynom_for_points.py +++ b/linear_algebra/src/polynom_for_points.py @@ -3,30 +3,36 @@ 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 - >>> print(points_to_polynomial([])) + >>> points_to_polynomial([]) Traceback (most recent call last): ... ValueError: The program cannot work out a fitting polynomial. - >>> print(points_to_polynomial([[]])) + >>> points_to_polynomial([[]]) + Traceback (most recent call last): + ... + ValueError: The program cannot work out a fitting polynomial. + >>> points_to_polynomial([[1, 0], [2, 0], [3, 0]]) + 'f(x)=x^2*0.0+x^1*-0.0+x^0*0.0' + >>> points_to_polynomial([[1, 1], [2, 1], [3, 1]]) + 'f(x)=x^2*0.0+x^1*-0.0+x^0*1.0' + >>> points_to_polynomial([[1, 3], [2, 3], [3, 3]]) + 'f(x)=x^2*0.0+x^1*-0.0+x^0*3.0' + >>> points_to_polynomial([[1, 1], [2, 2], [3, 3]]) + 'f(x)=x^2*0.0+x^1*1.0+x^0*0.0' + >>> points_to_polynomial([[1, 1], [2, 4], [3, 9]]) + 'f(x)=x^2*1.0+x^1*-0.0+x^0*0.0' + >>> points_to_polynomial([[1, 3], [2, 6], [3, 11]]) + 'f(x)=x^2*1.0+x^1*-0.0+x^0*2.0' + >>> points_to_polynomial([[1, -3], [2, -6], [3, -11]]) + 'f(x)=x^2*-1.0+x^1*-0.0+x^0*-2.0' + >>> points_to_polynomial([[1, 5], [2, 2], [3, 9]]) + 'f(x)=x^2*5.0+x^1*-18.0+x^0*18.0' + >>> points_to_polynomial([[1, 1], [1, 2], [1, 3]]) + 'x=1' + >>> points_to_polynomial([[1, 1], [2, 2], [2, 2]]) Traceback (most recent call last): ... ValueError: The program cannot work out a fitting polynomial. - >>> print(points_to_polynomial([[1, 0], [2, 0], [3, 0]])) - f(x)=x^2*0.0+x^1*-0.0+x^0*0.0 - >>> print(points_to_polynomial([[1, 1], [2, 1], [3, 1]])) - f(x)=x^2*0.0+x^1*-0.0+x^0*1.0 - >>> print(points_to_polynomial([[1, 3], [2, 3], [3, 3]])) - f(x)=x^2*0.0+x^1*-0.0+x^0*3.0 - >>> print(points_to_polynomial([[1, 1], [2, 2], [3, 3]])) - f(x)=x^2*0.0+x^1*1.0+x^0*0.0 - >>> print(points_to_polynomial([[1, 1], [2, 4], [3, 9]])) - f(x)=x^2*1.0+x^1*-0.0+x^0*0.0 - >>> print(points_to_polynomial([[1, 3], [2, 6], [3, 11]])) - f(x)=x^2*1.0+x^1*-0.0+x^0*2.0 - >>> print(points_to_polynomial([[1, -3], [2, -6], [3, -11]])) - f(x)=x^2*-1.0+x^1*-0.0+x^0*-2.0 - >>> print(points_to_polynomial([[1, 5], [2, 2], [3, 9]])) - f(x)=x^2*5.0+x^1*-18.0+x^0*18.0 """ if len(coordinates) == 0 or not all(len(pair) == 2 for pair in coordinates): raise ValueError("The program cannot work out a fitting polynomial.") From cfd6d095f122d1d3ef2f3c2cdcf84864aac56fa7 Mon Sep 17 00:00:00 2001 From: 1227haran <68032825+1227haran@users.noreply.github.com> Date: Mon, 7 Oct 2024 14:06:15 +0530 Subject: [PATCH 676/757] Added max_sum_bst.py (#11832) * Added new algorithm * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Updated changes * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Updated filename * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Updated code * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Updated the code * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Updated code * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Updated the code * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Updated code * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Updated the code * Updated code * Updated code * Updated code * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Updated the code * Updated code * Updated code * Updated code * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Updated code * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Updated code * updated * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Updated code * Updated code * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Updated code * Updated code * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Updated code * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Updated code * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Apply suggestions from code review * Update maximum_sum_bst.py * def max_sum_bst(root: TreeNode | None) -> int: * def solver(node: TreeNode | None) -> tuple[bool, int, int, int]: --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- .../binary_tree/maximum_sum_bst.py | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 data_structures/binary_tree/maximum_sum_bst.py diff --git a/data_structures/binary_tree/maximum_sum_bst.py b/data_structures/binary_tree/maximum_sum_bst.py new file mode 100644 index 000000000000..7dadc7b95920 --- /dev/null +++ b/data_structures/binary_tree/maximum_sum_bst.py @@ -0,0 +1,78 @@ +from __future__ import annotations + +import sys +from dataclasses import dataclass + +INT_MIN = -sys.maxsize + 1 +INT_MAX = sys.maxsize - 1 + + +@dataclass +class TreeNode: + val: int = 0 + left: TreeNode | None = None + right: TreeNode | None = None + + +def max_sum_bst(root: TreeNode | None) -> int: + """ + The solution traverses a binary tree to find the maximum sum of + keys in any subtree that is a Binary Search Tree (BST). It uses + recursion to validate BST properties and calculates sums, returning + the highest sum found among all valid BST subtrees. + + >>> t1 = TreeNode(4) + >>> t1.left = TreeNode(3) + >>> t1.left.left = TreeNode(1) + >>> t1.left.right = TreeNode(2) + >>> print(max_sum_bst(t1)) + 2 + >>> t2 = TreeNode(-4) + >>> t2.left = TreeNode(-2) + >>> t2.right = TreeNode(-5) + >>> print(max_sum_bst(t2)) + 0 + >>> t3 = TreeNode(1) + >>> t3.left = TreeNode(4) + >>> t3.left.left = TreeNode(2) + >>> t3.left.right = TreeNode(4) + >>> t3.right = TreeNode(3) + >>> t3.right.left = TreeNode(2) + >>> t3.right.right = TreeNode(5) + >>> t3.right.right.left = TreeNode(4) + >>> t3.right.right.right = TreeNode(6) + >>> print(max_sum_bst(t3)) + 20 + """ + ans: int = 0 + + def solver(node: TreeNode | None) -> tuple[bool, int, int, int]: + """ + Returns the maximum sum by making recursive calls + >>> t1 = TreeNode(1) + >>> print(solver(t1)) + 1 + """ + nonlocal ans + + if not node: + return True, INT_MAX, INT_MIN, 0 # Valid BST, min, max, sum + + is_left_valid, min_left, max_left, sum_left = solver(node.left) + is_right_valid, min_right, max_right, sum_right = solver(node.right) + + if is_left_valid and is_right_valid and max_left < node.val < min_right: + total_sum = sum_left + sum_right + node.val + ans = max(ans, total_sum) + return True, min(min_left, node.val), max(max_right, node.val), total_sum + + return False, -1, -1, -1 # Not a valid BST + + solver(root) + return ans + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From dba8eecb47cea7f11ac383344524afbc0ca7cf5b Mon Sep 17 00:00:00 2001 From: Lonercode <91500485+Lonercode@users.noreply.github.com> Date: Mon, 7 Oct 2024 10:58:07 +0100 Subject: [PATCH 677/757] added gronsfeld cipher implementation (#11835) * added gronsfeld cipher implementation * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * from string import ascii_uppercase * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update gronsfeld_cipher.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- ciphers/gronsfeld_cipher.py | 45 +++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 ciphers/gronsfeld_cipher.py diff --git a/ciphers/gronsfeld_cipher.py b/ciphers/gronsfeld_cipher.py new file mode 100644 index 000000000000..8fbeab4307fc --- /dev/null +++ b/ciphers/gronsfeld_cipher.py @@ -0,0 +1,45 @@ +from string import ascii_uppercase + + +def gronsfeld(text: str, key: str) -> str: + """ + Encrypt plaintext with the Gronsfeld cipher + + >>> gronsfeld('hello', '412') + 'LFNPP' + >>> gronsfeld('hello', '123') + 'IGOMQ' + >>> gronsfeld('', '123') + '' + >>> gronsfeld('yes, ¥€$ - _!@#%?', '0') + 'YES, ¥€$ - _!@#%?' + >>> gronsfeld('yes, ¥€$ - _!@#%?', '01') + 'YFS, ¥€$ - _!@#%?' + >>> gronsfeld('yes, ¥€$ - _!@#%?', '012') + 'YFU, ¥€$ - _!@#%?' + >>> gronsfeld('yes, ¥€$ - _!@#%?', '') + Traceback (most recent call last): + ... + ZeroDivisionError: integer modulo by zero + """ + ascii_len = len(ascii_uppercase) + key_len = len(key) + encrypted_text = "" + keys = [int(char) for char in key] + upper_case_text = text.upper() + + for i, char in enumerate(upper_case_text): + if char in ascii_uppercase: + new_position = (ascii_uppercase.index(char) + keys[i % key_len]) % ascii_len + shifted_letter = ascii_uppercase[new_position] + encrypted_text += shifted_letter + else: + encrypted_text += char + + return encrypted_text + + +if __name__ == "__main__": + from doctest import testmod + + testmod() From 2d671df073770f0122658f462c17b838ddbe4d2a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 7 Oct 2024 22:49:29 +0200 Subject: [PATCH 678/757] [pre-commit.ci] pre-commit autoupdate (#11874) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/pre-commit/pre-commit-hooks: v4.6.0 → v5.0.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.6.0...v5.0.0) - [github.com/astral-sh/ruff-pre-commit: v0.6.8 → v0.6.9](https://github.com/astral-sh/ruff-pre-commit/compare/v0.6.8...v0.6.9) * updating DIRECTORY.md --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] --- .pre-commit-config.yaml | 4 ++-- DIRECTORY.md | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8a8e5c1f6ad9..77541027afb3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.6.0 + rev: v5.0.0 hooks: - id: check-executables-have-shebangs - id: check-toml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.6.8 + rev: v0.6.9 hooks: - id: ruff - id: ruff-format diff --git a/DIRECTORY.md b/DIRECTORY.md index cdbbac684fd2..0a3be2a06533 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -22,6 +22,7 @@ * [Rat In Maze](backtracking/rat_in_maze.py) * [Sudoku](backtracking/sudoku.py) * [Sum Of Subsets](backtracking/sum_of_subsets.py) + * [Word Break](backtracking/word_break.py) * [Word Ladder](backtracking/word_ladder.py) * [Word Search](backtracking/word_search.py) @@ -99,6 +100,7 @@ * [Elgamal Key Generator](ciphers/elgamal_key_generator.py) * [Enigma Machine2](ciphers/enigma_machine2.py) * [Fractionated Morse Cipher](ciphers/fractionated_morse_cipher.py) + * [Gronsfeld Cipher](ciphers/gronsfeld_cipher.py) * [Hill Cipher](ciphers/hill_cipher.py) * [Mixed Keyword Cypher](ciphers/mixed_keyword_cypher.py) * [Mono Alphabetic Ciphers](ciphers/mono_alphabetic_ciphers.py) @@ -211,6 +213,7 @@ * [Lazy Segment Tree](data_structures/binary_tree/lazy_segment_tree.py) * [Lowest Common Ancestor](data_structures/binary_tree/lowest_common_ancestor.py) * [Maximum Fenwick Tree](data_structures/binary_tree/maximum_fenwick_tree.py) + * [Maximum Sum Bst](data_structures/binary_tree/maximum_sum_bst.py) * [Merge Two Binary Trees](data_structures/binary_tree/merge_two_binary_trees.py) * [Mirror Binary Tree](data_structures/binary_tree/mirror_binary_tree.py) * [Non Recursive Segment Tree](data_structures/binary_tree/non_recursive_segment_tree.py) @@ -284,6 +287,7 @@ * [Dijkstras Two Stack Algorithm](data_structures/stacks/dijkstras_two_stack_algorithm.py) * [Infix To Postfix Conversion](data_structures/stacks/infix_to_postfix_conversion.py) * [Infix To Prefix Conversion](data_structures/stacks/infix_to_prefix_conversion.py) + * [Lexicographical Numbers](data_structures/stacks/lexicographical_numbers.py) * [Next Greater Element](data_structures/stacks/next_greater_element.py) * [Postfix Evaluation](data_structures/stacks/postfix_evaluation.py) * [Prefix Evaluation](data_structures/stacks/prefix_evaluation.py) @@ -1201,6 +1205,7 @@ * [Binary Tree Traversal](searches/binary_tree_traversal.py) * [Double Linear Search](searches/double_linear_search.py) * [Double Linear Search Recursion](searches/double_linear_search_recursion.py) + * [Exponential Search](searches/exponential_search.py) * [Fibonacci Search](searches/fibonacci_search.py) * [Hill Climbing](searches/hill_climbing.py) * [Interpolation Search](searches/interpolation_search.py) From 260e3d8b350c64e927ecb1d62b953b8bf25490ea Mon Sep 17 00:00:00 2001 From: Jeel Rupapara Date: Tue, 8 Oct 2024 17:03:28 +0530 Subject: [PATCH 679/757] feat: add test cases in cipher's autokey (#11881) --- ciphers/autokey.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/ciphers/autokey.py b/ciphers/autokey.py index 8683e6d37001..05d8c066b139 100644 --- a/ciphers/autokey.py +++ b/ciphers/autokey.py @@ -24,6 +24,14 @@ def encrypt(plaintext: str, key: str) -> str: Traceback (most recent call last): ... ValueError: plaintext is empty + >>> encrypt("coffee is good as python", "") + Traceback (most recent call last): + ... + ValueError: key is empty + >>> encrypt(527.26, "TheAlgorithms") + Traceback (most recent call last): + ... + TypeError: plaintext must be a string """ if not isinstance(plaintext, str): raise TypeError("plaintext must be a string") @@ -80,6 +88,14 @@ def decrypt(ciphertext: str, key: str) -> str: Traceback (most recent call last): ... TypeError: ciphertext must be a string + >>> decrypt("", "TheAlgorithms") + Traceback (most recent call last): + ... + ValueError: ciphertext is empty + >>> decrypt("vvjfpk wj ohvp su ddylsv", 2) + Traceback (most recent call last): + ... + TypeError: key must be a string """ if not isinstance(ciphertext, str): raise TypeError("ciphertext must be a string") From e9e7c964655015819e0120694465928df1abefb0 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Tue, 8 Oct 2024 19:09:28 +0200 Subject: [PATCH 680/757] Create GitHub Pages docs with Sphinx (#11888) --- .devcontainer/Dockerfile | 2 +- .devcontainer/devcontainer.json | 2 +- .github/CODEOWNERS | 2 - .github/workflows/build.yml | 3 +- .github/workflows/sphinx.yml | 50 +++++++++ CONTRIBUTING.md | 2 +- DIRECTORY.md | 3 + LICENSE.md | 2 +- docs/{source => }/__init__.py | 0 docs/conf.py | 3 + financial/{ABOUT.md => README.md} | 2 +- index.md | 10 ++ .../{local_weighted_learning.md => README.md} | 0 pyproject.toml | 106 +++++++++++++++++- requirements.txt | 1 + source/__init__.py | 0 16 files changed, 179 insertions(+), 9 deletions(-) create mode 100644 .github/workflows/sphinx.yml rename docs/{source => }/__init__.py (100%) create mode 100644 docs/conf.py rename financial/{ABOUT.md => README.md} (97%) create mode 100644 index.md rename machine_learning/local_weighted_learning/{local_weighted_learning.md => README.md} (100%) delete mode 100644 source/__init__.py diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 6aa0073bf95b..a0bd05f47ec8 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,5 +1,5 @@ # https://github.com/microsoft/vscode-dev-containers/blob/main/containers/python-3/README.md -ARG VARIANT=3.12-bookworm +ARG VARIANT=3.13-bookworm FROM mcr.microsoft.com/vscode/devcontainers/python:${VARIANT} COPY requirements.txt /tmp/pip-tmp/ RUN python3 -m pip install --upgrade pip \ diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index ae1d4fb7494d..e23263f5b9de 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -7,7 +7,7 @@ // Update 'VARIANT' to pick a Python version: 3, 3.11, 3.10, 3.9, 3.8 // Append -bullseye or -buster to pin to an OS version. // Use -bullseye variants on local on arm64/Apple Silicon. - "VARIANT": "3.12-bookworm", + "VARIANT": "3.13-bookworm", } }, diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index d2ac43c7df31..3cc25d1bae1c 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -9,8 +9,6 @@ /.* @cclauss -# /arithmetic_analysis/ - # /backtracking/ # /bit_manipulation/ diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f54cc982d1ec..b5703e2f1ab6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -25,12 +25,13 @@ jobs: - name: Run tests # TODO: #8818 Re-enable quantum tests run: pytest - --ignore=quantum/q_fourier_transform.py --ignore=computer_vision/cnn_classification.py + --ignore=docs/conf.py --ignore=dynamic_programming/k_means_clustering_tensorflow.py --ignore=machine_learning/lstm/lstm_prediction.py --ignore=neural_network/input_data.py --ignore=project_euler/ + --ignore=quantum/q_fourier_transform.py --ignore=scripts/validate_solutions.py --cov-report=term-missing:skip-covered --cov=. . diff --git a/.github/workflows/sphinx.yml b/.github/workflows/sphinx.yml new file mode 100644 index 000000000000..9dfe344f9743 --- /dev/null +++ b/.github/workflows/sphinx.yml @@ -0,0 +1,50 @@ +name: sphinx + +on: + # Triggers the workflow on push or pull request events but only for the "master" branch + push: + branches: ["master"] + pull_request: + branches: ["master"] + # Or manually from the Actions tab + workflow_dispatch: + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + build_docs: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: 3.13 + allow-prereleases: true + - run: pip install --upgrade pip + - run: pip install myst-parser sphinx-autoapi sphinx-pyproject + - uses: actions/configure-pages@v5 + - run: sphinx-build -c docs . docs/_build/html + - uses: actions/upload-pages-artifact@v3 + with: + path: docs/_build/html + + deploy_docs: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + if: github.event_name != 'pull_request' + needs: build_docs + runs-on: ubuntu-latest + steps: + - uses: actions/deploy-pages@v4 + id: deployment diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b5113212929a..3df39f95b784 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -77,7 +77,7 @@ pre-commit run --all-files --show-diff-on-failure We want your work to be readable by others; therefore, we encourage you to note the following: -- Please write in Python 3.12+. For instance: `print()` is a function in Python 3 so `print "Hello"` will *not* work but `print("Hello")` will. +- Please write in Python 3.13+. For instance: `print()` is a function in Python 3 so `print "Hello"` will *not* work but `print("Hello")` will. - Please focus hard on the naming of functions, classes, and variables. Help your reader by using __descriptive names__ that can help you to remove redundant comments. - Single letter variable names are *old school* so please avoid them unless their life only spans a few lines. - Expand acronyms because `gcd()` is hard to understand but `greatest_common_divisor()` is not. diff --git a/DIRECTORY.md b/DIRECTORY.md index 0a3be2a06533..f0a34a553946 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -351,6 +351,9 @@ * [Power](divide_and_conquer/power.py) * [Strassen Matrix Multiplication](divide_and_conquer/strassen_matrix_multiplication.py) +## Docs + * [Conf](docs/conf.py) + ## Dynamic Programming * [Abbreviation](dynamic_programming/abbreviation.py) * [All Construct](dynamic_programming/all_construct.py) diff --git a/LICENSE.md b/LICENSE.md index 2897d02e2a01..de631c3ef333 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,4 +1,4 @@ -MIT License +## MIT License Copyright (c) 2016-2022 TheAlgorithms and contributors diff --git a/docs/source/__init__.py b/docs/__init__.py similarity index 100% rename from docs/source/__init__.py rename to docs/__init__.py diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 000000000000..f2481f107267 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,3 @@ +from sphinx_pyproject import SphinxConfig + +project = SphinxConfig("../pyproject.toml", globalns=globals()).name diff --git a/financial/ABOUT.md b/financial/README.md similarity index 97% rename from financial/ABOUT.md rename to financial/README.md index f6b0647f8201..e5d3a84c8381 100644 --- a/financial/ABOUT.md +++ b/financial/README.md @@ -1,4 +1,4 @@ -### Interest +# Interest * Compound Interest: "Compound interest is calculated by multiplying the initial principal amount by one plus the annual interest rate raised to the number of compound periods minus one." [Compound Interest](https://www.investopedia.com/) * Simple Interest: "Simple interest paid or received over a certain period is a fixed percentage of the principal amount that was borrowed or lent. " [Simple Interest](https://www.investopedia.com/) diff --git a/index.md b/index.md new file mode 100644 index 000000000000..134520cb94aa --- /dev/null +++ b/index.md @@ -0,0 +1,10 @@ +# TheAlgorithms/Python +```{toctree} +:maxdepth: 2 +:caption: index.md + + +CONTRIBUTING.md +README.md +LICENSE.md +``` diff --git a/machine_learning/local_weighted_learning/local_weighted_learning.md b/machine_learning/local_weighted_learning/README.md similarity index 100% rename from machine_learning/local_weighted_learning/local_weighted_learning.md rename to machine_learning/local_weighted_learning/README.md diff --git a/pyproject.toml b/pyproject.toml index bb8657183164..c57419e79db3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,22 @@ +[project] +name = "thealgorithms-python" +version = "0.0.1" +description = "TheAlgorithms in Python" +authors = [ { name = "TheAlgorithms Contributors" } ] +requires-python = ">=3.13" +classifiers = [ + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.13", + +] +optional-dependencies.docs = [ + "myst-parser", + "sphinx-autoapi", + "sphinx-pyproject", +] + [tool.ruff] -target-version = "py312" +target-version = "py313" output-format = "full" lint.select = [ @@ -113,6 +130,9 @@ lint.pylint.max-statements = 88 # default: 50 ignore-words-list = "3rt,ans,bitap,crate,damon,fo,followings,hist,iff,kwanza,manuel,mater,secant,som,sur,tim,toi,zar" skip = "./.*,*.json,ciphers/prehistoric_men.txt,project_euler/problem_022/p022_names.txt,pyproject.toml,strings/dictionary.txt,strings/words.txt" +[tool.pyproject-fmt] +max_supported_python = "3.13" + [tool.pytest.ini_options] markers = [ "mat_ops: mark a test as utilizing matrix operations.", @@ -129,3 +149,87 @@ omit = [ "project_euler/*", ] sort = "Cover" + +[tool.sphinx-pyproject] +copyright = "2014, TheAlgorithms" +autoapi_dirs = [ + "audio_filters", + "backtracking", + "bit_manipulation", + "blockchain", + "boolean_algebra", + "cellular_automata", + "ciphers", + "compression", + "computer_vision", + "conversions", + "data_structures", + "digital_image_processing", + "divide_and_conquer", + "dynamic_programming", + "electronics", + "file_transfer", + "financial", + "fractals", + "fuzzy_logic", + "genetic_algorithm", + "geodesy", + "geometry", + "graphics", + "graphs", + "greedy_methods", + "hashes", + "knapsack", + "linear_algebra", + "linear_programming", + "machine_learning", + "maths", + "matrix", + "networking_flow", + "neural_network", + "other", + "physics", + "project_euler", + "quantum", + "scheduling", + "searches", + "sorts", + "strings", + "web_programming", +] +autoapi_member_order = "groupwise" +# autoapi_python_use_implicit_namespaces = true +exclude_patterns = [ + ".*/*", + "docs/", +] +extensions = [ + "autoapi.extension", + "myst_parser", +] +html_static_path = [ "_static" ] +html_theme = "alabaster" +myst_enable_extensions = [ + "amsmath", + "attrs_inline", + "colon_fence", + "deflist", + "dollarmath", + "fieldlist", + "html_admonition", + "html_image", + # "linkify", + "replacements", + "smartquotes", + "strikethrough", + "substitution", + "tasklist", +] +myst_fence_as_directive = [ + "include", +] +templates_path = [ "_templates" ] +[tool.sphinx-pyproject.source_suffix] +".rst" = "restructuredtext" +# ".txt" = "markdown" +".md" = "markdown" diff --git a/requirements.txt b/requirements.txt index afbf25ba6edc..6754363332c4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,6 +15,7 @@ requests rich # scikit-fuzzy # uncomment once fuzzy_logic/fuzzy_operations.py is fixed scikit-learn +sphinx_pyproject statsmodels sympy tensorflow ; python_version < '3.13' diff --git a/source/__init__.py b/source/__init__.py deleted file mode 100644 index e69de29bb2d1..000000000000 From 03a42510b01c574292ca9c6525cbf0572ff5a2a5 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 14 Oct 2024 22:42:24 +0200 Subject: [PATCH 681/757] [pre-commit.ci] pre-commit autoupdate (#12071) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/tox-dev/pyproject-fmt: 2.2.4 → 2.3.0](https://github.com/tox-dev/pyproject-fmt/compare/2.2.4...2.3.0) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 77541027afb3..e1d185fabc12 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -29,7 +29,7 @@ repos: - tomli - repo: https://github.com/tox-dev/pyproject-fmt - rev: "2.2.4" + rev: "2.3.0" hooks: - id: pyproject-fmt From 6e24935f8860965dd7f2f5a50fd05724e84e9e8d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 22 Oct 2024 11:22:34 +0200 Subject: [PATCH 682/757] [pre-commit.ci] pre-commit autoupdate (#12234) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/astral-sh/ruff-pre-commit: v0.6.9 → v0.7.0](https://github.com/astral-sh/ruff-pre-commit/compare/v0.6.9...v0.7.0) - [github.com/tox-dev/pyproject-fmt: 2.3.0 → 2.4.3](https://github.com/tox-dev/pyproject-fmt/compare/2.3.0...2.4.3) - [github.com/abravalheri/validate-pyproject: v0.20.2 → v0.21](https://github.com/abravalheri/validate-pyproject/compare/v0.20.2...v0.21) - [github.com/pre-commit/mirrors-mypy: v1.11.2 → v1.12.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.11.2...v1.12.1) * project_euler/problem_047/sol1.py: def solution(n: int = 4) -> int | None: * Update sol1.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- .pre-commit-config.yaml | 8 ++++---- project_euler/problem_047/sol1.py | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e1d185fabc12..a849de0c4e16 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.6.9 + rev: v0.7.0 hooks: - id: ruff - id: ruff-format @@ -29,7 +29,7 @@ repos: - tomli - repo: https://github.com/tox-dev/pyproject-fmt - rev: "2.3.0" + rev: "2.4.3" hooks: - id: pyproject-fmt @@ -42,12 +42,12 @@ repos: pass_filenames: false - repo: https://github.com/abravalheri/validate-pyproject - rev: v0.20.2 + rev: v0.21 hooks: - id: validate-pyproject - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.11.2 + rev: v1.12.1 hooks: - id: mypy args: diff --git a/project_euler/problem_047/sol1.py b/project_euler/problem_047/sol1.py index 4ecd4f4b44c1..d174de27dcd0 100644 --- a/project_euler/problem_047/sol1.py +++ b/project_euler/problem_047/sol1.py @@ -24,7 +24,7 @@ def unique_prime_factors(n: int) -> set: """ Find unique prime factors of an integer. - Tests include sorting because only the set really matters, + Tests include sorting because only the set matters, not the order in which it is produced. >>> sorted(set(unique_prime_factors(14))) [2, 7] @@ -58,7 +58,7 @@ def upf_len(num: int) -> int: def equality(iterable: list) -> bool: """ - Check equality of ALL elements in an iterable + Check the equality of ALL elements in an iterable >>> equality([1, 2, 3, 4]) False >>> equality([2, 2, 2, 2]) @@ -69,7 +69,7 @@ def equality(iterable: list) -> bool: return len(set(iterable)) in (0, 1) -def run(n: int) -> list: +def run(n: int) -> list[int]: """ Runs core process to find problem solution. >>> run(3) @@ -77,7 +77,7 @@ def run(n: int) -> list: """ # Incrementor variable for our group list comprehension. - # This serves as the first number in each list of values + # This is the first number in each list of values # to test. base = 2 @@ -85,7 +85,7 @@ def run(n: int) -> list: # Increment each value of a generated range group = [base + i for i in range(n)] - # Run elements through out unique_prime_factors function + # Run elements through the unique_prime_factors function # Append our target number to the end. checker = [upf_len(x) for x in group] checker.append(n) @@ -98,7 +98,7 @@ def run(n: int) -> list: base += 1 -def solution(n: int = 4) -> int: +def solution(n: int = 4) -> int | None: """Return the first value of the first four consecutive integers to have four distinct prime factors each. >>> solution() From 52602ea5b6dd8179aa662c002891c6506f519435 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 28 Oct 2024 21:27:00 +0100 Subject: [PATCH 683/757] [pre-commit.ci] pre-commit autoupdate (#12313) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.7.0 → v0.7.1](https://github.com/astral-sh/ruff-pre-commit/compare/v0.7.0...v0.7.1) - [github.com/tox-dev/pyproject-fmt: 2.4.3 → v2.4.3](https://github.com/tox-dev/pyproject-fmt/compare/2.4.3...v2.4.3) - [github.com/abravalheri/validate-pyproject: v0.21 → v0.22](https://github.com/abravalheri/validate-pyproject/compare/v0.21...v0.22) - [github.com/pre-commit/mirrors-mypy: v1.12.1 → v1.13.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.12.1...v1.13.0) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a849de0c4e16..0828b715106d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.7.0 + rev: v0.7.1 hooks: - id: ruff - id: ruff-format @@ -29,7 +29,7 @@ repos: - tomli - repo: https://github.com/tox-dev/pyproject-fmt - rev: "2.4.3" + rev: "v2.4.3" hooks: - id: pyproject-fmt @@ -42,12 +42,12 @@ repos: pass_filenames: false - repo: https://github.com/abravalheri/validate-pyproject - rev: v0.21 + rev: v0.22 hooks: - id: validate-pyproject - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.12.1 + rev: v1.13.0 hooks: - id: mypy args: From a19bede190ddb4fa3c1c9850b612a47fc69d6709 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Fri, 1 Nov 2024 13:40:09 +0100 Subject: [PATCH 684/757] Add scripts/find_git_conflicts.sh (#12343) --- scripts/find_git_conflicts.sh | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100755 scripts/find_git_conflicts.sh diff --git a/scripts/find_git_conflicts.sh b/scripts/find_git_conflicts.sh new file mode 100755 index 000000000000..8af33fa75279 --- /dev/null +++ b/scripts/find_git_conflicts.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +# Replace with your repository (format: owner/repo) +REPO="TheAlgorithms/Python" + +# Fetch open pull requests with conflicts into a variable +echo "Checking for pull requests with conflicts in $REPO..." + +prs=$(gh pr list --repo "$REPO" --state open --json number,title,mergeable --jq '.[] | select(.mergeable == "CONFLICTING") | {number, title}' --limit 500) + +# Process each conflicting PR +echo "$prs" | jq -c '.[]' | while read -r pr; do + PR_NUMBER=$(echo "$pr" | jq -r '.number') + PR_TITLE=$(echo "$pr" | jq -r '.title') + echo "PR #$PR_NUMBER - $PR_TITLE has conflicts." +done From 3e9ca92ca972bbe752d32b43c71a88789dce94c0 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 4 Nov 2024 21:09:03 +0100 Subject: [PATCH 685/757] [pre-commit.ci] pre-commit autoupdate (#12349) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.7.1 → v0.7.2](https://github.com/astral-sh/ruff-pre-commit/compare/v0.7.1...v0.7.2) - [github.com/tox-dev/pyproject-fmt: v2.4.3 → v2.5.0](https://github.com/tox-dev/pyproject-fmt/compare/v2.4.3...v2.5.0) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0828b715106d..f112ee553b51 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.7.1 + rev: v0.7.2 hooks: - id: ruff - id: ruff-format @@ -29,7 +29,7 @@ repos: - tomli - repo: https://github.com/tox-dev/pyproject-fmt - rev: "v2.4.3" + rev: "v2.5.0" hooks: - id: pyproject-fmt From e3f3d668be4ada7aee82eea0bc75c50436c1ab3a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 11 Nov 2024 21:05:50 +0100 Subject: [PATCH 686/757] [pre-commit.ci] pre-commit autoupdate (#12370) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/astral-sh/ruff-pre-commit: v0.7.2 → v0.7.3](https://github.com/astral-sh/ruff-pre-commit/compare/v0.7.2...v0.7.3) - [github.com/abravalheri/validate-pyproject: v0.22 → v0.23](https://github.com/abravalheri/validate-pyproject/compare/v0.22...v0.23) * Update sudoku_solver.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- .pre-commit-config.yaml | 4 ++-- data_structures/arrays/sudoku_solver.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f112ee553b51..9d794473cc01 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.7.2 + rev: v0.7.3 hooks: - id: ruff - id: ruff-format @@ -42,7 +42,7 @@ repos: pass_filenames: false - repo: https://github.com/abravalheri/validate-pyproject - rev: v0.22 + rev: v0.23 hooks: - id: validate-pyproject diff --git a/data_structures/arrays/sudoku_solver.py b/data_structures/arrays/sudoku_solver.py index a8157a520c97..70bcdc748195 100644 --- a/data_structures/arrays/sudoku_solver.py +++ b/data_structures/arrays/sudoku_solver.py @@ -172,7 +172,7 @@ def unitsolved(unit): def from_file(filename, sep="\n"): "Parse a file into a list of strings, separated by sep." - return open(filename).read().strip().split(sep) # noqa: SIM115 + return open(filename).read().strip().split(sep) def random_puzzle(assignments=17): From e3bd7721c8241a6db77254bac44757dced1b96f8 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Fri, 15 Nov 2024 14:59:14 +0100 Subject: [PATCH 687/757] `validate_filenames.py` Shebang `python` for Windows (#12371) --- scripts/validate_filenames.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/validate_filenames.py b/scripts/validate_filenames.py index 0890024dd349..e76b4dbfe288 100755 --- a/scripts/validate_filenames.py +++ b/scripts/validate_filenames.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python3 +#!python import os try: From f3f32ae3ca818f64de2ed3267803882956681044 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 18 Nov 2024 22:07:12 +0100 Subject: [PATCH 688/757] [pre-commit.ci] pre-commit autoupdate (#12385) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.7.3 → v0.7.4](https://github.com/astral-sh/ruff-pre-commit/compare/v0.7.3...v0.7.4) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9d794473cc01..6ad19f1fdcb1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.7.3 + rev: v0.7.4 hooks: - id: ruff - id: ruff-format From fc33c505935e9927cffb6142591891f721a7bcd9 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 25 Nov 2024 21:46:20 +0100 Subject: [PATCH 689/757] [pre-commit.ci] pre-commit autoupdate (#12398) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.4.10 → v0.5.0](https://github.com/astral-sh/ruff-pre-commit/compare/v0.4.10...v0.5.0) - [github.com/pre-commit/mirrors-mypy: v1.10.0 → v1.10.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.10.0...v1.10.1) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- cellular_automata/conways_game_of_life.py | 6 ++--- ciphers/playfair_cipher.py | 2 +- ciphers/simple_keyword_cypher.py | 2 +- ciphers/transposition_cipher.py | 6 ++--- compression/lempel_ziv.py | 4 ++-- data_structures/arrays/sudoku_solver.py | 2 +- .../binary_tree/binary_tree_traversals.py | 24 ++++++++----------- data_structures/linked_list/deque_doubly.py | 2 +- data_structures/queue/double_ended_queue.py | 2 +- docs/source/__init__.py | 0 electronics/electrical_impedance.py | 2 +- graphs/ant_colony_optimization_algorithms.py | 6 ++--- graphs/basic_graphs.py | 22 ++++++++--------- graphs/minimum_spanning_tree_boruvka.py | 8 +++---- hashes/md5.py | 2 +- machine_learning/frequent_pattern_growth.py | 4 ++-- maths/collatz_sequence.py | 2 +- maths/prime_numbers.py | 6 ++--- maths/volume.py | 2 +- neural_network/input_data.py | 10 ++++---- physics/basic_orbital_capture.py | 9 ++++--- physics/grahams_law.py | 2 +- project_euler/problem_025/sol2.py | 2 +- project_euler/problem_123/sol1.py | 2 +- pyproject.toml | 1 + source/__init__.py | 0 strings/frequency_finder.py | 2 +- strings/min_cost_string_conversion.py | 8 +++---- web_programming/fetch_jobs.py | 2 +- 30 files changed, 66 insertions(+), 78 deletions(-) create mode 100644 docs/source/__init__.py create mode 100644 source/__init__.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6ad19f1fdcb1..64d9a833cd21 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.7.4 + rev: v0.8.0 hooks: - id: ruff - id: ruff-format diff --git a/cellular_automata/conways_game_of_life.py b/cellular_automata/conways_game_of_life.py index 364a34c3aba6..485f0d47bd8b 100644 --- a/cellular_automata/conways_game_of_life.py +++ b/cellular_automata/conways_game_of_life.py @@ -58,10 +58,8 @@ def new_generation(cells: list[list[int]]) -> list[list[int]]: # 3. All other live cells die in the next generation. # Similarly, all other dead cells stay dead. alive = cells[i][j] == 1 - if ( - (alive and 2 <= neighbour_count <= 3) - or not alive - and neighbour_count == 3 + if (alive and 2 <= neighbour_count <= 3) or ( + not alive and neighbour_count == 3 ): next_generation_row.append(1) else: diff --git a/ciphers/playfair_cipher.py b/ciphers/playfair_cipher.py index 86b45bc4fb6a..d48f113f02e0 100644 --- a/ciphers/playfair_cipher.py +++ b/ciphers/playfair_cipher.py @@ -24,7 +24,7 @@ from collections.abc import Generator, Iterable -def chunker(seq: Iterable[str], size: int) -> Generator[tuple[str, ...], None, None]: +def chunker(seq: Iterable[str], size: int) -> Generator[tuple[str, ...]]: it = iter(seq) while True: chunk = tuple(itertools.islice(it, size)) diff --git a/ciphers/simple_keyword_cypher.py b/ciphers/simple_keyword_cypher.py index 1635471aebd1..9dc624e7762c 100644 --- a/ciphers/simple_keyword_cypher.py +++ b/ciphers/simple_keyword_cypher.py @@ -10,7 +10,7 @@ def remove_duplicates(key: str) -> str: key_no_dups = "" for ch in key: - if ch == " " or ch not in key_no_dups and ch.isalpha(): + if ch == " " or (ch not in key_no_dups and ch.isalpha()): key_no_dups += ch return key_no_dups diff --git a/ciphers/transposition_cipher.py b/ciphers/transposition_cipher.py index f1f07ddc3f35..76178cb6a1bc 100644 --- a/ciphers/transposition_cipher.py +++ b/ciphers/transposition_cipher.py @@ -52,10 +52,8 @@ def decrypt_message(key: int, message: str) -> str: plain_text[col] += symbol col += 1 - if ( - (col == num_cols) - or (col == num_cols - 1) - and (row >= num_rows - num_shaded_boxes) + if (col == num_cols) or ( + (col == num_cols - 1) and (row >= num_rows - num_shaded_boxes) ): col = 0 row += 1 diff --git a/compression/lempel_ziv.py b/compression/lempel_ziv.py index 2751a0ebcdb6..648b029471bd 100644 --- a/compression/lempel_ziv.py +++ b/compression/lempel_ziv.py @@ -35,8 +35,8 @@ def add_key_to_lexicon( lexicon[curr_string + "0"] = last_match_id if math.log2(index).is_integer(): - for curr_key in lexicon: - lexicon[curr_key] = "0" + lexicon[curr_key] + for curr_key, value in lexicon.items(): + lexicon[curr_key] = f"0{value}" lexicon[curr_string + "1"] = bin(index)[2:] diff --git a/data_structures/arrays/sudoku_solver.py b/data_structures/arrays/sudoku_solver.py index 70bcdc748195..7e38e1465728 100644 --- a/data_structures/arrays/sudoku_solver.py +++ b/data_structures/arrays/sudoku_solver.py @@ -156,7 +156,7 @@ def time_solve(grid): times, results = zip(*[time_solve(grid) for grid in grids]) if (n := len(grids)) > 1: print( - "Solved %d of %d %s puzzles (avg %.2f secs (%d Hz), max %.2f secs)." + "Solved %d of %d %s puzzles (avg %.2f secs (%d Hz), max %.2f secs)." # noqa: UP031 % (sum(results), n, name, sum(times) / n, n / sum(times), max(times)) ) diff --git a/data_structures/binary_tree/binary_tree_traversals.py b/data_structures/binary_tree/binary_tree_traversals.py index 49c208335b2c..5ba149d0cbc6 100644 --- a/data_structures/binary_tree/binary_tree_traversals.py +++ b/data_structures/binary_tree/binary_tree_traversals.py @@ -30,7 +30,7 @@ def make_tree() -> Node | None: return tree -def preorder(root: Node | None) -> Generator[int, None, None]: +def preorder(root: Node | None) -> Generator[int]: """ Pre-order traversal visits root node, left subtree, right subtree. >>> list(preorder(make_tree())) @@ -43,7 +43,7 @@ def preorder(root: Node | None) -> Generator[int, None, None]: yield from preorder(root.right) -def postorder(root: Node | None) -> Generator[int, None, None]: +def postorder(root: Node | None) -> Generator[int]: """ Post-order traversal visits left subtree, right subtree, root node. >>> list(postorder(make_tree())) @@ -56,7 +56,7 @@ def postorder(root: Node | None) -> Generator[int, None, None]: yield root.data -def inorder(root: Node | None) -> Generator[int, None, None]: +def inorder(root: Node | None) -> Generator[int]: """ In-order traversal visits left subtree, root node, right subtree. >>> list(inorder(make_tree())) @@ -69,7 +69,7 @@ def inorder(root: Node | None) -> Generator[int, None, None]: yield from inorder(root.right) -def reverse_inorder(root: Node | None) -> Generator[int, None, None]: +def reverse_inorder(root: Node | None) -> Generator[int]: """ Reverse in-order traversal visits right subtree, root node, left subtree. >>> list(reverse_inorder(make_tree())) @@ -93,7 +93,7 @@ def height(root: Node | None) -> int: return (max(height(root.left), height(root.right)) + 1) if root else 0 -def level_order(root: Node | None) -> Generator[int, None, None]: +def level_order(root: Node | None) -> Generator[int]: """ Returns a list of nodes value from a whole binary tree in Level Order Traverse. Level Order traverse: Visit nodes of the tree level-by-level. @@ -116,9 +116,7 @@ def level_order(root: Node | None) -> Generator[int, None, None]: process_queue.append(node.right) -def get_nodes_from_left_to_right( - root: Node | None, level: int -) -> Generator[int, None, None]: +def get_nodes_from_left_to_right(root: Node | None, level: int) -> Generator[int]: """ Returns a list of nodes value from a particular level: Left to right direction of the binary tree. @@ -128,7 +126,7 @@ def get_nodes_from_left_to_right( [2, 3] """ - def populate_output(root: Node | None, level: int) -> Generator[int, None, None]: + def populate_output(root: Node | None, level: int) -> Generator[int]: if not root: return if level == 1: @@ -140,9 +138,7 @@ def populate_output(root: Node | None, level: int) -> Generator[int, None, None] yield from populate_output(root, level) -def get_nodes_from_right_to_left( - root: Node | None, level: int -) -> Generator[int, None, None]: +def get_nodes_from_right_to_left(root: Node | None, level: int) -> Generator[int]: """ Returns a list of nodes value from a particular level: Right to left direction of the binary tree. @@ -152,7 +148,7 @@ def get_nodes_from_right_to_left( [3, 2] """ - def populate_output(root: Node | None, level: int) -> Generator[int, None, None]: + def populate_output(root: Node | None, level: int) -> Generator[int]: if not root: return if level == 1: @@ -164,7 +160,7 @@ def populate_output(root: Node | None, level: int) -> Generator[int, None, None] yield from populate_output(root, level) -def zigzag(root: Node | None) -> Generator[int, None, None]: +def zigzag(root: Node | None) -> Generator[int]: """ ZigZag traverse: Returns a list of nodes value from left to right and right to left, alternatively. diff --git a/data_structures/linked_list/deque_doubly.py b/data_structures/linked_list/deque_doubly.py index 2b9d70c223c4..e554ead91c5a 100644 --- a/data_structures/linked_list/deque_doubly.py +++ b/data_structures/linked_list/deque_doubly.py @@ -12,7 +12,7 @@ class _DoublyLinkedBase: """A Private class (to be inherited)""" class _Node: - __slots__ = "_prev", "_data", "_next" + __slots__ = "_data", "_next", "_prev" def __init__(self, link_p, element, link_n): self._prev = link_p diff --git a/data_structures/queue/double_ended_queue.py b/data_structures/queue/double_ended_queue.py index 607d0bda3df4..c28d46c65168 100644 --- a/data_structures/queue/double_ended_queue.py +++ b/data_structures/queue/double_ended_queue.py @@ -33,7 +33,7 @@ class Deque: the number of nodes """ - __slots__ = ("_front", "_back", "_len") + __slots__ = ("_back", "_front", "_len") @dataclass class _Node: diff --git a/docs/source/__init__.py b/docs/source/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/electronics/electrical_impedance.py b/electronics/electrical_impedance.py index 44041ff790b6..4f4f1d308293 100644 --- a/electronics/electrical_impedance.py +++ b/electronics/electrical_impedance.py @@ -6,7 +6,7 @@ from __future__ import annotations -from math import pow, sqrt +from math import pow, sqrt # noqa: A004 def electrical_impedance( diff --git a/graphs/ant_colony_optimization_algorithms.py b/graphs/ant_colony_optimization_algorithms.py index 13637da44874..753f4c0962c8 100644 --- a/graphs/ant_colony_optimization_algorithms.py +++ b/graphs/ant_colony_optimization_algorithms.py @@ -194,10 +194,8 @@ def city_select( IndexError: list index out of range """ probabilities = [] - for city in unvisited_cities: - city_distance = distance( - unvisited_cities[city], next(iter(current_city.values())) - ) + for city, value in unvisited_cities.items(): + city_distance = distance(value, next(iter(current_city.values()))) probability = (pheromone[city][next(iter(current_city.keys()))] ** alpha) * ( (1 / city_distance) ** beta ) diff --git a/graphs/basic_graphs.py b/graphs/basic_graphs.py index 25c8045b3d2b..567fa65040ae 100644 --- a/graphs/basic_graphs.py +++ b/graphs/basic_graphs.py @@ -133,18 +133,18 @@ def dijk(g, s): if len(known) == len(g) - 1: break mini = 100000 - for i in dist: - if i not in known and dist[i] < mini: - mini = dist[i] - u = i + for key, value in dist: + if key not in known and value < mini: + mini = value + u = key known.add(u) for v in g[u]: if v[0] not in known and dist[u] + v[1] < dist.get(v[0], 100000): dist[v[0]] = dist[u] + v[1] path[v[0]] = u - for i in dist: - if i != s: - print(dist[i]) + for key, value in dist.items(): + if key != s: + print(value) """ @@ -255,10 +255,10 @@ def prim(g, s): if len(known) == len(g) - 1: break mini = 100000 - for i in dist: - if i not in known and dist[i] < mini: - mini = dist[i] - u = i + for key, value in dist.items(): + if key not in known and value < mini: + mini = value + u = key known.add(u) for v in g[u]: if v[0] not in known and v[1] < dist.get(v[0], 100000): diff --git a/graphs/minimum_spanning_tree_boruvka.py b/graphs/minimum_spanning_tree_boruvka.py index 3c6888037948..f234d65ab765 100644 --- a/graphs/minimum_spanning_tree_boruvka.py +++ b/graphs/minimum_spanning_tree_boruvka.py @@ -185,12 +185,12 @@ def boruvka_mst(graph): if cheap_edge[set2] == -1 or cheap_edge[set2][2] > weight: cheap_edge[set2] = [head, tail, weight] - for vertex in cheap_edge: - if cheap_edge[vertex] != -1: - head, tail, weight = cheap_edge[vertex] + for head_tail_weight in cheap_edge.values(): + if head_tail_weight != -1: + head, tail, weight = head_tail_weight if union_find.find(head) != union_find.find(tail): union_find.union(head, tail) - mst_edges.append(cheap_edge[vertex]) + mst_edges.append(head_tail_weight) num_components = num_components - 1 mst = Graph.build(edges=mst_edges) return mst diff --git a/hashes/md5.py b/hashes/md5.py index 622a50d290e1..f9d802ff0308 100644 --- a/hashes/md5.py +++ b/hashes/md5.py @@ -131,7 +131,7 @@ def preprocess(message: bytes) -> bytes: return bit_string -def get_block_words(bit_string: bytes) -> Generator[list[int], None, None]: +def get_block_words(bit_string: bytes) -> Generator[list[int]]: """ Splits bit string into blocks of 512 chars and yields each block as a list of 32-bit words diff --git a/machine_learning/frequent_pattern_growth.py b/machine_learning/frequent_pattern_growth.py index 947f8692f298..fae2df16efb1 100644 --- a/machine_learning/frequent_pattern_growth.py +++ b/machine_learning/frequent_pattern_growth.py @@ -107,8 +107,8 @@ def create_tree(data_set: list, min_sup: int = 1) -> tuple[TreeNode, dict]: if not (freq_item_set := set(header_table)): return TreeNode("Null Set", 1, None), {} - for k in header_table: - header_table[k] = [header_table[k], None] + for key, value in header_table.items(): + header_table[key] = [value, None] fp_tree = TreeNode("Null Set", 1, None) # Parent is None for the root node for tran_set in data_set: diff --git a/maths/collatz_sequence.py b/maths/collatz_sequence.py index b47017146a1e..b00dca8d70b7 100644 --- a/maths/collatz_sequence.py +++ b/maths/collatz_sequence.py @@ -17,7 +17,7 @@ from collections.abc import Generator -def collatz_sequence(n: int) -> Generator[int, None, None]: +def collatz_sequence(n: int) -> Generator[int]: """ Generate the Collatz sequence starting at n. >>> tuple(collatz_sequence(2.1)) diff --git a/maths/prime_numbers.py b/maths/prime_numbers.py index 38cc6670385d..5ad12baf3dc3 100644 --- a/maths/prime_numbers.py +++ b/maths/prime_numbers.py @@ -2,7 +2,7 @@ from collections.abc import Generator -def slow_primes(max_n: int) -> Generator[int, None, None]: +def slow_primes(max_n: int) -> Generator[int]: """ Return a list of all primes numbers up to max. >>> list(slow_primes(0)) @@ -29,7 +29,7 @@ def slow_primes(max_n: int) -> Generator[int, None, None]: yield i -def primes(max_n: int) -> Generator[int, None, None]: +def primes(max_n: int) -> Generator[int]: """ Return a list of all primes numbers up to max. >>> list(primes(0)) @@ -58,7 +58,7 @@ def primes(max_n: int) -> Generator[int, None, None]: yield i -def fast_primes(max_n: int) -> Generator[int, None, None]: +def fast_primes(max_n: int) -> Generator[int]: """ Return a list of all primes numbers up to max. >>> list(fast_primes(0)) diff --git a/maths/volume.py b/maths/volume.py index 33be9bdd131a..23fcf6be6ef1 100644 --- a/maths/volume.py +++ b/maths/volume.py @@ -6,7 +6,7 @@ from __future__ import annotations -from math import pi, pow +from math import pi, pow # noqa: A004 def vol_cube(side_length: float) -> float: diff --git a/neural_network/input_data.py b/neural_network/input_data.py index f90287fe3f5b..72debabb566a 100644 --- a/neural_network/input_data.py +++ b/neural_network/input_data.py @@ -61,9 +61,8 @@ def _extract_images(f): with gzip.GzipFile(fileobj=f) as bytestream: magic = _read32(bytestream) if magic != 2051: - raise ValueError( - "Invalid magic number %d in MNIST image file: %s" % (magic, f.name) - ) + msg = f"Invalid magic number {magic} in MNIST image file: {f.name}" + raise ValueError(msg) num_images = _read32(bytestream) rows = _read32(bytestream) cols = _read32(bytestream) @@ -102,9 +101,8 @@ def _extract_labels(f, one_hot=False, num_classes=10): with gzip.GzipFile(fileobj=f) as bytestream: magic = _read32(bytestream) if magic != 2049: - raise ValueError( - "Invalid magic number %d in MNIST label file: %s" % (magic, f.name) - ) + msg = f"Invalid magic number {magic} in MNIST label file: {f.name}" + raise ValueError(msg) num_items = _read32(bytestream) buf = bytestream.read(num_items) labels = np.frombuffer(buf, dtype=np.uint8) diff --git a/physics/basic_orbital_capture.py b/physics/basic_orbital_capture.py index a5434b5cb7cb..eb1fdd9d6420 100644 --- a/physics/basic_orbital_capture.py +++ b/physics/basic_orbital_capture.py @@ -1,7 +1,3 @@ -from math import pow, sqrt - -from scipy.constants import G, c, pi - """ These two functions will return the radii of impact for a target object of mass M and radius R as well as it's effective cross sectional area sigma. @@ -14,9 +10,12 @@ cross section for capture as sigma=π*R_capture**2. This algorithm does not account for an N-body problem. - """ +from math import pow, sqrt # noqa: A004 + +from scipy.constants import G, c, pi + def capture_radii( target_body_radius: float, target_body_mass: float, projectile_velocity: float diff --git a/physics/grahams_law.py b/physics/grahams_law.py index 6e5d75127e83..c56359280ea4 100644 --- a/physics/grahams_law.py +++ b/physics/grahams_law.py @@ -14,7 +14,7 @@ (Description adapted from https://en.wikipedia.org/wiki/Graham%27s_law) """ -from math import pow, sqrt +from math import pow, sqrt # noqa: A004 def validate(*values: float) -> bool: diff --git a/project_euler/problem_025/sol2.py b/project_euler/problem_025/sol2.py index a0f056023bc9..4094b6251d50 100644 --- a/project_euler/problem_025/sol2.py +++ b/project_euler/problem_025/sol2.py @@ -27,7 +27,7 @@ from collections.abc import Generator -def fibonacci_generator() -> Generator[int, None, None]: +def fibonacci_generator() -> Generator[int]: """ A generator that produces numbers in the Fibonacci sequence diff --git a/project_euler/problem_123/sol1.py b/project_euler/problem_123/sol1.py index 3dd31a2e8505..265348d2d4c8 100644 --- a/project_euler/problem_123/sol1.py +++ b/project_euler/problem_123/sol1.py @@ -43,7 +43,7 @@ from collections.abc import Generator -def sieve() -> Generator[int, None, None]: +def sieve() -> Generator[int]: """ Returns a prime number generator using sieve method. >>> type(sieve()) diff --git a/pyproject.toml b/pyproject.toml index c57419e79db3..c60ec246144e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -80,6 +80,7 @@ lint.ignore = [ "EM101", # Exception must not use a string literal, assign to variable first "EXE001", # Shebang is present but file is not executable -- DO NOT FIX "G004", # Logging statement uses f-string + "ISC001", # Conflicts with ruff format -- DO NOT FIX "PLC1901", # `{}` can be simplified to `{}` as an empty string is falsey "PLW060", # Using global for `{name}` but no assignment is done -- DO NOT FIX "PLW2901", # PLW2901: Redefined loop variable -- FIX ME diff --git a/source/__init__.py b/source/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/strings/frequency_finder.py b/strings/frequency_finder.py index 8479c81ae464..e5afee891bd9 100644 --- a/strings/frequency_finder.py +++ b/strings/frequency_finder.py @@ -67,7 +67,7 @@ def get_frequency_order(message: str) -> str: freq_to_letter_str: dict[int, str] = {} - for freq in freq_to_letter: + for freq in freq_to_letter: # noqa: PLC0206 freq_to_letter[freq].sort(key=ETAOIN.find, reverse=True) freq_to_letter_str[freq] = "".join(freq_to_letter[freq]) diff --git a/strings/min_cost_string_conversion.py b/strings/min_cost_string_conversion.py index a5a3c4a4e3f8..93791e2a7ed3 100644 --- a/strings/min_cost_string_conversion.py +++ b/strings/min_cost_string_conversion.py @@ -124,7 +124,7 @@ def assemble_transformation(ops: list[list[str]], i: int, j: int) -> list[str]: print("".join(string)) if op[0] == "C": - file.write("%-16s" % "Copy %c" % op[1]) + file.write("%-16s" % "Copy %c" % op[1]) # noqa: UP031 file.write("\t\t\t" + "".join(string)) file.write("\r\n") @@ -132,7 +132,7 @@ def assemble_transformation(ops: list[list[str]], i: int, j: int) -> list[str]: elif op[0] == "R": string[i] = op[2] - file.write("%-16s" % ("Replace %c" % op[1] + " with " + str(op[2]))) + file.write("%-16s" % ("Replace %c" % op[1] + " with " + str(op[2]))) # noqa: UP031 file.write("\t\t" + "".join(string)) file.write("\r\n") @@ -140,7 +140,7 @@ def assemble_transformation(ops: list[list[str]], i: int, j: int) -> list[str]: elif op[0] == "D": string.pop(i) - file.write("%-16s" % "Delete %c" % op[1]) + file.write("%-16s" % "Delete %c" % op[1]) # noqa: UP031 file.write("\t\t\t" + "".join(string)) file.write("\r\n") @@ -148,7 +148,7 @@ def assemble_transformation(ops: list[list[str]], i: int, j: int) -> list[str]: else: string.insert(i, op[1]) - file.write("%-16s" % "Insert %c" % op[1]) + file.write("%-16s" % "Insert %c" % op[1]) # noqa: UP031 file.write("\t\t\t" + "".join(string)) file.write("\r\n") diff --git a/web_programming/fetch_jobs.py b/web_programming/fetch_jobs.py index 0d89bf45de57..3753d25bbe5f 100644 --- a/web_programming/fetch_jobs.py +++ b/web_programming/fetch_jobs.py @@ -12,7 +12,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]]: soup = BeautifulSoup( requests.get(url + location, timeout=10).content, "html.parser" ) From c7921226326f35932bbc9d214e9742c2f3d310bf Mon Sep 17 00:00:00 2001 From: Anamaria Miranda Date: Mon, 2 Dec 2024 11:57:04 +0100 Subject: [PATCH 690/757] Added matrix based color game algorithm (#12400) * Added matrix based color game * updating DIRECTORY.md --------- Co-authored-by: Miranda13 --- DIRECTORY.md | 1 + matrix/matrix_based_game.py | 284 ++++++++++++++++++++++++++++++++++++ 2 files changed, 285 insertions(+) create mode 100644 matrix/matrix_based_game.py diff --git a/DIRECTORY.md b/DIRECTORY.md index f0a34a553946..d234d366df06 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -794,6 +794,7 @@ * [Cramers Rule 2X2](matrix/cramers_rule_2x2.py) * [Inverse Of Matrix](matrix/inverse_of_matrix.py) * [Largest Square Area In Matrix](matrix/largest_square_area_in_matrix.py) + * [Matrix Based Game](matrix/matrix_based_game.py) * [Matrix Class](matrix/matrix_class.py) * [Matrix Equalization](matrix/matrix_equalization.py) * [Matrix Multiplication Recursion](matrix/matrix_multiplication_recursion.py) diff --git a/matrix/matrix_based_game.py b/matrix/matrix_based_game.py new file mode 100644 index 000000000000..1ff0cbe93435 --- /dev/null +++ b/matrix/matrix_based_game.py @@ -0,0 +1,284 @@ +""" +Matrix-Based Game Script +========================= +This script implements a matrix-based game where players interact with a grid of +elements. The primary goals are to: +- Identify connected elements of the same type from a selected position. +- Remove those elements, adjust the matrix by simulating gravity, and reorganize empty + columns. +- Calculate and display the score based on the number of elements removed in each move. + +Functions: +----------- +1. `find_repeat`: Finds all connected elements of the same type. +2. `increment_score`: Calculates the score for a given move. +3. `move_x`: Simulates gravity in a column. +4. `move_y`: Reorganizes the matrix by shifting columns leftward when a column becomes + empty. +5. `play`: Executes a single move, updating the matrix and returning the score. + +Input Format: +-------------- +1. Matrix size (`lines`): Integer specifying the size of the matrix (N x N). +2. Matrix content (`matrix`): Rows of the matrix, each consisting of characters. +3. Number of moves (`movs`): Integer indicating the number of moves. +4. List of moves (`movements`): A comma-separated string of coordinates for each move. + +(0,0) position starts from first left column to last right, and below row to up row + + +Example Input: +--------------- +4 +RRBG +RBBG +YYGG +XYGG +2 +0 1,1 1 + +Example (0,0) = X + +Output: +-------- +The script outputs the total score after processing all moves. + +Usage: +------- +Run the script and provide the required inputs as prompted. + +""" + + +def validate_matrix_size(size: int) -> None: + """ + >>> validate_matrix_size(-1) + Traceback (most recent call last): + ... + ValueError: Matrix size must be a positive integer. + """ + if not isinstance(size, int) or size <= 0: + raise ValueError("Matrix size must be a positive integer.") + + +def validate_matrix_content(matrix: list[str], size: int) -> None: + """ + Validates that the number of elements in the matrix matches the given size. + + >>> validate_matrix_content(['aaaa', 'aaaa', 'aaaa', 'aaaa'], 3) + Traceback (most recent call last): + ... + ValueError: The matrix dont match with size. + >>> validate_matrix_content(['aa%', 'aaa', 'aaa'], 3) + Traceback (most recent call last): + ... + ValueError: Matrix rows can only contain letters and numbers. + >>> validate_matrix_content(['aaa', 'aaa', 'aaaa'], 3) + Traceback (most recent call last): + ... + ValueError: Each row in the matrix must have exactly 3 characters. + """ + print(matrix) + if len(matrix) != size: + raise ValueError("The matrix dont match with size.") + for row in matrix: + if len(row) != size: + msg = f"Each row in the matrix must have exactly {size} characters." + raise ValueError(msg) + if not all(char.isalnum() for char in row): + raise ValueError("Matrix rows can only contain letters and numbers.") + + +def validate_moves(moves: list[tuple[int, int]], size: int) -> None: + """ + >>> validate_moves([(1, 2), (-1, 0)], 3) + Traceback (most recent call last): + ... + ValueError: Move is out of bounds for a matrix. + """ + for move in moves: + x, y = move + if not (0 <= x < size and 0 <= y < size): + raise ValueError("Move is out of bounds for a matrix.") + + +def parse_moves(input_str: str) -> list[tuple[int, int]]: + """ + >>> parse_moves("0 1, 1 1") + [(0, 1), (1, 1)] + >>> parse_moves("0 1, 1 1, 2") + Traceback (most recent call last): + ... + ValueError: Each move must have exactly two numbers. + >>> parse_moves("0 1, 1 1, 2 4 5 6") + Traceback (most recent call last): + ... + ValueError: Each move must have exactly two numbers. + """ + moves = [] + for pair in input_str.split(","): + parts = pair.strip().split() + if len(parts) != 2: + raise ValueError("Each move must have exactly two numbers.") + x, y = map(int, parts) + moves.append((x, y)) + return moves + + +def find_repeat( + matrix_g: list[list[str]], row: int, column: int, size: int +) -> set[tuple[int, int]]: + """ + Finds all connected elements of the same type from a given position. + + >>> find_repeat([['A', 'B', 'A'], ['A', 'B', 'A'], ['A', 'A', 'A']], 0, 0, 3) + {(1, 2), (2, 1), (0, 0), (2, 0), (0, 2), (2, 2), (1, 0)} + >>> find_repeat([['-', '-', '-'], ['-', '-', '-'], ['-', '-', '-']], 1, 1, 3) + set() + """ + + column = size - 1 - column + visited = set() + repeated = set() + + if (color := matrix_g[column][row]) != "-": + + def dfs(row_n: int, column_n: int) -> None: + if row_n < 0 or row_n >= size or column_n < 0 or column_n >= size: + return + if (row_n, column_n) in visited: + return + visited.add((row_n, column_n)) + if matrix_g[row_n][column_n] == color: + repeated.add((row_n, column_n)) + dfs(row_n - 1, column_n) + dfs(row_n + 1, column_n) + dfs(row_n, column_n - 1) + dfs(row_n, column_n + 1) + + dfs(column, row) + + return repeated + + +def increment_score(count: int) -> int: + """ + Calculates the score for a move based on the number of elements removed. + + >>> increment_score(3) + 6 + >>> increment_score(0) + 0 + """ + return int(count * (count + 1) / 2) + + +def move_x(matrix_g: list[list[str]], column: int, size: int) -> list[list[str]]: + """ + Simulates gravity in a specific column. + + >>> move_x([['-', 'A'], ['-', '-'], ['-', 'C']], 1, 2) + [['-', '-'], ['-', 'A'], ['-', 'C']] + """ + + new_list = [] + + for row in range(size): + if matrix_g[row][column] != "-": + new_list.append(matrix_g[row][column]) + else: + new_list.insert(0, matrix_g[row][column]) + for row in range(size): + matrix_g[row][column] = new_list[row] + return matrix_g + + +def move_y(matrix_g: list[list[str]], size: int) -> list[list[str]]: + """ + Shifts all columns leftward when an entire column becomes empty. + + >>> move_y([['-', 'A'], ['-', '-'], ['-', 'C']], 2) + [['A', '-'], ['-', '-'], ['-', 'C']] + """ + + empty_columns = [] + + for column in range(size - 1, -1, -1): + if all(matrix_g[row][column] == "-" for row in range(size)): + empty_columns.append(column) + + for column in empty_columns: + for col in range(column + 1, size): + for row in range(size): + matrix_g[row][col - 1] = matrix_g[row][col] + for row in range(size): + matrix_g[row][-1] = "-" + + return matrix_g + + +def play( + matrix_g: list[list[str]], pos_x: int, pos_y: int, size: int +) -> tuple[list[list[str]], int]: + """ + Processes a single move, updating the matrix and calculating the score. + + >>> play([['R', 'G'], ['R', 'G']], 0, 0, 2) + ([['G', '-'], ['G', '-']], 3) + """ + + same_colors = find_repeat(matrix_g, pos_x, pos_y, size) + + if len(same_colors) != 0: + for pos in same_colors: + matrix_g[pos[0]][pos[1]] = "-" + for column in range(size): + matrix_g = move_x(matrix_g, column, size) + + matrix_g = move_y(matrix_g, size) + + return (matrix_g, increment_score(len(same_colors))) + + +def process_game(size: int, matrix: list[str], moves: list[tuple[int, int]]) -> int: + """Processes the game logic for the given matrix and moves. + + Args: + size (int): Size of the game board. + matrix (List[str]): Initial game matrix. + moves (List[Tuple[int, int]]): List of moves as (x, y) coordinates. + + Returns: + int: The total score obtained. + >>> process_game(3, ['aaa', 'bbb', 'ccc'], [(0, 0)]) + 6 + """ + + game_matrix = [list(row) for row in matrix] + total_score = 0 + + for move in moves: + pos_x, pos_y = move + game_matrix, score = play(game_matrix, pos_x, pos_y, size) + total_score += score + + return total_score + + +if __name__ == "__main__": + import doctest + + doctest.testmod(verbose=True) + try: + size = int(input("Enter the size of the matrix: ")) + validate_matrix_size(size) + print(f"Enter the {size} rows of the matrix:") + matrix = [input(f"Row {i+1}: ") for i in range(size)] + validate_matrix_content(matrix, size) + moves_input = input("Enter the moves (e.g., '0 0, 1 1'): ") + moves = parse_moves(moves_input) + validate_moves(moves, size) + score = process_game(size, matrix, moves) + print(f"Total score: {score}") + except ValueError as e: + print(f"{e}") From b22fab0ea46c7b625d8137d1fb07d082e20d6d7b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 2 Dec 2024 22:35:21 +0100 Subject: [PATCH 691/757] [pre-commit.ci] pre-commit autoupdate (#12404) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.8.0 → v0.8.1](https://github.com/astral-sh/ruff-pre-commit/compare/v0.8.0...v0.8.1) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 64d9a833cd21..bef251749c19 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.8.0 + rev: v0.8.1 hooks: - id: ruff - id: ruff-format From 0bcdfbdb34e03e24e2f5da90a7236226b721981d Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Thu, 5 Dec 2024 05:34:48 +0100 Subject: [PATCH 692/757] Use Astral uv (#12402) * Use Astral uv * uvx vs uv run * uv sync --group=euler-validate,test * uv sync --group=euler-validate --group=test * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * --group=test --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 15 +- .github/workflows/project_euler.yml | 16 +- .github/workflows/ruff.yml | 4 +- .github/workflows/sphinx.yml | 6 +- pyproject.toml | 48 +- requirements.txt | 6 - uv.lock | 1246 +++++++++++++++++++++++++++ 7 files changed, 1301 insertions(+), 40 deletions(-) create mode 100644 uv.lock diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b5703e2f1ab6..a6f308715cc2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,21 +10,18 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - uses: astral-sh/setup-uv@v4 + with: + enable-cache: true + cache-dependency-glob: uv.lock - uses: actions/setup-python@v5 with: python-version: 3.13 allow-prereleases: true - - uses: actions/cache@v4 - with: - path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }} - - name: Install dependencies - run: | - python -m pip install --upgrade pip setuptools wheel - python -m pip install pytest-cov -r requirements.txt + - run: uv sync --group=test - name: Run tests # TODO: #8818 Re-enable quantum tests - run: pytest + run: uv run pytest --ignore=computer_vision/cnn_classification.py --ignore=docs/conf.py --ignore=dynamic_programming/k_means_clustering_tensorflow.py diff --git a/.github/workflows/project_euler.yml b/.github/workflows/project_euler.yml index 59e1208a650d..84c55335451e 100644 --- a/.github/workflows/project_euler.yml +++ b/.github/workflows/project_euler.yml @@ -15,25 +15,21 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - uses: astral-sh/setup-uv@v4 - uses: actions/setup-python@v5 with: python-version: 3.x - - name: Install pytest and pytest-cov - run: | - python -m pip install --upgrade pip - python -m pip install --upgrade numpy pytest pytest-cov - - run: pytest --doctest-modules --cov-report=term-missing:skip-covered --cov=project_euler/ project_euler/ + - run: uv sync --group=euler-validate --group=test + - run: uv run pytest --doctest-modules --cov-report=term-missing:skip-covered --cov=project_euler/ project_euler/ validate-solutions: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - uses: astral-sh/setup-uv@v4 - uses: actions/setup-python@v5 with: python-version: 3.x - - name: Install pytest and requests - run: | - python -m pip install --upgrade pip - python -m pip install --upgrade numpy pytest requests - - run: pytest scripts/validate_solutions.py + - run: uv sync --group=euler-validate --group=test + - run: uv run pytest scripts/validate_solutions.py env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/ruff.yml b/.github/workflows/ruff.yml index d354eba672ae..2c6f92fcf7bf 100644 --- a/.github/workflows/ruff.yml +++ b/.github/workflows/ruff.yml @@ -12,5 +12,5 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - run: pip install --user ruff - - run: ruff check --output-format=github . + - uses: astral-sh/setup-uv@v4 + - run: uvx ruff check --output-format=github . diff --git a/.github/workflows/sphinx.yml b/.github/workflows/sphinx.yml index 9dfe344f9743..e3e2ce81a95d 100644 --- a/.github/workflows/sphinx.yml +++ b/.github/workflows/sphinx.yml @@ -26,14 +26,14 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - uses: astral-sh/setup-uv@v4 - uses: actions/setup-python@v5 with: python-version: 3.13 allow-prereleases: true - - run: pip install --upgrade pip - - run: pip install myst-parser sphinx-autoapi sphinx-pyproject + - run: uv sync --group=docs - uses: actions/configure-pages@v5 - - run: sphinx-build -c docs . docs/_build/html + - run: uv run sphinx-build -c docs . docs/_build/html - uses: actions/upload-pages-artifact@v3 with: path: docs/_build/html diff --git a/pyproject.toml b/pyproject.toml index c60ec246144e..7b7176705c44 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,12 +7,43 @@ requires-python = ">=3.13" classifiers = [ "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3.13", +] +dependencies = [ + "beautifulsoup4>=4.12.3", + "fake-useragent>=1.5.1", + "imageio>=2.36.1", + "keras>=3.7", + "lxml>=5.3", + "matplotlib>=3.9.3", + "numpy>=2.1.3", + "opencv-python>=4.10.0.84", + "pandas>=2.2.3", + "pillow>=11", + "requests>=2.32.3", + "rich>=13.9.4", + "scikit-learn>=1.5.2", + "sphinx-pyproject>=0.3", + "statsmodels>=0.14.4", + "sympy>=1.13.3", + "tweepy>=4.14", + "typing-extensions>=4.12.2", + "xgboost>=2.1.3", +] +[dependency-groups] +test = [ + "pytest>=8.3.4", + "pytest-cov>=6", ] -optional-dependencies.docs = [ - "myst-parser", - "sphinx-autoapi", - "sphinx-pyproject", + +docs = [ + "myst-parser>=4", + "sphinx-autoapi>=3.4", + "sphinx-pyproject>=0.3", +] +euler-validate = [ + "numpy>=2.1.3", + "requests>=2.32.3", ] [tool.ruff] @@ -61,8 +92,8 @@ lint.select = [ "UP", # pyupgrade "W", # pycodestyle "YTT", # flake8-2020 - # "ANN", # flake8-annotations # FIX ME? - # "COM", # flake8-commas + # "ANN", # flake8-annotations -- FIX ME? + # "COM", # flake8-commas -- DO NOT FIX # "D", # pydocstyle -- FIX ME? # "ERA", # eradicate -- DO NOT FIX # "FBT", # flake8-boolean-trap # FIX ME @@ -129,10 +160,7 @@ lint.pylint.max-statements = 88 # default: 50 [tool.codespell] ignore-words-list = "3rt,ans,bitap,crate,damon,fo,followings,hist,iff,kwanza,manuel,mater,secant,som,sur,tim,toi,zar" -skip = "./.*,*.json,ciphers/prehistoric_men.txt,project_euler/problem_022/p022_names.txt,pyproject.toml,strings/dictionary.txt,strings/words.txt" - -[tool.pyproject-fmt] -max_supported_python = "3.13" +skip = "./.*,*.json,*.lock,ciphers/prehistoric_men.txt,project_euler/problem_022/p022_names.txt,pyproject.toml,strings/dictionary.txt,strings/words.txt" [tool.pytest.ini_options] markers = [ diff --git a/requirements.txt b/requirements.txt index 6754363332c4..4cc83f44987d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,18 +8,12 @@ numpy opencv-python pandas pillow -# projectq # uncomment once quantum/quantum_random.py is fixed -qiskit ; python_version < '3.12' -qiskit-aer ; python_version < '3.12' requests rich -# scikit-fuzzy # uncomment once fuzzy_logic/fuzzy_operations.py is fixed scikit-learn sphinx_pyproject statsmodels sympy -tensorflow ; python_version < '3.13' tweepy -# yulewalker # uncomment once audio_filters/equal_loudness_filter.py is fixed typing_extensions xgboost diff --git a/uv.lock b/uv.lock new file mode 100644 index 000000000000..077288f041a1 --- /dev/null +++ b/uv.lock @@ -0,0 +1,1246 @@ +version = 1 +requires-python = ">=3.13" +resolution-markers = [ + "platform_system == 'Darwin'", + "platform_machine == 'aarch64' and platform_system == 'Linux'", + "(platform_machine != 'aarch64' and platform_system != 'Darwin') or (platform_system != 'Darwin' and platform_system != 'Linux')", +] + +[[package]] +name = "absl-py" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7a/8f/fc001b92ecc467cc32ab38398bd0bfb45df46e7523bf33c2ad22a505f06e/absl-py-2.1.0.tar.gz", hash = "sha256:7820790efbb316739cde8b4e19357243fc3608a152024288513dd968d7d959ff", size = 118055 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a2/ad/e0d3c824784ff121c03cc031f944bc7e139a8f1870ffd2845cc2dd76f6c4/absl_py-2.1.0-py3-none-any.whl", hash = "sha256:526a04eadab8b4ee719ce68f204172ead1027549089702d99b9059f129ff1308", size = 133706 }, +] + +[[package]] +name = "alabaster" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a6/f8/d9c74d0daf3f742840fd818d69cfae176fa332022fd44e3469487d5a9420/alabaster-1.0.0.tar.gz", hash = "sha256:c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e", size = 24210 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/b3/6b4067be973ae96ba0d615946e314c5ae35f9f993eca561b356540bb0c2b/alabaster-1.0.0-py3-none-any.whl", hash = "sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b", size = 13929 }, +] + +[[package]] +name = "astroid" +version = "3.3.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/38/1e/326fb1d3d83a3bb77c9f9be29d31f2901e35acb94b0605c3f2e5085047f9/astroid-3.3.5.tar.gz", hash = "sha256:5cfc40ae9f68311075d27ef68a4841bdc5cc7f6cf86671b49f00607d30188e2d", size = 397229 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/41/30/624365383fa4a40329c0f0bbbc151abc4a64e30dfc110fc8f6e2afcd02bb/astroid-3.3.5-py3-none-any.whl", hash = "sha256:a9d1c946ada25098d790e079ba2a1b112157278f3fb7e718ae6a9252f5835dc8", size = 274586 }, +] + +[[package]] +name = "babel" +version = "2.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2a/74/f1bc80f23eeba13393b7222b11d95ca3af2c1e28edca18af487137eefed9/babel-2.16.0.tar.gz", hash = "sha256:d1f3554ca26605fe173f3de0c65f750f5a42f924499bf134de6423582298e316", size = 9348104 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ed/20/bc79bc575ba2e2a7f70e8a1155618bb1301eaa5132a8271373a6903f73f8/babel-2.16.0-py3-none-any.whl", hash = "sha256:368b5b98b37c06b7daf6696391c3240c938b37767d4584413e8438c5c435fa8b", size = 9587599 }, +] + +[[package]] +name = "beautifulsoup4" +version = "4.12.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "soupsieve" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b3/ca/824b1195773ce6166d388573fc106ce56d4a805bd7427b624e063596ec58/beautifulsoup4-4.12.3.tar.gz", hash = "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051", size = 581181 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b1/fe/e8c672695b37eecc5cbf43e1d0638d88d66ba3a44c4d321c796f4e59167f/beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed", size = 147925 }, +] + +[[package]] +name = "certifi" +version = "2024.8.30" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/ee/9b19140fe824b367c04c5e1b369942dd754c4c5462d5674002f75c4dedc1/certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9", size = 168507 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/90/3c9ff0512038035f59d279fddeb79f5f1eccd8859f06d6163c58798b9487/certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8", size = 167321 }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f2/4f/e1808dc01273379acc506d18f1504eb2d299bd4131743b9fc54d7be4df1e/charset_normalizer-3.4.0.tar.gz", hash = "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e", size = 106620 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f3/89/68a4c86f1a0002810a27f12e9a7b22feb198c59b2f05231349fbce5c06f4/charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:dd4eda173a9fcccb5f2e2bd2a9f423d180194b1bf17cf59e3269899235b2a114", size = 194617 }, + { url = "https://files.pythonhosted.org/packages/4f/cd/8947fe425e2ab0aa57aceb7807af13a0e4162cd21eee42ef5b053447edf5/charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e9e3c4c9e1ed40ea53acf11e2a386383c3304212c965773704e4603d589343ed", size = 125310 }, + { url = "https://files.pythonhosted.org/packages/5b/f0/b5263e8668a4ee9becc2b451ed909e9c27058337fda5b8c49588183c267a/charset_normalizer-3.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92a7e36b000bf022ef3dbb9c46bfe2d52c047d5e3f3343f43204263c5addc250", size = 119126 }, + { url = "https://files.pythonhosted.org/packages/ff/6e/e445afe4f7fda27a533f3234b627b3e515a1b9429bc981c9a5e2aa5d97b6/charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54b6a92d009cbe2fb11054ba694bc9e284dad30a26757b1e372a1fdddaf21920", size = 139342 }, + { url = "https://files.pythonhosted.org/packages/a1/b2/4af9993b532d93270538ad4926c8e37dc29f2111c36f9c629840c57cd9b3/charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ffd9493de4c922f2a38c2bf62b831dcec90ac673ed1ca182fe11b4d8e9f2a64", size = 149383 }, + { url = "https://files.pythonhosted.org/packages/fb/6f/4e78c3b97686b871db9be6f31d64e9264e889f8c9d7ab33c771f847f79b7/charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35c404d74c2926d0287fbd63ed5d27eb911eb9e4a3bb2c6d294f3cfd4a9e0c23", size = 142214 }, + { url = "https://files.pythonhosted.org/packages/2b/c9/1c8fe3ce05d30c87eff498592c89015b19fade13df42850aafae09e94f35/charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4796efc4faf6b53a18e3d46343535caed491776a22af773f366534056c4e1fbc", size = 144104 }, + { url = "https://files.pythonhosted.org/packages/ee/68/efad5dcb306bf37db7db338338e7bb8ebd8cf38ee5bbd5ceaaaa46f257e6/charset_normalizer-3.4.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7fdd52961feb4c96507aa649550ec2a0d527c086d284749b2f582f2d40a2e0d", size = 146255 }, + { url = "https://files.pythonhosted.org/packages/0c/75/1ed813c3ffd200b1f3e71121c95da3f79e6d2a96120163443b3ad1057505/charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:92db3c28b5b2a273346bebb24857fda45601aef6ae1c011c0a997106581e8a88", size = 140251 }, + { url = "https://files.pythonhosted.org/packages/7d/0d/6f32255c1979653b448d3c709583557a4d24ff97ac4f3a5be156b2e6a210/charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ab973df98fc99ab39080bfb0eb3a925181454d7c3ac8a1e695fddfae696d9e90", size = 148474 }, + { url = "https://files.pythonhosted.org/packages/ac/a0/c1b5298de4670d997101fef95b97ac440e8c8d8b4efa5a4d1ef44af82f0d/charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4b67fdab07fdd3c10bb21edab3cbfe8cf5696f453afce75d815d9d7223fbe88b", size = 151849 }, + { url = "https://files.pythonhosted.org/packages/04/4f/b3961ba0c664989ba63e30595a3ed0875d6790ff26671e2aae2fdc28a399/charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:aa41e526a5d4a9dfcfbab0716c7e8a1b215abd3f3df5a45cf18a12721d31cb5d", size = 149781 }, + { url = "https://files.pythonhosted.org/packages/d8/90/6af4cd042066a4adad58ae25648a12c09c879efa4849c705719ba1b23d8c/charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ffc519621dce0c767e96b9c53f09c5d215578e10b02c285809f76509a3931482", size = 144970 }, + { url = "https://files.pythonhosted.org/packages/cc/67/e5e7e0cbfefc4ca79025238b43cdf8a2037854195b37d6417f3d0895c4c2/charset_normalizer-3.4.0-cp313-cp313-win32.whl", hash = "sha256:f19c1585933c82098c2a520f8ec1227f20e339e33aca8fa6f956f6691b784e67", size = 94973 }, + { url = "https://files.pythonhosted.org/packages/65/97/fc9bbc54ee13d33dc54a7fcf17b26368b18505500fc01e228c27b5222d80/charset_normalizer-3.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:707b82d19e65c9bd28b81dde95249b07bf9f5b90ebe1ef17d9b57473f8a64b7b", size = 102308 }, + { url = "https://files.pythonhosted.org/packages/bf/9b/08c0432272d77b04803958a4598a51e2a4b51c06640af8b8f0f908c18bf2/charset_normalizer-3.4.0-py3-none-any.whl", hash = "sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079", size = 49446 }, +] + +[[package]] +name = "codespell" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a0/a9/98353dfc7afcdf18cffd2dd3e959a25eaaf2728cf450caa59af89648a8e4/codespell-2.3.0.tar.gz", hash = "sha256:360c7d10f75e65f67bad720af7007e1060a5d395670ec11a7ed1fed9dd17471f", size = 329791 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0e/20/b6019add11e84f821184234cea0ad91442373489ef7ccfa3d73a71b908fa/codespell-2.3.0-py3-none-any.whl", hash = "sha256:a9c7cef2501c9cfede2110fd6d4e5e62296920efe9abfb84648df866e47f58d1", size = 329167 }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, +] + +[[package]] +name = "contourpy" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/25/c2/fc7193cc5383637ff390a712e88e4ded0452c9fbcf84abe3de5ea3df1866/contourpy-1.3.1.tar.gz", hash = "sha256:dfd97abd83335045a913e3bcc4a09c0ceadbe66580cf573fe961f4a825efa699", size = 13465753 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/e7/de62050dce687c5e96f946a93546910bc67e483fe05324439e329ff36105/contourpy-1.3.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a761d9ccfc5e2ecd1bf05534eda382aa14c3e4f9205ba5b1684ecfe400716ef2", size = 271548 }, + { url = "https://files.pythonhosted.org/packages/78/4d/c2a09ae014ae984c6bdd29c11e74d3121b25eaa117eca0bb76340efd7e1c/contourpy-1.3.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:523a8ee12edfa36f6d2a49407f705a6ef4c5098de4f498619787e272de93f2d5", size = 255576 }, + { url = "https://files.pythonhosted.org/packages/ab/8a/915380ee96a5638bda80cd061ccb8e666bfdccea38d5741cb69e6dbd61fc/contourpy-1.3.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece6df05e2c41bd46776fbc712e0996f7c94e0d0543af1656956d150c4ca7c81", size = 306635 }, + { url = "https://files.pythonhosted.org/packages/29/5c/c83ce09375428298acd4e6582aeb68b1e0d1447f877fa993d9bf6cd3b0a0/contourpy-1.3.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:573abb30e0e05bf31ed067d2f82500ecfdaec15627a59d63ea2d95714790f5c2", size = 345925 }, + { url = "https://files.pythonhosted.org/packages/29/63/5b52f4a15e80c66c8078a641a3bfacd6e07106835682454647aca1afc852/contourpy-1.3.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9fa36448e6a3a1a9a2ba23c02012c43ed88905ec80163f2ffe2421c7192a5d7", size = 318000 }, + { url = "https://files.pythonhosted.org/packages/9a/e2/30ca086c692691129849198659bf0556d72a757fe2769eb9620a27169296/contourpy-1.3.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ea9924d28fc5586bf0b42d15f590b10c224117e74409dd7a0be3b62b74a501c", size = 322689 }, + { url = "https://files.pythonhosted.org/packages/6b/77/f37812ef700f1f185d348394debf33f22d531e714cf6a35d13d68a7003c7/contourpy-1.3.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5b75aa69cb4d6f137b36f7eb2ace9280cfb60c55dc5f61c731fdf6f037f958a3", size = 1268413 }, + { url = "https://files.pythonhosted.org/packages/3f/6d/ce84e79cdd128542ebeb268f84abb4b093af78e7f8ec504676673d2675bc/contourpy-1.3.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:041b640d4ec01922083645a94bb3b2e777e6b626788f4095cf21abbe266413c1", size = 1326530 }, + { url = "https://files.pythonhosted.org/packages/72/22/8282f4eae20c73c89bee7a82a19c4e27af9b57bb602ecaa00713d5bdb54d/contourpy-1.3.1-cp313-cp313-win32.whl", hash = "sha256:36987a15e8ace5f58d4d5da9dca82d498c2bbb28dff6e5d04fbfcc35a9cb3a82", size = 175315 }, + { url = "https://files.pythonhosted.org/packages/e3/d5/28bca491f65312b438fbf076589dcde7f6f966b196d900777f5811b9c4e2/contourpy-1.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:a7895f46d47671fa7ceec40f31fae721da51ad34bdca0bee83e38870b1f47ffd", size = 220987 }, + { url = "https://files.pythonhosted.org/packages/2f/24/a4b285d6adaaf9746e4700932f579f1a7b6f9681109f694cfa233ae75c4e/contourpy-1.3.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:9ddeb796389dadcd884c7eb07bd14ef12408aaae358f0e2ae24114d797eede30", size = 285001 }, + { url = "https://files.pythonhosted.org/packages/48/1d/fb49a401b5ca4f06ccf467cd6c4f1fd65767e63c21322b29b04ec40b40b9/contourpy-1.3.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:19c1555a6801c2f084c7ddc1c6e11f02eb6a6016ca1318dd5452ba3f613a1751", size = 268553 }, + { url = "https://files.pythonhosted.org/packages/79/1e/4aef9470d13fd029087388fae750dccb49a50c012a6c8d1d634295caa644/contourpy-1.3.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:841ad858cff65c2c04bf93875e384ccb82b654574a6d7f30453a04f04af71342", size = 310386 }, + { url = "https://files.pythonhosted.org/packages/b0/34/910dc706ed70153b60392b5305c708c9810d425bde12499c9184a1100888/contourpy-1.3.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4318af1c925fb9a4fb190559ef3eec206845f63e80fb603d47f2d6d67683901c", size = 349806 }, + { url = "https://files.pythonhosted.org/packages/31/3c/faee6a40d66d7f2a87f7102236bf4780c57990dd7f98e5ff29881b1b1344/contourpy-1.3.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:14c102b0eab282427b662cb590f2e9340a9d91a1c297f48729431f2dcd16e14f", size = 321108 }, + { url = "https://files.pythonhosted.org/packages/17/69/390dc9b20dd4bb20585651d7316cc3054b7d4a7b4f8b710b2b698e08968d/contourpy-1.3.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05e806338bfeaa006acbdeba0ad681a10be63b26e1b17317bfac3c5d98f36cda", size = 327291 }, + { url = "https://files.pythonhosted.org/packages/ef/74/7030b67c4e941fe1e5424a3d988080e83568030ce0355f7c9fc556455b01/contourpy-1.3.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4d76d5993a34ef3df5181ba3c92fabb93f1eaa5729504fb03423fcd9f3177242", size = 1263752 }, + { url = "https://files.pythonhosted.org/packages/f0/ed/92d86f183a8615f13f6b9cbfc5d4298a509d6ce433432e21da838b4b63f4/contourpy-1.3.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:89785bb2a1980c1bd87f0cb1517a71cde374776a5f150936b82580ae6ead44a1", size = 1318403 }, + { url = "https://files.pythonhosted.org/packages/b3/0e/c8e4950c77dcfc897c71d61e56690a0a9df39543d2164040301b5df8e67b/contourpy-1.3.1-cp313-cp313t-win32.whl", hash = "sha256:8eb96e79b9f3dcadbad2a3891672f81cdcab7f95b27f28f1c67d75f045b6b4f1", size = 185117 }, + { url = "https://files.pythonhosted.org/packages/c1/31/1ae946f11dfbd229222e6d6ad8e7bd1891d3d48bde5fbf7a0beb9491f8e3/contourpy-1.3.1-cp313-cp313t-win_amd64.whl", hash = "sha256:287ccc248c9e0d0566934e7d606201abd74761b5703d804ff3df8935f523d546", size = 236668 }, +] + +[[package]] +name = "coverage" +version = "7.6.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ab/75/aecfd0a3adbec6e45753976bc2a9fed62b42cea9a206d10fd29244a77953/coverage-7.6.8.tar.gz", hash = "sha256:8b2b8503edb06822c86d82fa64a4a5cb0760bb8f31f26e138ec743f422f37cfc", size = 801425 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/84/6f0ccf94a098ac3d6d6f236bd3905eeac049a9e0efcd9a63d4feca37ac4b/coverage-7.6.8-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0b0c69f4f724c64dfbfe79f5dfb503b42fe6127b8d479b2677f2b227478db2eb", size = 207313 }, + { url = "https://files.pythonhosted.org/packages/db/2b/e3b3a3a12ebec738c545897ac9f314620470fcbc368cdac88cf14974ba20/coverage-7.6.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c15b32a7aca8038ed7644f854bf17b663bc38e1671b5d6f43f9a2b2bd0c46f63", size = 207574 }, + { url = "https://files.pythonhosted.org/packages/db/c0/5bf95d42b6a8d21dfce5025ce187f15db57d6460a59b67a95fe8728162f1/coverage-7.6.8-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63068a11171e4276f6ece913bde059e77c713b48c3a848814a6537f35afb8365", size = 240090 }, + { url = "https://files.pythonhosted.org/packages/57/b8/d6fd17d1a8e2b0e1a4e8b9cb1f0f261afd422570735899759c0584236916/coverage-7.6.8-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f4548c5ead23ad13fb7a2c8ea541357474ec13c2b736feb02e19a3085fac002", size = 237237 }, + { url = "https://files.pythonhosted.org/packages/d4/e4/a91e9bb46809c8b63e68fc5db5c4d567d3423b6691d049a4f950e38fbe9d/coverage-7.6.8-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b4b4299dd0d2c67caaaf286d58aef5e75b125b95615dda4542561a5a566a1e3", size = 239225 }, + { url = "https://files.pythonhosted.org/packages/31/9c/9b99b0591ec4555b7292d271e005f27b465388ce166056c435b288db6a69/coverage-7.6.8-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c9ebfb2507751f7196995142f057d1324afdab56db1d9743aab7f50289abd022", size = 238888 }, + { url = "https://files.pythonhosted.org/packages/a6/85/285c2df9a04bc7c31f21fd9d4a24d19e040ec5e2ff06e572af1f6514c9e7/coverage-7.6.8-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:c1b4474beee02ede1eef86c25ad4600a424fe36cff01a6103cb4533c6bf0169e", size = 236974 }, + { url = "https://files.pythonhosted.org/packages/cb/a1/95ec8522206f76cdca033bf8bb61fff56429fb414835fc4d34651dfd29fc/coverage-7.6.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d9fd2547e6decdbf985d579cf3fc78e4c1d662b9b0ff7cc7862baaab71c9cc5b", size = 238815 }, + { url = "https://files.pythonhosted.org/packages/8d/ac/687e9ba5e6d0979e9dab5c02e01c4f24ac58260ef82d88d3b433b3f84f1e/coverage-7.6.8-cp313-cp313-win32.whl", hash = "sha256:8aae5aea53cbfe024919715eca696b1a3201886ce83790537d1c3668459c7146", size = 209957 }, + { url = "https://files.pythonhosted.org/packages/2f/a3/b61cc8e3fcf075293fb0f3dee405748453c5ba28ac02ceb4a87f52bdb105/coverage-7.6.8-cp313-cp313-win_amd64.whl", hash = "sha256:ae270e79f7e169ccfe23284ff5ea2d52a6f401dc01b337efb54b3783e2ce3f28", size = 210711 }, + { url = "https://files.pythonhosted.org/packages/ee/4b/891c8b9acf1b62c85e4a71dac142ab9284e8347409b7355de02e3f38306f/coverage-7.6.8-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:de38add67a0af869b0d79c525d3e4588ac1ffa92f39116dbe0ed9753f26eba7d", size = 208053 }, + { url = "https://files.pythonhosted.org/packages/18/a9/9e330409b291cc002723d339346452800e78df1ce50774ca439ade1d374f/coverage-7.6.8-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b07c25d52b1c16ce5de088046cd2432b30f9ad5e224ff17c8f496d9cb7d1d451", size = 208329 }, + { url = "https://files.pythonhosted.org/packages/9c/0d/33635fd429f6589c6e1cdfc7bf581aefe4c1792fbff06383f9d37f59db60/coverage-7.6.8-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62a66ff235e4c2e37ed3b6104d8b478d767ff73838d1222132a7a026aa548764", size = 251052 }, + { url = "https://files.pythonhosted.org/packages/23/32/8a08da0e46f3830bbb9a5b40614241b2e700f27a9c2889f53122486443ed/coverage-7.6.8-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09b9f848b28081e7b975a3626e9081574a7b9196cde26604540582da60235fdf", size = 246765 }, + { url = "https://files.pythonhosted.org/packages/56/3f/3b86303d2c14350fdb1c6c4dbf9bc76000af2382f42ca1d4d99c6317666e/coverage-7.6.8-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:093896e530c38c8e9c996901858ac63f3d4171268db2c9c8b373a228f459bbc5", size = 249125 }, + { url = "https://files.pythonhosted.org/packages/36/cb/c4f081b9023f9fd8646dbc4ef77be0df090263e8f66f4ea47681e0dc2cff/coverage-7.6.8-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9a7b8ac36fd688c8361cbc7bf1cb5866977ece6e0b17c34aa0df58bda4fa18a4", size = 248615 }, + { url = "https://files.pythonhosted.org/packages/32/ee/53bdbf67760928c44b57b2c28a8c0a4bf544f85a9ee129a63ba5c78fdee4/coverage-7.6.8-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:38c51297b35b3ed91670e1e4efb702b790002e3245a28c76e627478aa3c10d83", size = 246507 }, + { url = "https://files.pythonhosted.org/packages/57/49/5a57910bd0af6d8e802b4ca65292576d19b54b49f81577fd898505dee075/coverage-7.6.8-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2e4e0f60cb4bd7396108823548e82fdab72d4d8a65e58e2c19bbbc2f1e2bfa4b", size = 247785 }, + { url = "https://files.pythonhosted.org/packages/bd/37/e450c9f6b297c79bb9858407396ed3e084dcc22990dd110ab01d5ceb9770/coverage-7.6.8-cp313-cp313t-win32.whl", hash = "sha256:6535d996f6537ecb298b4e287a855f37deaf64ff007162ec0afb9ab8ba3b8b71", size = 210605 }, + { url = "https://files.pythonhosted.org/packages/44/79/7d0c7dd237c6905018e2936cd1055fe1d42e7eba2ebab3c00f4aad2a27d7/coverage-7.6.8-cp313-cp313t-win_amd64.whl", hash = "sha256:c79c0685f142ca53256722a384540832420dff4ab15fec1863d7e5bc8691bdcc", size = 211777 }, +] + +[[package]] +name = "cycler" +version = "0.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c", size = 7615 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321 }, +] + +[[package]] +name = "docutils" +version = "0.21.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ae/ed/aefcc8cd0ba62a0560c3c18c33925362d46c6075480bfa4df87b28e169a9/docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f", size = 2204444 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8f/d7/9322c609343d929e75e7e5e6255e614fcc67572cfd083959cdef3b7aad79/docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2", size = 587408 }, +] + +[[package]] +name = "dom-toml" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "domdf-python-tools" }, + { name = "tomli" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/70/34/f7690cf288eaa86b55c8f1b890d0834e6df44a026a88eca12274fcd624ab/dom_toml-2.0.0.tar.gz", hash = "sha256:3c07e8436538994974127b1ae037661d1a779ac915c44fd06b3ab5fe140ff589", size = 11133 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/99/b6fc87dff3138491d81676bdcbf1531080925ba41486ec1dafd86e33fdbc/dom_toml-2.0.0-py3-none-any.whl", hash = "sha256:0b6d02a72bcbc6be8175c61afc30623bbb6b74c4650f2a806fbc3fb7fe86935d", size = 13376 }, +] + +[[package]] +name = "domdf-python-tools" +version = "3.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "natsort" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6b/78/974e10c583ba9d2302e748c9585313a7f2c7ba00e4f600324f432e38fe68/domdf_python_tools-3.9.0.tar.gz", hash = "sha256:1f8a96971178333a55e083e35610d7688cd7620ad2b99790164e1fc1a3614c18", size = 103792 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/e9/7447a88b217650a74927d3444a89507986479a69b83741900eddd34167fe/domdf_python_tools-3.9.0-py3-none-any.whl", hash = "sha256:4e1ef365cbc24627d6d1e90cf7d46d8ab8df967e1237f4a26885f6986c78872e", size = 127106 }, +] + +[[package]] +name = "fake-useragent" +version = "1.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/24/a1/1f662631ab153975fa8dbf09296324ecbaf53370dce922054e8de6b57370/fake-useragent-1.5.1.tar.gz", hash = "sha256:6387269f5a2196b5ba7ed8935852f75486845a1c95c50e72460e6a8e762f5c49", size = 22631 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e4/99/60d8cf1b26938c2e0a57e232f7f15641dfcd6f8deda454d73e4145910ff6/fake_useragent-1.5.1-py3-none-any.whl", hash = "sha256:57415096557c8a4e23b62a375c21c55af5fd4ba30549227f562d2c4f5b60e3b3", size = 17190 }, +] + +[[package]] +name = "fonttools" +version = "4.55.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/4e/053fe1b5c0ce346c0a9d0557492c654362bafb14f026eae0d3ee98009152/fonttools-4.55.0.tar.gz", hash = "sha256:7636acc6ab733572d5e7eec922b254ead611f1cdad17be3f0be7418e8bfaca71", size = 3490431 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c3/87/a669ac26c6077e37ffb06abf29c5571789eefe518d06c52df392181ee694/fonttools-4.55.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8118dc571921dc9e4b288d9cb423ceaf886d195a2e5329cc427df82bba872cd9", size = 2752519 }, + { url = "https://files.pythonhosted.org/packages/0c/e9/4822ad238fe215133c7df20f1cdb1a58cfb634a31523e77ff0fb2033970a/fonttools-4.55.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:01124f2ca6c29fad4132d930da69158d3f49b2350e4a779e1efbe0e82bd63f6c", size = 2286819 }, + { url = "https://files.pythonhosted.org/packages/3e/a4/d7941c3897129e60fe68d20e4819fda4d0c4858d77badae0e80ca6440b36/fonttools-4.55.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81ffd58d2691f11f7c8438796e9f21c374828805d33e83ff4b76e4635633674c", size = 4770382 }, + { url = "https://files.pythonhosted.org/packages/31/cf/c51ea1348f9fba9c627439afad9dee0090040809ab431f4422b5bfdda34c/fonttools-4.55.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5435e5f1eb893c35c2bc2b9cd3c9596b0fcb0a59e7a14121562986dd4c47b8dd", size = 4858336 }, + { url = "https://files.pythonhosted.org/packages/73/be/36c1fe0e5c9a96b068ddd7e82001243bbe7fe12549c8d14e1bd025bf40c9/fonttools-4.55.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d12081729280c39d001edd0f4f06d696014c26e6e9a0a55488fabc37c28945e4", size = 4756072 }, + { url = "https://files.pythonhosted.org/packages/5c/18/6dd381c29f215a017f79aa9fea0722424a0046b47991c4390a78ff87ce0c/fonttools-4.55.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a7ad1f1b98ab6cb927ab924a38a8649f1ffd7525c75fe5b594f5dab17af70e18", size = 5008668 }, + { url = "https://files.pythonhosted.org/packages/b8/95/316f20092b389b927dba1d1dccd3f541853f96e707e210f1b9f4e7bacdd5/fonttools-4.55.0-cp313-cp313-win32.whl", hash = "sha256:abe62987c37630dca69a104266277216de1023cf570c1643bb3a19a9509e7a1b", size = 2155841 }, + { url = "https://files.pythonhosted.org/packages/35/ca/b4638aa3e446184892e2f9cc8ef44bb506f47fea04580df7fb84f5a4363d/fonttools-4.55.0-cp313-cp313-win_amd64.whl", hash = "sha256:2863555ba90b573e4201feaf87a7e71ca3b97c05aa4d63548a4b69ea16c9e998", size = 2200587 }, + { url = "https://files.pythonhosted.org/packages/b4/4a/786589606d4989cb34d8bc766cd687d955aaf3039c367fe7104bcf82dc98/fonttools-4.55.0-py3-none-any.whl", hash = "sha256:12db5888cd4dd3fcc9f0ee60c6edd3c7e1fd44b7dd0f31381ea03df68f8a153f", size = 1100249 }, +] + +[[package]] +name = "h5py" +version = "3.12.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cc/0c/5c2b0a88158682aeafb10c1c2b735df5bc31f165bfe192f2ee9f2a23b5f1/h5py-3.12.1.tar.gz", hash = "sha256:326d70b53d31baa61f00b8aa5f95c2fcb9621a3ee8365d770c551a13dbbcbfdf", size = 411457 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/23/1c/ecdd0efab52c24f2a9bf2324289828b860e8dd1e3c5ada3cf0889e14fdc1/h5py-3.12.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:513171e90ed92236fc2ca363ce7a2fc6f2827375efcbb0cc7fbdd7fe11fecafc", size = 3346239 }, + { url = "https://files.pythonhosted.org/packages/93/cd/5b6f574bf3e318bbe305bc93ba45181676550eb44ba35e006d2e98004eaa/h5py-3.12.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:59400f88343b79655a242068a9c900001a34b63e3afb040bd7cdf717e440f653", size = 2843416 }, + { url = "https://files.pythonhosted.org/packages/8a/4f/b74332f313bfbe94ba03fff784219b9db385e6139708e55b11490149f90a/h5py-3.12.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3e465aee0ec353949f0f46bf6c6f9790a2006af896cee7c178a8c3e5090aa32", size = 5154390 }, + { url = "https://files.pythonhosted.org/packages/1a/57/93ea9e10a6457ea8d3b867207deb29a527e966a08a84c57ffd954e32152a/h5py-3.12.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba51c0c5e029bb5420a343586ff79d56e7455d496d18a30309616fdbeed1068f", size = 5378244 }, + { url = "https://files.pythonhosted.org/packages/50/51/0bbf3663062b2eeee78aa51da71e065f8a0a6e3cb950cc7020b4444999e6/h5py-3.12.1-cp313-cp313-win_amd64.whl", hash = "sha256:52ab036c6c97055b85b2a242cb540ff9590bacfda0c03dd0cf0661b311f522f8", size = 2979760 }, +] + +[[package]] +name = "idna" +version = "3.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, +] + +[[package]] +name = "imageio" +version = "2.36.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "pillow" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/70/aa/2e7a49259339e691ff2b477ae0696b1784a09313c5872700bbbdd00a3030/imageio-2.36.1.tar.gz", hash = "sha256:e4e1d231f47f9a9e16100b0f7ce1a86e8856fb4d1c0fa2c4365a316f1746be62", size = 389522 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5c/f9/f78e7f5ac8077c481bf6b43b8bc736605363034b3d5eb3ce8eb79f53f5f1/imageio-2.36.1-py3-none-any.whl", hash = "sha256:20abd2cae58e55ca1af8a8dcf43293336a59adf0391f1917bf8518633cfc2cdf", size = 315435 }, +] + +[[package]] +name = "imagesize" +version = "1.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a7/84/62473fb57d61e31fef6e36d64a179c8781605429fd927b5dd608c997be31/imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a", size = 1280026 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ff/62/85c4c919272577931d407be5ba5d71c20f0b616d31a0befe0ae45bb79abd/imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b", size = 8769 }, +] + +[[package]] +name = "iniconfig" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 }, +] + +[[package]] +name = "jinja2" +version = "3.1.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ed/55/39036716d19cab0747a5020fc7e907f362fbf48c984b14e62127f7e68e5d/jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369", size = 240245 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/31/80/3a54838c3fb461f6fec263ebf3a3a41771bd05190238de3486aae8540c36/jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d", size = 133271 }, +] + +[[package]] +name = "joblib" +version = "1.4.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/64/33/60135848598c076ce4b231e1b1895170f45fbcaeaa2c9d5e38b04db70c35/joblib-1.4.2.tar.gz", hash = "sha256:2382c5816b2636fbd20a09e0f4e9dad4736765fdfb7dca582943b9c1366b3f0e", size = 2116621 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/91/29/df4b9b42f2be0b623cbd5e2140cafcaa2bef0759a00b7b70104dcfe2fb51/joblib-1.4.2-py3-none-any.whl", hash = "sha256:06d478d5674cbc267e7496a410ee875abd68e4340feff4490bcb7afb88060ae6", size = 301817 }, +] + +[[package]] +name = "keras" +version = "3.7.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "absl-py" }, + { name = "h5py" }, + { name = "ml-dtypes" }, + { name = "namex" }, + { name = "numpy" }, + { name = "optree" }, + { name = "packaging" }, + { name = "rich" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c9/c3/56fc6800c5eab94bd0f5e930751bd4c0fa1ee0aee272fad4a72723ffae87/keras-3.7.0.tar.gz", hash = "sha256:a4451a5591e75dfb414d0b84a3fd2fb9c0240cc87ebe7e397f547ce10b0e67b7", size = 924719 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8a/bf/9e3f10e55df30b0fb4bf6c2ee7d50bda2e070599b86f62ea3f9954af172b/keras-3.7.0-py3-none-any.whl", hash = "sha256:546a64f302e4779c129c06d9826fa586de752cdfd43d7dc4010c31b282587969", size = 1228365 }, +] + +[[package]] +name = "kiwisolver" +version = "1.4.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/85/4d/2255e1c76304cbd60b48cee302b66d1dde4468dc5b1160e4b7cb43778f2a/kiwisolver-1.4.7.tar.gz", hash = "sha256:9893ff81bd7107f7b685d3017cc6583daadb4fc26e4a888350df530e41980a60", size = 97286 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c4/06/7da99b04259b0f18b557a4effd1b9c901a747f7fdd84cf834ccf520cb0b2/kiwisolver-1.4.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2e6039dcbe79a8e0f044f1c39db1986a1b8071051efba3ee4d74f5b365f5226e", size = 121913 }, + { url = "https://files.pythonhosted.org/packages/97/f5/b8a370d1aa593c17882af0a6f6755aaecd643640c0ed72dcfd2eafc388b9/kiwisolver-1.4.7-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a1ecf0ac1c518487d9d23b1cd7139a6a65bc460cd101ab01f1be82ecf09794b6", size = 65627 }, + { url = "https://files.pythonhosted.org/packages/2a/fc/6c0374f7503522539e2d4d1b497f5ebad3f8ed07ab51aed2af988dd0fb65/kiwisolver-1.4.7-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7ab9ccab2b5bd5702ab0803676a580fffa2aa178c2badc5557a84cc943fcf750", size = 63888 }, + { url = "https://files.pythonhosted.org/packages/bf/3e/0b7172793d0f41cae5c923492da89a2ffcd1adf764c16159ca047463ebd3/kiwisolver-1.4.7-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f816dd2277f8d63d79f9c8473a79fe54047bc0467754962840782c575522224d", size = 1369145 }, + { url = "https://files.pythonhosted.org/packages/77/92/47d050d6f6aced2d634258123f2688fbfef8ded3c5baf2c79d94d91f1f58/kiwisolver-1.4.7-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf8bcc23ceb5a1b624572a1623b9f79d2c3b337c8c455405ef231933a10da379", size = 1461448 }, + { url = "https://files.pythonhosted.org/packages/9c/1b/8f80b18e20b3b294546a1adb41701e79ae21915f4175f311a90d042301cf/kiwisolver-1.4.7-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dea0bf229319828467d7fca8c7c189780aa9ff679c94539eed7532ebe33ed37c", size = 1578750 }, + { url = "https://files.pythonhosted.org/packages/a4/fe/fe8e72f3be0a844f257cadd72689c0848c6d5c51bc1d60429e2d14ad776e/kiwisolver-1.4.7-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c06a4c7cf15ec739ce0e5971b26c93638730090add60e183530d70848ebdd34", size = 1507175 }, + { url = "https://files.pythonhosted.org/packages/39/fa/cdc0b6105d90eadc3bee525fecc9179e2b41e1ce0293caaf49cb631a6aaf/kiwisolver-1.4.7-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:913983ad2deb14e66d83c28b632fd35ba2b825031f2fa4ca29675e665dfecbe1", size = 1463963 }, + { url = "https://files.pythonhosted.org/packages/6e/5c/0c03c4e542720c6177d4f408e56d1c8315899db72d46261a4e15b8b33a41/kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5337ec7809bcd0f424c6b705ecf97941c46279cf5ed92311782c7c9c2026f07f", size = 2248220 }, + { url = "https://files.pythonhosted.org/packages/3d/ee/55ef86d5a574f4e767df7da3a3a7ff4954c996e12d4fbe9c408170cd7dcc/kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4c26ed10c4f6fa6ddb329a5120ba3b6db349ca192ae211e882970bfc9d91420b", size = 2404463 }, + { url = "https://files.pythonhosted.org/packages/0f/6d/73ad36170b4bff4825dc588acf4f3e6319cb97cd1fb3eb04d9faa6b6f212/kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c619b101e6de2222c1fcb0531e1b17bbffbe54294bfba43ea0d411d428618c27", size = 2352842 }, + { url = "https://files.pythonhosted.org/packages/0b/16/fa531ff9199d3b6473bb4d0f47416cdb08d556c03b8bc1cccf04e756b56d/kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:073a36c8273647592ea332e816e75ef8da5c303236ec0167196793eb1e34657a", size = 2501635 }, + { url = "https://files.pythonhosted.org/packages/78/7e/aa9422e78419db0cbe75fb86d8e72b433818f2e62e2e394992d23d23a583/kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3ce6b2b0231bda412463e152fc18335ba32faf4e8c23a754ad50ffa70e4091ee", size = 2314556 }, + { url = "https://files.pythonhosted.org/packages/a8/b2/15f7f556df0a6e5b3772a1e076a9d9f6c538ce5f05bd590eca8106508e06/kiwisolver-1.4.7-cp313-cp313-win32.whl", hash = "sha256:f4c9aee212bc89d4e13f58be11a56cc8036cabad119259d12ace14b34476fd07", size = 46364 }, + { url = "https://files.pythonhosted.org/packages/0b/db/32e897e43a330eee8e4770bfd2737a9584b23e33587a0812b8e20aac38f7/kiwisolver-1.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:8a3ec5aa8e38fc4c8af308917ce12c536f1c88452ce554027e55b22cbbfbff76", size = 55887 }, + { url = "https://files.pythonhosted.org/packages/c8/a4/df2bdca5270ca85fd25253049eb6708d4127be2ed0e5c2650217450b59e9/kiwisolver-1.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:76c8094ac20ec259471ac53e774623eb62e6e1f56cd8690c67ce6ce4fcb05650", size = 48530 }, +] + +[[package]] +name = "lxml" +version = "5.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/6b/20c3a4b24751377aaa6307eb230b66701024012c29dd374999cc92983269/lxml-5.3.0.tar.gz", hash = "sha256:4e109ca30d1edec1ac60cdbe341905dc3b8f55b16855e03a54aaf59e51ec8c6f", size = 3679318 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/6a/42141e4d373903bfea6f8e94b2f554d05506dfda522ada5343c651410dc8/lxml-5.3.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8c72e9563347c7395910de6a3100a4840a75a6f60e05af5e58566868d5eb2d6a", size = 8156284 }, + { url = "https://files.pythonhosted.org/packages/91/5e/fa097f0f7d8b3d113fb7312c6308af702f2667f22644441715be961f2c7e/lxml-5.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e92ce66cd919d18d14b3856906a61d3f6b6a8500e0794142338da644260595cd", size = 4432407 }, + { url = "https://files.pythonhosted.org/packages/2d/a1/b901988aa6d4ff937f2e5cfc114e4ec561901ff00660c3e56713642728da/lxml-5.3.0-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d04f064bebdfef9240478f7a779e8c5dc32b8b7b0b2fc6a62e39b928d428e51", size = 5048331 }, + { url = "https://files.pythonhosted.org/packages/30/0f/b2a54f48e52de578b71bbe2a2f8160672a8a5e103df3a78da53907e8c7ed/lxml-5.3.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c2fb570d7823c2bbaf8b419ba6e5662137f8166e364a8b2b91051a1fb40ab8b", size = 4744835 }, + { url = "https://files.pythonhosted.org/packages/82/9d/b000c15538b60934589e83826ecbc437a1586488d7c13f8ee5ff1f79a9b8/lxml-5.3.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0c120f43553ec759f8de1fee2f4794452b0946773299d44c36bfe18e83caf002", size = 5316649 }, + { url = "https://files.pythonhosted.org/packages/e3/ee/ffbb9eaff5e541922611d2c56b175c45893d1c0b8b11e5a497708a6a3b3b/lxml-5.3.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:562e7494778a69086f0312ec9689f6b6ac1c6b65670ed7d0267e49f57ffa08c4", size = 4812046 }, + { url = "https://files.pythonhosted.org/packages/15/ff/7ff89d567485c7b943cdac316087f16b2399a8b997007ed352a1248397e5/lxml-5.3.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:423b121f7e6fa514ba0c7918e56955a1d4470ed35faa03e3d9f0e3baa4c7e492", size = 4918597 }, + { url = "https://files.pythonhosted.org/packages/c6/a3/535b6ed8c048412ff51268bdf4bf1cf052a37aa7e31d2e6518038a883b29/lxml-5.3.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:c00f323cc00576df6165cc9d21a4c21285fa6b9989c5c39830c3903dc4303ef3", size = 4738071 }, + { url = "https://files.pythonhosted.org/packages/7a/8f/cbbfa59cb4d4fd677fe183725a76d8c956495d7a3c7f111ab8f5e13d2e83/lxml-5.3.0-cp313-cp313-manylinux_2_28_ppc64le.whl", hash = "sha256:1fdc9fae8dd4c763e8a31e7630afef517eab9f5d5d31a278df087f307bf601f4", size = 5342213 }, + { url = "https://files.pythonhosted.org/packages/5c/fb/db4c10dd9958d4b52e34d1d1f7c1f434422aeaf6ae2bbaaff2264351d944/lxml-5.3.0-cp313-cp313-manylinux_2_28_s390x.whl", hash = "sha256:658f2aa69d31e09699705949b5fc4719cbecbd4a97f9656a232e7d6c7be1a367", size = 4893749 }, + { url = "https://files.pythonhosted.org/packages/f2/38/bb4581c143957c47740de18a3281a0cab7722390a77cc6e610e8ebf2d736/lxml-5.3.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:1473427aff3d66a3fa2199004c3e601e6c4500ab86696edffdbc84954c72d832", size = 4945901 }, + { url = "https://files.pythonhosted.org/packages/fc/d5/18b7de4960c731e98037bd48fa9f8e6e8f2558e6fbca4303d9b14d21ef3b/lxml-5.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a87de7dd873bf9a792bf1e58b1c3887b9264036629a5bf2d2e6579fe8e73edff", size = 4815447 }, + { url = "https://files.pythonhosted.org/packages/97/a8/cd51ceaad6eb849246559a8ef60ae55065a3df550fc5fcd27014361c1bab/lxml-5.3.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:0d7b36afa46c97875303a94e8f3ad932bf78bace9e18e603f2085b652422edcd", size = 5411186 }, + { url = "https://files.pythonhosted.org/packages/89/c3/1e3dabab519481ed7b1fdcba21dcfb8832f57000733ef0e71cf6d09a5e03/lxml-5.3.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:cf120cce539453ae086eacc0130a324e7026113510efa83ab42ef3fcfccac7fb", size = 5324481 }, + { url = "https://files.pythonhosted.org/packages/b6/17/71e9984cf0570cd202ac0a1c9ed5c1b8889b0fc8dc736f5ef0ffb181c284/lxml-5.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:df5c7333167b9674aa8ae1d4008fa4bc17a313cc490b2cca27838bbdcc6bb15b", size = 5011053 }, + { url = "https://files.pythonhosted.org/packages/69/68/9f7e6d3312a91e30829368c2b3217e750adef12a6f8eb10498249f4e8d72/lxml-5.3.0-cp313-cp313-win32.whl", hash = "sha256:c802e1c2ed9f0c06a65bc4ed0189d000ada8049312cfeab6ca635e39c9608957", size = 3485634 }, + { url = "https://files.pythonhosted.org/packages/7d/db/214290d58ad68c587bd5d6af3d34e56830438733d0d0856c0275fde43652/lxml-5.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:406246b96d552e0503e17a1006fd27edac678b3fcc9f1be71a2f94b4ff61528d", size = 3814417 }, +] + +[[package]] +name = "markdown-it-py" +version = "3.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mdurl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 }, +] + +[[package]] +name = "markupsafe" +version = "3.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274 }, + { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352 }, + { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122 }, + { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085 }, + { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978 }, + { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208 }, + { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357 }, + { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344 }, + { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101 }, + { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603 }, + { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510 }, + { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486 }, + { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480 }, + { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914 }, + { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796 }, + { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473 }, + { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114 }, + { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098 }, + { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208 }, + { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739 }, +] + +[[package]] +name = "matplotlib" +version = "3.9.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "contourpy" }, + { name = "cycler" }, + { name = "fonttools" }, + { name = "kiwisolver" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pillow" }, + { name = "pyparsing" }, + { name = "python-dateutil" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/75/9f/562ed484b11ac9f4bb4f9d2d7546954ec106a8c0f06cc755d6f63e519274/matplotlib-3.9.3.tar.gz", hash = "sha256:cd5dbbc8e25cad5f706845c4d100e2c8b34691b412b93717ce38d8ae803bcfa5", size = 36113438 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/60/04/949640040982822416c471d9ebe4e9e6c69ca9f9bb6ba82ed30808863c02/matplotlib-3.9.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:203d18df84f5288973b2d56de63d4678cc748250026ca9e1ad8f8a0fd8a75d83", size = 7883417 }, + { url = "https://files.pythonhosted.org/packages/9f/90/ebd37143cd3150b6c650ee1580024df3dd649d176e68d346f826b8d24e37/matplotlib-3.9.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b651b0d3642991259109dc0351fc33ad44c624801367bb8307be9bfc35e427ad", size = 7768720 }, + { url = "https://files.pythonhosted.org/packages/dc/84/6591e6b55d755d16dacdc113205067031867c1f5e3c08b32c01aad831420/matplotlib-3.9.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:66d7b171fecf96940ce069923a08ba3df33ef542de82c2ff4fe8caa8346fa95a", size = 8192723 }, + { url = "https://files.pythonhosted.org/packages/29/09/146a17d37e32313507f11ac984e65311f2d5805d731eb981d4f70eb928dc/matplotlib-3.9.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6be0ba61f6ff2e6b68e4270fb63b6813c9e7dec3d15fc3a93f47480444fd72f0", size = 8305801 }, + { url = "https://files.pythonhosted.org/packages/85/cb/d2690572c08f19ca7c0f44b1fb4d11c121d63467a57b508cc3656ff80b43/matplotlib-3.9.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9d6b2e8856dec3a6db1ae51aec85c82223e834b228c1d3228aede87eee2b34f9", size = 9086564 }, + { url = "https://files.pythonhosted.org/packages/28/dd/0a5176027c1cb94fe75f69f76cb274180c8abf740df6fc0e6a1e4cbaec3f/matplotlib-3.9.3-cp313-cp313-win_amd64.whl", hash = "sha256:90a85a004fefed9e583597478420bf904bb1a065b0b0ee5b9d8d31b04b0f3f70", size = 7833257 }, + { url = "https://files.pythonhosted.org/packages/42/d4/e477d50a8e4b437c2afbb5c665cb8e5d79b06abe6fe3c6915d6f7f0c2ef2/matplotlib-3.9.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3119b2f16de7f7b9212ba76d8fe6a0e9f90b27a1e04683cd89833a991682f639", size = 7911906 }, + { url = "https://files.pythonhosted.org/packages/ae/a1/ba5ab89666c42ace8e31b4ff5a2c76a17e4d6f91aefce476b064c56ff61d/matplotlib-3.9.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:87ad73763d93add1b6c1f9fcd33af662fd62ed70e620c52fcb79f3ac427cf3a6", size = 7801336 }, + { url = "https://files.pythonhosted.org/packages/77/59/4dcdb3a6695af6c698a95aec13016a550ef2f85144d22f61f81d1e064148/matplotlib-3.9.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:026bdf3137ab6022c866efa4813b6bbeddc2ed4c9e7e02f0e323a7bca380dfa0", size = 8218178 }, + { url = "https://files.pythonhosted.org/packages/4f/27/7c72db0d0ee35d9237572565ffa3c0eb25fc46a3f47e0f16412a587bc9d8/matplotlib-3.9.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:760a5e89ebbb172989e8273024a1024b0f084510b9105261b3b00c15e9c9f006", size = 8327768 }, + { url = "https://files.pythonhosted.org/packages/de/ad/213eee624feadba7b77e881c9d2c04c1e036efe69d19031e3fa927fdb5dc/matplotlib-3.9.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a42b9dc42de2cfe357efa27d9c50c7833fc5ab9b2eb7252ccd5d5f836a84e1e4", size = 9094075 }, + { url = "https://files.pythonhosted.org/packages/19/1b/cb8e99a5fe2e2b14e3b8234cb1649a675be63f74a5224a648ae4ab61f60c/matplotlib-3.9.3-cp313-cp313t-win_amd64.whl", hash = "sha256:e0fcb7da73fbf67b5f4bdaa57d85bb585a4e913d4a10f3e15b32baea56a67f0a", size = 7888937 }, +] + +[[package]] +name = "mdit-py-plugins" +version = "0.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/03/a2ecab526543b152300717cf232bb4bb8605b6edb946c845016fa9c9c9fd/mdit_py_plugins-0.4.2.tar.gz", hash = "sha256:5f2cd1fdb606ddf152d37ec30e46101a60512bc0e5fa1a7002c36647b09e26b5", size = 43542 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/f7/7782a043553ee469c1ff49cfa1cdace2d6bf99a1f333cf38676b3ddf30da/mdit_py_plugins-0.4.2-py3-none-any.whl", hash = "sha256:0c673c3f889399a33b95e88d2f0d111b4447bdfea7f237dab2d488f459835636", size = 55316 }, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 }, +] + +[[package]] +name = "ml-dtypes" +version = "0.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", marker = "python_full_version >= '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ab/79/717c5e22ad25d63ce3acdfe8ff8d64bdedec18914256c59b838218708b16/ml_dtypes-0.5.0.tar.gz", hash = "sha256:3e7d3a380fe73a63c884f06136f8baa7a5249cc8e9fdec677997dd78549f8128", size = 699367 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/4a/18f670a2703e771a6775fbc354208e597ff062a88efb0cecc220a282210b/ml_dtypes-0.5.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d3b3db9990c3840986a0e70524e122cfa32b91139c3653df76121ba7776e015f", size = 753345 }, + { url = "https://files.pythonhosted.org/packages/ed/c6/358d85e274e22d53def0c85f3cbe0933475fa3cf6922e9dca66eb25cb22f/ml_dtypes-0.5.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e04fde367b2fe901b1d47234426fe8819909bd1dd862a5adb630f27789c20599", size = 4424962 }, + { url = "https://files.pythonhosted.org/packages/4c/b4/d766586e24e7a073333c8eb8bd9275f3c6fe0569b509ae7b1699d4f00c74/ml_dtypes-0.5.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54415257f00eb44fbcc807454efac3356f75644f1cbfc2d4e5522a72ae1dacab", size = 4475201 }, + { url = "https://files.pythonhosted.org/packages/14/87/30323ad2e52f56262019a4493fe5f5e71067c5561ce7e2f9c75de520f5e8/ml_dtypes-0.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:cb5cc7b25acabd384f75bbd78892d0c724943f3e2e1986254665a1aa10982e07", size = 213195 }, +] + +[[package]] +name = "mpmath" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198 }, +] + +[[package]] +name = "myst-parser" +version = "4.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "docutils" }, + { name = "jinja2" }, + { name = "markdown-it-py" }, + { name = "mdit-py-plugins" }, + { name = "pyyaml" }, + { name = "sphinx" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/85/55/6d1741a1780e5e65038b74bce6689da15f620261c490c3511eb4c12bac4b/myst_parser-4.0.0.tar.gz", hash = "sha256:851c9dfb44e36e56d15d05e72f02b80da21a9e0d07cba96baf5e2d476bb91531", size = 93858 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ca/b4/b036f8fdb667587bb37df29dc6644681dd78b7a2a6321a34684b79412b28/myst_parser-4.0.0-py3-none-any.whl", hash = "sha256:b9317997552424448c6096c2558872fdb6f81d3ecb3a40ce84a7518798f3f28d", size = 84563 }, +] + +[[package]] +name = "namex" +version = "0.0.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9d/48/d275cdb6216c6bb4f9351675795a0b48974e138f16b1ffe0252c1f8faa28/namex-0.0.8.tar.gz", hash = "sha256:32a50f6c565c0bb10aa76298c959507abdc0e850efe085dc38f3440fcb3aa90b", size = 6623 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/73/59/7854fbfb59f8ae35483ce93493708be5942ebb6328cd85b3a609df629736/namex-0.0.8-py3-none-any.whl", hash = "sha256:7ddb6c2bb0e753a311b7590f84f6da659dd0c05e65cb89d519d54c0a250c0487", size = 5806 }, +] + +[[package]] +name = "natsort" +version = "8.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e2/a9/a0c57aee75f77794adaf35322f8b6404cbd0f89ad45c87197a937764b7d0/natsort-8.4.0.tar.gz", hash = "sha256:45312c4a0e5507593da193dedd04abb1469253b601ecaf63445ad80f0a1ea581", size = 76575 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/82/7a9d0550484a62c6da82858ee9419f3dd1ccc9aa1c26a1e43da3ecd20b0d/natsort-8.4.0-py3-none-any.whl", hash = "sha256:4732914fb471f56b5cce04d7bae6f164a592c7712e1c85f9ef585e197299521c", size = 38268 }, +] + +[[package]] +name = "numpy" +version = "2.1.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/25/ca/1166b75c21abd1da445b97bf1fa2f14f423c6cfb4fc7c4ef31dccf9f6a94/numpy-2.1.3.tar.gz", hash = "sha256:aa08e04e08aaf974d4458def539dece0d28146d866a39da5639596f4921fd761", size = 20166090 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4d/0b/620591441457e25f3404c8057eb924d04f161244cb8a3680d529419aa86e/numpy-2.1.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96fe52fcdb9345b7cd82ecd34547fca4321f7656d500eca497eb7ea5a926692f", size = 20836263 }, + { url = "https://files.pythonhosted.org/packages/45/e1/210b2d8b31ce9119145433e6ea78046e30771de3fe353f313b2778142f34/numpy-2.1.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f653490b33e9c3a4c1c01d41bc2aef08f9475af51146e4a7710c450cf9761598", size = 13507771 }, + { url = "https://files.pythonhosted.org/packages/55/44/aa9ee3caee02fa5a45f2c3b95cafe59c44e4b278fbbf895a93e88b308555/numpy-2.1.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:dc258a761a16daa791081d026f0ed4399b582712e6fc887a95af09df10c5ca57", size = 5075805 }, + { url = "https://files.pythonhosted.org/packages/78/d6/61de6e7e31915ba4d87bbe1ae859e83e6582ea14c6add07c8f7eefd8488f/numpy-2.1.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:016d0f6f5e77b0f0d45d77387ffa4bb89816b57c835580c3ce8e099ef830befe", size = 6608380 }, + { url = "https://files.pythonhosted.org/packages/3e/46/48bdf9b7241e317e6cf94276fe11ba673c06d1fdf115d8b4ebf616affd1a/numpy-2.1.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c181ba05ce8299c7aa3125c27b9c2167bca4a4445b7ce73d5febc411ca692e43", size = 13602451 }, + { url = "https://files.pythonhosted.org/packages/70/50/73f9a5aa0810cdccda9c1d20be3cbe4a4d6ea6bfd6931464a44c95eef731/numpy-2.1.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5641516794ca9e5f8a4d17bb45446998c6554704d888f86df9b200e66bdcce56", size = 16039822 }, + { url = "https://files.pythonhosted.org/packages/ad/cd/098bc1d5a5bc5307cfc65ee9369d0ca658ed88fbd7307b0d49fab6ca5fa5/numpy-2.1.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ea4dedd6e394a9c180b33c2c872b92f7ce0f8e7ad93e9585312b0c5a04777a4a", size = 16411822 }, + { url = "https://files.pythonhosted.org/packages/83/a2/7d4467a2a6d984549053b37945620209e702cf96a8bc658bc04bba13c9e2/numpy-2.1.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b0df3635b9c8ef48bd3be5f862cf71b0a4716fa0e702155c45067c6b711ddcef", size = 14079598 }, + { url = "https://files.pythonhosted.org/packages/e9/6a/d64514dcecb2ee70bfdfad10c42b76cab657e7ee31944ff7a600f141d9e9/numpy-2.1.3-cp313-cp313-win32.whl", hash = "sha256:50ca6aba6e163363f132b5c101ba078b8cbd3fa92c7865fd7d4d62d9779ac29f", size = 6236021 }, + { url = "https://files.pythonhosted.org/packages/bb/f9/12297ed8d8301a401e7d8eb6b418d32547f1d700ed3c038d325a605421a4/numpy-2.1.3-cp313-cp313-win_amd64.whl", hash = "sha256:747641635d3d44bcb380d950679462fae44f54b131be347d5ec2bce47d3df9ed", size = 12560405 }, + { url = "https://files.pythonhosted.org/packages/a7/45/7f9244cd792e163b334e3a7f02dff1239d2890b6f37ebf9e82cbe17debc0/numpy-2.1.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:996bb9399059c5b82f76b53ff8bb686069c05acc94656bb259b1d63d04a9506f", size = 20859062 }, + { url = "https://files.pythonhosted.org/packages/b1/b4/a084218e7e92b506d634105b13e27a3a6645312b93e1c699cc9025adb0e1/numpy-2.1.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:45966d859916ad02b779706bb43b954281db43e185015df6eb3323120188f9e4", size = 13515839 }, + { url = "https://files.pythonhosted.org/packages/27/45/58ed3f88028dcf80e6ea580311dc3edefdd94248f5770deb980500ef85dd/numpy-2.1.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:baed7e8d7481bfe0874b566850cb0b85243e982388b7b23348c6db2ee2b2ae8e", size = 5116031 }, + { url = "https://files.pythonhosted.org/packages/37/a8/eb689432eb977d83229094b58b0f53249d2209742f7de529c49d61a124a0/numpy-2.1.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:a9f7f672a3388133335589cfca93ed468509cb7b93ba3105fce780d04a6576a0", size = 6629977 }, + { url = "https://files.pythonhosted.org/packages/42/a3/5355ad51ac73c23334c7caaed01adadfda49544f646fcbfbb4331deb267b/numpy-2.1.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7aac50327da5d208db2eec22eb11e491e3fe13d22653dce51b0f4109101b408", size = 13575951 }, + { url = "https://files.pythonhosted.org/packages/c4/70/ea9646d203104e647988cb7d7279f135257a6b7e3354ea6c56f8bafdb095/numpy-2.1.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4394bc0dbd074b7f9b52024832d16e019decebf86caf909d94f6b3f77a8ee3b6", size = 16022655 }, + { url = "https://files.pythonhosted.org/packages/14/ce/7fc0612903e91ff9d0b3f2eda4e18ef9904814afcae5b0f08edb7f637883/numpy-2.1.3-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:50d18c4358a0a8a53f12a8ba9d772ab2d460321e6a93d6064fc22443d189853f", size = 16399902 }, + { url = "https://files.pythonhosted.org/packages/ef/62/1d3204313357591c913c32132a28f09a26357e33ea3c4e2fe81269e0dca1/numpy-2.1.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:14e253bd43fc6b37af4921b10f6add6925878a42a0c5fe83daee390bca80bc17", size = 14067180 }, + { url = "https://files.pythonhosted.org/packages/24/d7/78a40ed1d80e23a774cb8a34ae8a9493ba1b4271dde96e56ccdbab1620ef/numpy-2.1.3-cp313-cp313t-win32.whl", hash = "sha256:08788d27a5fd867a663f6fc753fd7c3ad7e92747efc73c53bca2f19f8bc06f48", size = 6291907 }, + { url = "https://files.pythonhosted.org/packages/86/09/a5ab407bd7f5f5599e6a9261f964ace03a73e7c6928de906981c31c38082/numpy-2.1.3-cp313-cp313t-win_amd64.whl", hash = "sha256:2564fbdf2b99b3f815f2107c1bbc93e2de8ee655a69c261363a1172a79a257d4", size = 12644098 }, +] + +[[package]] +name = "nvidia-nccl-cu12" +version = "2.23.4" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/3a/0112397396dec37ffc8edd7836d48261b4d14ca60ec8ed7bc857cce1d916/nvidia_nccl_cu12-2.23.4-py3-none-manylinux2014_aarch64.whl", hash = "sha256:aa946c8327e22ced28e7cef508a334673abc42064ec85f02d005ba1785ea4cec", size = 198953892 }, + { url = "https://files.pythonhosted.org/packages/ed/1f/6482380ec8dcec4894e7503490fc536d846b0d59694acad9cf99f27d0e7d/nvidia_nccl_cu12-2.23.4-py3-none-manylinux2014_x86_64.whl", hash = "sha256:b097258d9aab2fa9f686e33c6fe40ae57b27df60cedbd15d139701bb5509e0c1", size = 198954603 }, +] + +[[package]] +name = "oauthlib" +version = "3.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6d/fa/fbf4001037904031639e6bfbfc02badfc7e12f137a8afa254df6c4c8a670/oauthlib-3.2.2.tar.gz", hash = "sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918", size = 177352 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/80/cab10959dc1faead58dc8384a781dfbf93cb4d33d50988f7a69f1b7c9bbe/oauthlib-3.2.2-py3-none-any.whl", hash = "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca", size = 151688 }, +] + +[[package]] +name = "opencv-python" +version = "4.10.0.84" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", marker = "python_full_version >= '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4a/e7/b70a2d9ab205110d715906fc8ec83fbb00404aeb3a37a0654fdb68eb0c8c/opencv-python-4.10.0.84.tar.gz", hash = "sha256:72d234e4582e9658ffea8e9cae5b63d488ad06994ef12d81dc303b17472f3526", size = 95103981 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/66/82/564168a349148298aca281e342551404ef5521f33fba17b388ead0a84dc5/opencv_python-4.10.0.84-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:fc182f8f4cda51b45f01c64e4cbedfc2f00aff799debebc305d8d0210c43f251", size = 54835524 }, + { url = "https://files.pythonhosted.org/packages/64/4a/016cda9ad7cf18c58ba074628a4eaae8aa55f3fd06a266398cef8831a5b9/opencv_python-4.10.0.84-cp37-abi3-macosx_12_0_x86_64.whl", hash = "sha256:71e575744f1d23f79741450254660442785f45a0797212852ee5199ef12eed98", size = 56475426 }, + { url = "https://files.pythonhosted.org/packages/81/e4/7a987ebecfe5ceaf32db413b67ff18eb3092c598408862fff4d7cc3fd19b/opencv_python-4.10.0.84-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09a332b50488e2dda866a6c5573ee192fe3583239fb26ff2f7f9ceb0bc119ea6", size = 41746971 }, + { url = "https://files.pythonhosted.org/packages/3f/a4/d2537f47fd7fcfba966bd806e3ec18e7ee1681056d4b0a9c8d983983e4d5/opencv_python-4.10.0.84-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ace140fc6d647fbe1c692bcb2abce768973491222c067c131d80957c595b71f", size = 62548253 }, + { url = "https://files.pythonhosted.org/packages/1e/39/bbf57e7b9dab623e8773f6ff36385456b7ae7fa9357a5e53db732c347eac/opencv_python-4.10.0.84-cp37-abi3-win32.whl", hash = "sha256:2db02bb7e50b703f0a2d50c50ced72e95c574e1e5a0bb35a8a86d0b35c98c236", size = 28737688 }, + { url = "https://files.pythonhosted.org/packages/ec/6c/fab8113424af5049f85717e8e527ca3773299a3c6b02506e66436e19874f/opencv_python-4.10.0.84-cp37-abi3-win_amd64.whl", hash = "sha256:32dbbd94c26f611dc5cc6979e6b7aa1f55a64d6b463cc1dcd3c95505a63e48fe", size = 38842521 }, +] + +[[package]] +name = "optree" +version = "0.13.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f7/f2/56afdaeaae36b076659be7db8e72be0924dd64ebd1c131675c77f7e704a6/optree-0.13.1.tar.gz", hash = "sha256:af67856aa8073d237fe67313d84f8aeafac32c1cef7239c628a2768d02679c43", size = 155738 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/53/f3727cad24f16a06666f328f1212476988cadac9b9e7919ddfb2c22eb662/optree-0.13.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f788b2ad120deb73b4908a74473cd6de79cfb9f33bbe9dcb59cea2e2477d4e28", size = 608270 }, + { url = "https://files.pythonhosted.org/packages/64/f2/68beb9da2dd52baa50e7a589ed2bd8434fdd70cdba06754aa5910263da06/optree-0.13.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2909cb42add6bb1a5a2b0243bdd8c4b861bf072f3741e26239481907ac8ad4e6", size = 325703 }, + { url = "https://files.pythonhosted.org/packages/45/db/08921e56f3425bf649eb593eb28775263c935d029985d35572dc5690cc1a/optree-0.13.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbc5fa2ff5090389f3a906567446f01d692bd6fe5cfcc5ae2d5861f24e8e0e4d", size = 355813 }, + { url = "https://files.pythonhosted.org/packages/e5/e3/587e0d28dc2cee064902adfebca97db124e12b275dbe9c2b05a70a22345f/optree-0.13.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4711f5cac5a2a49c3d6c9f0eca7b77c22b452170bb33ea01c3214ebb17931db9", size = 402566 }, + { url = "https://files.pythonhosted.org/packages/8a/1d/0d5bbab8c99580b732b89ef2c5fcdd6ef410478295949fdf2984fa1bfc28/optree-0.13.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6c4ab1d391b89cb88eb3c63383d5eb0930bc21141de9d5acd277feed9e38eb65", size = 397005 }, + { url = "https://files.pythonhosted.org/packages/16/fa/fc2a8183e14f0d195d25824bf65095ff32b34bd469614a6c30d0a596a30f/optree-0.13.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b5e5f09c85ae558a6bdaea57e63168082e728e777391393e9e2792f0d15b7b59", size = 369400 }, + { url = "https://files.pythonhosted.org/packages/9f/42/8c08ce4ebb3d9a6e4415f1a97830c84879e2d1a43710a7c8a18b2c3e169d/optree-0.13.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c8ee1e988c634a451146b87d9ebdbf650a75dc1f52a9cffcd89fabb7289321c", size = 390179 }, + { url = "https://files.pythonhosted.org/packages/06/02/3a701d6307fdfefe4fcecbac644803e2a4314ab2406ff465e03129cc85f6/optree-0.13.1-cp313-cp313-win32.whl", hash = "sha256:5b6531cd4eb23fadbbf77faf834e1119da06d7af3154f55786b59953cd87bb8a", size = 264264 }, + { url = "https://files.pythonhosted.org/packages/ef/f9/8a1421181c5eb0c0f81d1423a900baeb3faba68a48747bbdffb7581239ac/optree-0.13.1-cp313-cp313-win_amd64.whl", hash = "sha256:27d81dc43b522ba47ba7d2e7d91dbb486940348b1bf85caeb0afc2815c0aa492", size = 293682 }, + { url = "https://files.pythonhosted.org/packages/80/34/d1b1849a6240385c4a3af5da9425b11912204d0b1cf142d802815319b73a/optree-0.13.1-cp313-cp313-win_arm64.whl", hash = "sha256:f39c7174a3f3cdc3f5fe6fb4b832f608c40ac174d7567ed6734b2ee952094631", size = 293670 }, + { url = "https://files.pythonhosted.org/packages/0d/d6/f81e6748bcc3f35a2f570a814014e3418b0ed425d7cbc2b42d88d12863d5/optree-0.13.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:3010ae24e994f6e00071098d34e98e78eb995b7454a2ef629a0bf7df17441b24", size = 702861 }, + { url = "https://files.pythonhosted.org/packages/08/7f/70a2d02110ccb245bc57bd9ad57668acfea0ff364c27d7dfe1735ede79ed/optree-0.13.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5b5626c38d4a18a144063db5c1dbb558431d83ca10682324f74665a12214801f", size = 370740 }, + { url = "https://files.pythonhosted.org/packages/63/37/4ddf05267467809236203e2007e9443519c4d55e0744ce7eea1aa74dffee/optree-0.13.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1935639dd498a42367633e3877797e1330e39d44d48bbca1a136bb4dbe4c1bc9", size = 374695 }, + { url = "https://files.pythonhosted.org/packages/19/f2/51a63a799f6dce31813d7e02a7547394aebcb39f407e62038ecbd999d490/optree-0.13.1-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01819c3df950696f32c91faf8d376ae6b695ffdba18f330f1cab6b8e314e4612", size = 418671 }, + { url = "https://files.pythonhosted.org/packages/f0/7c/a08191e0c9202f2be9c415057eea3cf3a5af18e9a6d81f4c7b0e6faf0a1f/optree-0.13.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:48c29d9c6c64c8dc48c8ee97f7c1d5cdb83e37320f0be0857c06ce4b97994aea", size = 414966 }, + { url = "https://files.pythonhosted.org/packages/8f/37/7bf815f4da7234e387863228b17246b42b8c02553882581a4013a64a88d0/optree-0.13.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:025d23400b8b579462a251420f0a9ae77d3d3593f84276f3465985731d79d722", size = 389219 }, + { url = "https://files.pythonhosted.org/packages/3d/84/bb521a66d3a84fe2f1500ef67d245c2cc1a26277fcaaf4bc70b22c06e99b/optree-0.13.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:55e82426bef151149cfa41d68ac957730fcd420996c0db8324fca81aa6a810ba", size = 405377 }, + { url = "https://files.pythonhosted.org/packages/06/99/3eb53829c4c0b6dc20115d957d2d8e945630ddf40c656dc4e39c5a6e51f2/optree-0.13.1-cp313-cp313t-win32.whl", hash = "sha256:e40f018f522fcfd244688d1b3a360518e636ba7f636385aae0566eae3e7d29bc", size = 292734 }, + { url = "https://files.pythonhosted.org/packages/2f/59/d7601959ad0b90d309794c0975a256304488b4c5671f24e3e12101ade7ef/optree-0.13.1-cp313-cp313t-win_amd64.whl", hash = "sha256:d580f1bf23bb352c4db6b3544f282f1ac08dcb0d9ab537d25e56220353438cf7", size = 331457 }, + { url = "https://files.pythonhosted.org/packages/8b/36/c01a5bc34660d46c6a3b1fe090bbdc8c76af7b5c1a6613cc671aa6df8349/optree-0.13.1-cp313-cp313t-win_arm64.whl", hash = "sha256:c4d13f55dbd509d27be3af54d53b4ca0751bc518244ced6d0567e518e51452a2", size = 331470 }, +] + +[[package]] +name = "packaging" +version = "24.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 }, +] + +[[package]] +name = "pandas" +version = "2.2.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", marker = "python_full_version >= '3.13'" }, + { name = "python-dateutil" }, + { name = "pytz" }, + { name = "tzdata" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9c/d6/9f8431bacc2e19dca897724cd097b1bb224a6ad5433784a44b587c7c13af/pandas-2.2.3.tar.gz", hash = "sha256:4f18ba62b61d7e192368b84517265a99b4d7ee8912f8708660fb4a366cc82667", size = 4399213 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/64/22/3b8f4e0ed70644e85cfdcd57454686b9057c6c38d2f74fe4b8bc2527214a/pandas-2.2.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f00d1345d84d8c86a63e476bb4955e46458b304b9575dcf71102b5c705320015", size = 12477643 }, + { url = "https://files.pythonhosted.org/packages/e4/93/b3f5d1838500e22c8d793625da672f3eec046b1a99257666c94446969282/pandas-2.2.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3508d914817e153ad359d7e069d752cdd736a247c322d932eb89e6bc84217f28", size = 11281573 }, + { url = "https://files.pythonhosted.org/packages/f5/94/6c79b07f0e5aab1dcfa35a75f4817f5c4f677931d4234afcd75f0e6a66ca/pandas-2.2.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22a9d949bfc9a502d320aa04e5d02feab689d61da4e7764b62c30b991c42c5f0", size = 15196085 }, + { url = "https://files.pythonhosted.org/packages/e8/31/aa8da88ca0eadbabd0a639788a6da13bb2ff6edbbb9f29aa786450a30a91/pandas-2.2.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3a255b2c19987fbbe62a9dfd6cff7ff2aa9ccab3fc75218fd4b7530f01efa24", size = 12711809 }, + { url = "https://files.pythonhosted.org/packages/ee/7c/c6dbdb0cb2a4344cacfb8de1c5808ca885b2e4dcfde8008266608f9372af/pandas-2.2.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:800250ecdadb6d9c78eae4990da62743b857b470883fa27f652db8bdde7f6659", size = 16356316 }, + { url = "https://files.pythonhosted.org/packages/57/b7/8b757e7d92023b832869fa8881a992696a0bfe2e26f72c9ae9f255988d42/pandas-2.2.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6374c452ff3ec675a8f46fd9ab25c4ad0ba590b71cf0656f8b6daa5202bca3fb", size = 14022055 }, + { url = "https://files.pythonhosted.org/packages/3b/bc/4b18e2b8c002572c5a441a64826252ce5da2aa738855747247a971988043/pandas-2.2.3-cp313-cp313-win_amd64.whl", hash = "sha256:61c5ad4043f791b61dd4752191d9f07f0ae412515d59ba8f005832a532f8736d", size = 11481175 }, + { url = "https://files.pythonhosted.org/packages/76/a3/a5d88146815e972d40d19247b2c162e88213ef51c7c25993942c39dbf41d/pandas-2.2.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3b71f27954685ee685317063bf13c7709a7ba74fc996b84fc6821c59b0f06468", size = 12615650 }, + { url = "https://files.pythonhosted.org/packages/9c/8c/f0fd18f6140ddafc0c24122c8a964e48294acc579d47def376fef12bcb4a/pandas-2.2.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:38cf8125c40dae9d5acc10fa66af8ea6fdf760b2714ee482ca691fc66e6fcb18", size = 11290177 }, + { url = "https://files.pythonhosted.org/packages/ed/f9/e995754eab9c0f14c6777401f7eece0943840b7a9fc932221c19d1abee9f/pandas-2.2.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ba96630bc17c875161df3818780af30e43be9b166ce51c9a18c1feae342906c2", size = 14651526 }, + { url = "https://files.pythonhosted.org/packages/25/b0/98d6ae2e1abac4f35230aa756005e8654649d305df9a28b16b9ae4353bff/pandas-2.2.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1db71525a1538b30142094edb9adc10be3f3e176748cd7acc2240c2f2e5aa3a4", size = 11871013 }, + { url = "https://files.pythonhosted.org/packages/cc/57/0f72a10f9db6a4628744c8e8f0df4e6e21de01212c7c981d31e50ffc8328/pandas-2.2.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:15c0e1e02e93116177d29ff83e8b1619c93ddc9c49083f237d4312337a61165d", size = 15711620 }, + { url = "https://files.pythonhosted.org/packages/ab/5f/b38085618b950b79d2d9164a711c52b10aefc0ae6833b96f626b7021b2ed/pandas-2.2.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ad5b65698ab28ed8d7f18790a0dc58005c7629f227be9ecc1072aa74c0c1d43a", size = 13098436 }, +] + +[[package]] +name = "patsy" +version = "1.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d1/81/74f6a65b848ffd16c18f920620ce999fe45fe27f01ab3911260ce4ed85e4/patsy-1.0.1.tar.gz", hash = "sha256:e786a9391eec818c054e359b737bbce692f051aee4c661f4141cc88fb459c0c4", size = 396010 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/87/2b/b50d3d08ea0fc419c183a84210571eba005328efa62b6b98bc28e9ead32a/patsy-1.0.1-py2.py3-none-any.whl", hash = "sha256:751fb38f9e97e62312e921a1954b81e1bb2bcda4f5eeabaf94db251ee791509c", size = 232923 }, +] + +[[package]] +name = "pillow" +version = "11.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a5/26/0d95c04c868f6bdb0c447e3ee2de5564411845e36a858cfd63766bc7b563/pillow-11.0.0.tar.gz", hash = "sha256:72bacbaf24ac003fea9bff9837d1eedb6088758d41e100c1552930151f677739", size = 46737780 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/63/24/e2e15e392d00fcf4215907465d8ec2a2f23bcec1481a8ebe4ae760459995/pillow-11.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:bcd1fb5bb7b07f64c15618c89efcc2cfa3e95f0e3bcdbaf4642509de1942a699", size = 3147300 }, + { url = "https://files.pythonhosted.org/packages/43/72/92ad4afaa2afc233dc44184adff289c2e77e8cd916b3ddb72ac69495bda3/pillow-11.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0e038b0745997c7dcaae350d35859c9715c71e92ffb7e0f4a8e8a16732150f38", size = 2978742 }, + { url = "https://files.pythonhosted.org/packages/9e/da/c8d69c5bc85d72a8523fe862f05ababdc52c0a755cfe3d362656bb86552b/pillow-11.0.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ae08bd8ffc41aebf578c2af2f9d8749d91f448b3bfd41d7d9ff573d74f2a6b2", size = 4194349 }, + { url = "https://files.pythonhosted.org/packages/cd/e8/686d0caeed6b998351d57796496a70185376ed9c8ec7d99e1d19ad591fc6/pillow-11.0.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d69bfd8ec3219ae71bcde1f942b728903cad25fafe3100ba2258b973bd2bc1b2", size = 4298714 }, + { url = "https://files.pythonhosted.org/packages/ec/da/430015cec620d622f06854be67fd2f6721f52fc17fca8ac34b32e2d60739/pillow-11.0.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:61b887f9ddba63ddf62fd02a3ba7add935d053b6dd7d58998c630e6dbade8527", size = 4208514 }, + { url = "https://files.pythonhosted.org/packages/44/ae/7e4f6662a9b1cb5f92b9cc9cab8321c381ffbee309210940e57432a4063a/pillow-11.0.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:c6a660307ca9d4867caa8d9ca2c2658ab685de83792d1876274991adec7b93fa", size = 4380055 }, + { url = "https://files.pythonhosted.org/packages/74/d5/1a807779ac8a0eeed57f2b92a3c32ea1b696e6140c15bd42eaf908a261cd/pillow-11.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:73e3a0200cdda995c7e43dd47436c1548f87a30bb27fb871f352a22ab8dcf45f", size = 4296751 }, + { url = "https://files.pythonhosted.org/packages/38/8c/5fa3385163ee7080bc13026d59656267daaaaf3c728c233d530e2c2757c8/pillow-11.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fba162b8872d30fea8c52b258a542c5dfd7b235fb5cb352240c8d63b414013eb", size = 4430378 }, + { url = "https://files.pythonhosted.org/packages/ca/1d/ad9c14811133977ff87035bf426875b93097fb50af747793f013979facdb/pillow-11.0.0-cp313-cp313-win32.whl", hash = "sha256:f1b82c27e89fffc6da125d5eb0ca6e68017faf5efc078128cfaa42cf5cb38798", size = 2249588 }, + { url = "https://files.pythonhosted.org/packages/fb/01/3755ba287dac715e6afdb333cb1f6d69740a7475220b4637b5ce3d78cec2/pillow-11.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:8ba470552b48e5835f1d23ecb936bb7f71d206f9dfeee64245f30c3270b994de", size = 2567509 }, + { url = "https://files.pythonhosted.org/packages/c0/98/2c7d727079b6be1aba82d195767d35fcc2d32204c7a5820f822df5330152/pillow-11.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:846e193e103b41e984ac921b335df59195356ce3f71dcfd155aa79c603873b84", size = 2254791 }, + { url = "https://files.pythonhosted.org/packages/eb/38/998b04cc6f474e78b563716b20eecf42a2fa16a84589d23c8898e64b0ffd/pillow-11.0.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4ad70c4214f67d7466bea6a08061eba35c01b1b89eaa098040a35272a8efb22b", size = 3150854 }, + { url = "https://files.pythonhosted.org/packages/13/8e/be23a96292113c6cb26b2aa3c8b3681ec62b44ed5c2bd0b258bd59503d3c/pillow-11.0.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:6ec0d5af64f2e3d64a165f490d96368bb5dea8b8f9ad04487f9ab60dc4bb6003", size = 2982369 }, + { url = "https://files.pythonhosted.org/packages/97/8a/3db4eaabb7a2ae8203cd3a332a005e4aba00067fc514aaaf3e9721be31f1/pillow-11.0.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c809a70e43c7977c4a42aefd62f0131823ebf7dd73556fa5d5950f5b354087e2", size = 4333703 }, + { url = "https://files.pythonhosted.org/packages/28/ac/629ffc84ff67b9228fe87a97272ab125bbd4dc462745f35f192d37b822f1/pillow-11.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:4b60c9520f7207aaf2e1d94de026682fc227806c6e1f55bba7606d1c94dd623a", size = 4412550 }, + { url = "https://files.pythonhosted.org/packages/d6/07/a505921d36bb2df6868806eaf56ef58699c16c388e378b0dcdb6e5b2fb36/pillow-11.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:1e2688958a840c822279fda0086fec1fdab2f95bf2b717b66871c4ad9859d7e8", size = 4461038 }, + { url = "https://files.pythonhosted.org/packages/d6/b9/fb620dd47fc7cc9678af8f8bd8c772034ca4977237049287e99dda360b66/pillow-11.0.0-cp313-cp313t-win32.whl", hash = "sha256:607bbe123c74e272e381a8d1957083a9463401f7bd01287f50521ecb05a313f8", size = 2253197 }, + { url = "https://files.pythonhosted.org/packages/df/86/25dde85c06c89d7fc5db17940f07aae0a56ac69aa9ccb5eb0f09798862a8/pillow-11.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5c39ed17edea3bc69c743a8dd3e9853b7509625c2462532e62baa0732163a904", size = 2572169 }, + { url = "https://files.pythonhosted.org/packages/51/85/9c33f2517add612e17f3381aee7c4072779130c634921a756c97bc29fb49/pillow-11.0.0-cp313-cp313t-win_arm64.whl", hash = "sha256:75acbbeb05b86bc53cbe7b7e6fe00fbcf82ad7c684b3ad82e3d711da9ba287d3", size = 2256828 }, +] + +[[package]] +name = "pluggy" +version = "1.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 }, +] + +[[package]] +name = "pygments" +version = "2.18.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/62/8336eff65bcbc8e4cb5d05b55faf041285951b6e80f33e2bff2024788f31/pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199", size = 4891905 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f7/3f/01c8b82017c199075f8f788d0d906b9ffbbc5a47dc9918a945e13d5a2bda/pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a", size = 1205513 }, +] + +[[package]] +name = "pyparsing" +version = "3.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/d5/e5aeee5387091148a19e1145f63606619cb5f20b83fccb63efae6474e7b2/pyparsing-3.2.0.tar.gz", hash = "sha256:cbf74e27246d595d9a74b186b810f6fbb86726dbf3b9532efb343f6d7294fe9c", size = 920984 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/be/ec/2eb3cd785efd67806c46c13a17339708ddc346cbb684eade7a6e6f79536a/pyparsing-3.2.0-py3-none-any.whl", hash = "sha256:93d9577b88da0bbea8cc8334ee8b918ed014968fd2ec383e868fb8afb1ccef84", size = 106921 }, +] + +[[package]] +name = "pytest" +version = "8.3.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/05/35/30e0d83068951d90a01852cb1cef56e5d8a09d20c7f511634cc2f7e0372a/pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761", size = 1445919 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/11/92/76a1c94d3afee238333bc0a42b82935dd8f9cf8ce9e336ff87ee14d9e1cf/pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6", size = 343083 }, +] + +[[package]] +name = "pytest-cov" +version = "6.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "coverage" }, + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/be/45/9b538de8cef30e17c7b45ef42f538a94889ed6a16f2387a6c89e73220651/pytest-cov-6.0.0.tar.gz", hash = "sha256:fde0b595ca248bb8e2d76f020b465f3b107c9632e6a1d1705f17834c89dcadc0", size = 66945 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/36/3b/48e79f2cd6a61dbbd4807b4ed46cb564b4fd50a76166b1c4ea5c1d9e2371/pytest_cov-6.0.0-py3-none-any.whl", hash = "sha256:eee6f1b9e61008bd34975a4d5bab25801eb31898b032dd55addc93e96fcaaa35", size = 22949 }, +] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 }, +] + +[[package]] +name = "pytz" +version = "2024.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3a/31/3c70bf7603cc2dca0f19bdc53b4537a797747a58875b552c8c413d963a3f/pytz-2024.2.tar.gz", hash = "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a", size = 319692 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/11/c3/005fcca25ce078d2cc29fd559379817424e94885510568bc1bc53d7d5846/pytz-2024.2-py2.py3-none-any.whl", hash = "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725", size = 508002 }, +] + +[[package]] +name = "pyyaml" +version = "6.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309 }, + { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679 }, + { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428 }, + { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361 }, + { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523 }, + { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660 }, + { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597 }, + { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527 }, + { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446 }, +] + +[[package]] +name = "requests" +version = "2.32.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 }, +] + +[[package]] +name = "requests-oauthlib" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "oauthlib" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/95/52/531ef197b426646f26b53815a7d2a67cb7a331ef098bb276db26a68ac49f/requests-oauthlib-1.3.1.tar.gz", hash = "sha256:75beac4a47881eeb94d5ea5d6ad31ef88856affe2332b9aafb52c6452ccf0d7a", size = 52027 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6f/bb/5deac77a9af870143c684ab46a7934038a53eb4aa975bc0687ed6ca2c610/requests_oauthlib-1.3.1-py2.py3-none-any.whl", hash = "sha256:2577c501a2fb8d05a304c09d090d6e47c306fef15809d102b327cf8364bddab5", size = 23892 }, +] + +[[package]] +name = "rich" +version = "13.9.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ab/3a/0316b28d0761c6734d6bc14e770d85506c986c85ffb239e688eeaab2c2bc/rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098", size = 223149 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/19/71/39c7c0d87f8d4e6c020a393182060eaefeeae6c01dab6a84ec346f2567df/rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90", size = 242424 }, +] + +[[package]] +name = "ruff" +version = "0.8.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/95/d0/8ff5b189d125f4260f2255d143bf2fa413b69c2610c405ace7a0a8ec81ec/ruff-0.8.1.tar.gz", hash = "sha256:3583db9a6450364ed5ca3f3b4225958b24f78178908d5c4bc0f46251ccca898f", size = 3313222 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a2/d6/1a6314e568db88acdbb5121ed53e2c52cebf3720d3437a76f82f923bf171/ruff-0.8.1-py3-none-linux_armv6l.whl", hash = "sha256:fae0805bd514066f20309f6742f6ee7904a773eb9e6c17c45d6b1600ca65c9b5", size = 10532605 }, + { url = "https://files.pythonhosted.org/packages/89/a8/a957a8812e31facffb6a26a30be0b5b4af000a6e30c7d43a22a5232a3398/ruff-0.8.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b8a4f7385c2285c30f34b200ca5511fcc865f17578383db154e098150ce0a087", size = 10278243 }, + { url = "https://files.pythonhosted.org/packages/a8/23/9db40fa19c453fabf94f7a35c61c58f20e8200b4734a20839515a19da790/ruff-0.8.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:cd054486da0c53e41e0086e1730eb77d1f698154f910e0cd9e0d64274979a209", size = 9917739 }, + { url = "https://files.pythonhosted.org/packages/e2/a0/6ee2d949835d5701d832fc5acd05c0bfdad5e89cfdd074a171411f5ccad5/ruff-0.8.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2029b8c22da147c50ae577e621a5bfbc5d1fed75d86af53643d7a7aee1d23871", size = 10779153 }, + { url = "https://files.pythonhosted.org/packages/7a/25/9c11dca9404ef1eb24833f780146236131a3c7941de394bc356912ef1041/ruff-0.8.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2666520828dee7dfc7e47ee4ea0d928f40de72056d929a7c5292d95071d881d1", size = 10304387 }, + { url = "https://files.pythonhosted.org/packages/c8/b9/84c323780db1b06feae603a707d82dbbd85955c8c917738571c65d7d5aff/ruff-0.8.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:333c57013ef8c97a53892aa56042831c372e0bb1785ab7026187b7abd0135ad5", size = 11360351 }, + { url = "https://files.pythonhosted.org/packages/6b/e1/9d4bbb2ace7aad14ded20e4674a48cda5b902aed7a1b14e6b028067060c4/ruff-0.8.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:288326162804f34088ac007139488dcb43de590a5ccfec3166396530b58fb89d", size = 12022879 }, + { url = "https://files.pythonhosted.org/packages/75/28/752ff6120c0e7f9981bc4bc275d540c7f36db1379ba9db9142f69c88db21/ruff-0.8.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b12c39b9448632284561cbf4191aa1b005882acbc81900ffa9f9f471c8ff7e26", size = 11610354 }, + { url = "https://files.pythonhosted.org/packages/ba/8c/967b61c2cc8ebd1df877607fbe462bc1e1220b4a30ae3352648aec8c24bd/ruff-0.8.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:364e6674450cbac8e998f7b30639040c99d81dfb5bbc6dfad69bc7a8f916b3d1", size = 12813976 }, + { url = "https://files.pythonhosted.org/packages/7f/29/e059f945d6bd2d90213387b8c360187f2fefc989ddcee6bbf3c241329b92/ruff-0.8.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b22346f845fec132aa39cd29acb94451d030c10874408dbf776af3aaeb53284c", size = 11154564 }, + { url = "https://files.pythonhosted.org/packages/55/47/cbd05e5a62f3fb4c072bc65c1e8fd709924cad1c7ec60a1000d1e4ee8307/ruff-0.8.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b2f2f7a7e7648a2bfe6ead4e0a16745db956da0e3a231ad443d2a66a105c04fa", size = 10760604 }, + { url = "https://files.pythonhosted.org/packages/bb/ee/4c3981c47147c72647a198a94202633130cfda0fc95cd863a553b6f65c6a/ruff-0.8.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:adf314fc458374c25c5c4a4a9270c3e8a6a807b1bec018cfa2813d6546215540", size = 10391071 }, + { url = "https://files.pythonhosted.org/packages/6b/e6/083eb61300214590b188616a8ac6ae1ef5730a0974240fb4bec9c17de78b/ruff-0.8.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:a885d68342a231b5ba4d30b8c6e1b1ee3a65cf37e3d29b3c74069cdf1ee1e3c9", size = 10896657 }, + { url = "https://files.pythonhosted.org/packages/77/bd/aacdb8285d10f1b943dbeb818968efca35459afc29f66ae3bd4596fbf954/ruff-0.8.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:d2c16e3508c8cc73e96aa5127d0df8913d2290098f776416a4b157657bee44c5", size = 11228362 }, + { url = "https://files.pythonhosted.org/packages/39/72/fcb7ad41947f38b4eaa702aca0a361af0e9c2bf671d7fd964480670c297e/ruff-0.8.1-py3-none-win32.whl", hash = "sha256:93335cd7c0eaedb44882d75a7acb7df4b77cd7cd0d2255c93b28791716e81790", size = 8803476 }, + { url = "https://files.pythonhosted.org/packages/e4/ea/cae9aeb0f4822c44651c8407baacdb2e5b4dcd7b31a84e1c5df33aa2cc20/ruff-0.8.1-py3-none-win_amd64.whl", hash = "sha256:2954cdbe8dfd8ab359d4a30cd971b589d335a44d444b6ca2cb3d1da21b75e4b6", size = 9614463 }, + { url = "https://files.pythonhosted.org/packages/eb/76/fbb4bd23dfb48fa7758d35b744413b650a9fd2ddd93bca77e30376864414/ruff-0.8.1-py3-none-win_arm64.whl", hash = "sha256:55873cc1a473e5ac129d15eccb3c008c096b94809d693fc7053f588b67822737", size = 8959621 }, +] + +[[package]] +name = "scikit-learn" +version = "1.5.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "joblib" }, + { name = "numpy" }, + { name = "scipy" }, + { name = "threadpoolctl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/37/59/44985a2bdc95c74e34fef3d10cb5d93ce13b0e2a7baefffe1b53853b502d/scikit_learn-1.5.2.tar.gz", hash = "sha256:b4237ed7b3fdd0a4882792e68ef2545d5baa50aca3bb45aa7df468138ad8f94d", size = 7001680 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/50/8891028437858cc510e13578fe7046574a60c2aaaa92b02d64aac5b1b412/scikit_learn-1.5.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e9a702e2de732bbb20d3bad29ebd77fc05a6b427dc49964300340e4c9328b3f5", size = 12025584 }, + { url = "https://files.pythonhosted.org/packages/d2/79/17feef8a1c14149436083bec0e61d7befb4812e272d5b20f9d79ea3e9ab1/scikit_learn-1.5.2-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:b0768ad641981f5d3a198430a1d31c3e044ed2e8a6f22166b4d546a5116d7908", size = 10959795 }, + { url = "https://files.pythonhosted.org/packages/b1/c8/f08313f9e2e656bd0905930ae8bf99a573ea21c34666a813b749c338202f/scikit_learn-1.5.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:178ddd0a5cb0044464fc1bfc4cca5b1833bfc7bb022d70b05db8530da4bb3dd3", size = 12077302 }, + { url = "https://files.pythonhosted.org/packages/a7/48/fbfb4dc72bed0fe31fe045fb30e924909ad03f717c36694351612973b1a9/scikit_learn-1.5.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7284ade780084d94505632241bf78c44ab3b6f1e8ccab3d2af58e0e950f9c12", size = 13002811 }, + { url = "https://files.pythonhosted.org/packages/a5/e7/0c869f9e60d225a77af90d2aefa7a4a4c0e745b149325d1450f0f0ce5399/scikit_learn-1.5.2-cp313-cp313-win_amd64.whl", hash = "sha256:b7b0f9a0b1040830d38c39b91b3a44e1b643f4b36e36567b80b7c6bd2202a27f", size = 10951354 }, +] + +[[package]] +name = "scipy" +version = "1.14.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/62/11/4d44a1f274e002784e4dbdb81e0ea96d2de2d1045b2132d5af62cc31fd28/scipy-1.14.1.tar.gz", hash = "sha256:5a275584e726026a5699459aa72f828a610821006228e841b94275c4a7c08417", size = 58620554 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/50/ef/ac98346db016ff18a6ad7626a35808f37074d25796fd0234c2bb0ed1e054/scipy-1.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1729560c906963fc8389f6aac023739ff3983e727b1a4d87696b7bf108316a79", size = 39091068 }, + { url = "https://files.pythonhosted.org/packages/b9/cc/70948fe9f393b911b4251e96b55bbdeaa8cca41f37c26fd1df0232933b9e/scipy-1.14.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:4079b90df244709e675cdc8b93bfd8a395d59af40b72e339c2287c91860deb8e", size = 29875417 }, + { url = "https://files.pythonhosted.org/packages/3b/2e/35f549b7d231c1c9f9639f9ef49b815d816bf54dd050da5da1c11517a218/scipy-1.14.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:e0cf28db0f24a38b2a0ca33a85a54852586e43cf6fd876365c86e0657cfe7d73", size = 23084508 }, + { url = "https://files.pythonhosted.org/packages/3f/d6/b028e3f3e59fae61fb8c0f450db732c43dd1d836223a589a8be9f6377203/scipy-1.14.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:0c2f95de3b04e26f5f3ad5bb05e74ba7f68b837133a4492414b3afd79dfe540e", size = 25503364 }, + { url = "https://files.pythonhosted.org/packages/a7/2f/6c142b352ac15967744d62b165537a965e95d557085db4beab2a11f7943b/scipy-1.14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b99722ea48b7ea25e8e015e8341ae74624f72e5f21fc2abd45f3a93266de4c5d", size = 35292639 }, + { url = "https://files.pythonhosted.org/packages/56/46/2449e6e51e0d7c3575f289f6acb7f828938eaab8874dbccfeb0cd2b71a27/scipy-1.14.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5149e3fd2d686e42144a093b206aef01932a0059c2a33ddfa67f5f035bdfe13e", size = 40798288 }, + { url = "https://files.pythonhosted.org/packages/32/cd/9d86f7ed7f4497c9fd3e39f8918dd93d9f647ba80d7e34e4946c0c2d1a7c/scipy-1.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e4f5a7c49323533f9103d4dacf4e4f07078f360743dec7f7596949149efeec06", size = 42524647 }, + { url = "https://files.pythonhosted.org/packages/f5/1b/6ee032251bf4cdb0cc50059374e86a9f076308c1512b61c4e003e241efb7/scipy-1.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:baff393942b550823bfce952bb62270ee17504d02a1801d7fd0719534dfb9c84", size = 44469524 }, +] + +[[package]] +name = "six" +version = "1.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/71/39/171f1c67cd00715f190ba0b100d606d440a28c93c7714febeca8b79af85e/six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", size = 34041 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d9/5a/e7c31adbe875f2abbb91bd84cf2dc52d792b5a01506781dbcf25c91daf11/six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254", size = 11053 }, +] + +[[package]] +name = "snowballstemmer" +version = "2.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/44/7b/af302bebf22c749c56c9c3e8ae13190b5b5db37a33d9068652e8f73b7089/snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1", size = 86699 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ed/dc/c02e01294f7265e63a7315fe086dd1df7dacb9f840a804da846b96d01b96/snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a", size = 93002 }, +] + +[[package]] +name = "soupsieve" +version = "2.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/ce/fbaeed4f9fb8b2daa961f90591662df6a86c1abf25c548329a86920aedfb/soupsieve-2.6.tar.gz", hash = "sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb", size = 101569 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/c2/fe97d779f3ef3b15f05c94a2f1e3d21732574ed441687474db9d342a7315/soupsieve-2.6-py3-none-any.whl", hash = "sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9", size = 36186 }, +] + +[[package]] +name = "sphinx" +version = "8.1.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "alabaster" }, + { name = "babel" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "docutils" }, + { name = "imagesize" }, + { name = "jinja2" }, + { name = "packaging" }, + { name = "pygments" }, + { name = "requests" }, + { name = "snowballstemmer" }, + { name = "sphinxcontrib-applehelp" }, + { name = "sphinxcontrib-devhelp" }, + { name = "sphinxcontrib-htmlhelp" }, + { name = "sphinxcontrib-jsmath" }, + { name = "sphinxcontrib-qthelp" }, + { name = "sphinxcontrib-serializinghtml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/be0b61178fe2cdcb67e2a92fc9ebb488e3c51c4f74a36a7824c0adf23425/sphinx-8.1.3.tar.gz", hash = "sha256:43c1911eecb0d3e161ad78611bc905d1ad0e523e4ddc202a58a821773dc4c927", size = 8184611 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/26/60/1ddff83a56d33aaf6f10ec8ce84b4c007d9368b21008876fceda7e7381ef/sphinx-8.1.3-py3-none-any.whl", hash = "sha256:09719015511837b76bf6e03e42eb7595ac8c2e41eeb9c29c5b755c6b677992a2", size = 3487125 }, +] + +[[package]] +name = "sphinx-autoapi" +version = "3.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "astroid", marker = "python_full_version >= '3.13'" }, + { name = "jinja2" }, + { name = "pyyaml" }, + { name = "sphinx" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4a/eb/cc243583bb1d518ca3b10998c203d919a8ed90affd4831f2b61ad09043d2/sphinx_autoapi-3.4.0.tar.gz", hash = "sha256:e6d5371f9411bbb9fca358c00a9e57aef3ac94cbfc5df4bab285946462f69e0c", size = 29292 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/d6/f2acdc2567337fd5f5dc091a4e58d8a0fb14927b9779fc1e5ecee96d9824/sphinx_autoapi-3.4.0-py3-none-any.whl", hash = "sha256:4027fef2875a22c5f2a57107c71641d82f6166bf55beb407a47aaf3ef14e7b92", size = 34095 }, +] + +[[package]] +name = "sphinx-pyproject" +version = "0.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "dom-toml" }, + { name = "domdf-python-tools" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/39/97/aa8cec3da3e78f2c396b63332e2fe92fe43f7ff2ad19b3998735f28b0a7f/sphinx_pyproject-0.3.0.tar.gz", hash = "sha256:efc4ee9d96f579c4e4ed1ac273868c64565e88c8e37fe6ec2dc59fbcd57684ab", size = 7695 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/90/d5/89cb47c6399fd57ca451af15361499813c5d53e588cb6e00d89411ce724f/sphinx_pyproject-0.3.0-py3-none-any.whl", hash = "sha256:3aca968919f5ecd390f96874c3f64a43c9c7fcfdc2fd4191a781ad9228501b52", size = 23076 }, +] + +[[package]] +name = "sphinxcontrib-applehelp" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/6e/b837e84a1a704953c62ef8776d45c3e8d759876b4a84fe14eba2859106fe/sphinxcontrib_applehelp-2.0.0.tar.gz", hash = "sha256:2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1", size = 20053 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5d/85/9ebeae2f76e9e77b952f4b274c27238156eae7979c5421fba91a28f4970d/sphinxcontrib_applehelp-2.0.0-py3-none-any.whl", hash = "sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5", size = 119300 }, +] + +[[package]] +name = "sphinxcontrib-devhelp" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/d2/5beee64d3e4e747f316bae86b55943f51e82bb86ecd325883ef65741e7da/sphinxcontrib_devhelp-2.0.0.tar.gz", hash = "sha256:411f5d96d445d1d73bb5d52133377b4248ec79db5c793ce7dbe59e074b4dd1ad", size = 12967 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/35/7a/987e583882f985fe4d7323774889ec58049171828b58c2217e7f79cdf44e/sphinxcontrib_devhelp-2.0.0-py3-none-any.whl", hash = "sha256:aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2", size = 82530 }, +] + +[[package]] +name = "sphinxcontrib-htmlhelp" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/93/983afd9aa001e5201eab16b5a444ed5b9b0a7a010541e0ddfbbfd0b2470c/sphinxcontrib_htmlhelp-2.1.0.tar.gz", hash = "sha256:c9e2916ace8aad64cc13a0d233ee22317f2b9025b9cf3295249fa985cc7082e9", size = 22617 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0a/7b/18a8c0bcec9182c05a0b3ec2a776bba4ead82750a55ff798e8d406dae604/sphinxcontrib_htmlhelp-2.1.0-py3-none-any.whl", hash = "sha256:166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8", size = 98705 }, +] + +[[package]] +name = "sphinxcontrib-jsmath" +version = "1.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/e8/9ed3830aeed71f17c026a07a5097edcf44b692850ef215b161b8ad875729/sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8", size = 5787 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c2/42/4c8646762ee83602e3fb3fbe774c2fac12f317deb0b5dbeeedd2d3ba4b77/sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178", size = 5071 }, +] + +[[package]] +name = "sphinxcontrib-qthelp" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/68/bc/9104308fc285eb3e0b31b67688235db556cd5b0ef31d96f30e45f2e51cae/sphinxcontrib_qthelp-2.0.0.tar.gz", hash = "sha256:4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab", size = 17165 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/27/83/859ecdd180cacc13b1f7e857abf8582a64552ea7a061057a6c716e790fce/sphinxcontrib_qthelp-2.0.0-py3-none-any.whl", hash = "sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb", size = 88743 }, +] + +[[package]] +name = "sphinxcontrib-serializinghtml" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3b/44/6716b257b0aa6bfd51a1b31665d1c205fb12cb5ad56de752dfa15657de2f/sphinxcontrib_serializinghtml-2.0.0.tar.gz", hash = "sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d", size = 16080 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/52/a7/d2782e4e3f77c8450f727ba74a8f12756d5ba823d81b941f1b04da9d033a/sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl", hash = "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331", size = 92072 }, +] + +[[package]] +name = "statsmodels" +version = "0.14.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "packaging" }, + { name = "pandas" }, + { name = "patsy" }, + { name = "scipy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1f/3b/963a015dd8ea17e10c7b0e2f14d7c4daec903baf60a017e756b57953a4bf/statsmodels-0.14.4.tar.gz", hash = "sha256:5d69e0f39060dc72c067f9bb6e8033b6dccdb0bae101d76a7ef0bcc94e898b67", size = 20354802 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/31/f8/2662e6a101315ad336f75168fa9bac71f913ebcb92a6be84031d84a0f21f/statsmodels-0.14.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b5a24f5d2c22852d807d2b42daf3a61740820b28d8381daaf59dcb7055bf1a79", size = 10186886 }, + { url = "https://files.pythonhosted.org/packages/fa/c0/ee6e8ed35fc1ca9c7538c592f4974547bf72274bc98db1ae4a6e87481a83/statsmodels-0.14.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:df4f7864606fa843d7e7c0e6af288f034a2160dba14e6ccc09020a3cf67cb092", size = 9880066 }, + { url = "https://files.pythonhosted.org/packages/d1/97/3380ca6d8fd66cfb3d12941e472642f26e781a311c355a4e97aab2ed0216/statsmodels-0.14.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91341cbde9e8bea5fb419a76e09114e221567d03f34ca26e6d67ae2c27d8fe3c", size = 10283521 }, + { url = "https://files.pythonhosted.org/packages/fe/2a/55c5b5c5e5124a202ea3fe0bcdbdeceaf91b4ec6164b8434acb9dd97409c/statsmodels-0.14.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1322286a7bfdde2790bf72d29698a1b76c20b8423a55bdcd0d457969d0041f72", size = 10723228 }, + { url = "https://files.pythonhosted.org/packages/4f/76/67747e49dc758daae06f33aad8247b718cd7d224f091d2cd552681215bb2/statsmodels-0.14.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e31b95ac603415887c9f0d344cb523889cf779bc52d68e27e2d23c358958fec7", size = 10859503 }, + { url = "https://files.pythonhosted.org/packages/1d/eb/cb8b01f5edf8f135eb3d0553d159db113a35b2948d0e51eeb735e7ae09ea/statsmodels-0.14.4-cp313-cp313-win_amd64.whl", hash = "sha256:81030108d27aecc7995cac05aa280cf8c6025f6a6119894eef648997936c2dd0", size = 9817574 }, +] + +[[package]] +name = "sympy" +version = "1.13.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mpmath" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/11/8a/5a7fd6284fa8caac23a26c9ddf9c30485a48169344b4bd3b0f02fef1890f/sympy-1.13.3.tar.gz", hash = "sha256:b27fd2c6530e0ab39e275fc9b683895367e51d5da91baa8d3d64db2565fec4d9", size = 7533196 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/99/ff/c87e0622b1dadea79d2fb0b25ade9ed98954c9033722eb707053d310d4f3/sympy-1.13.3-py3-none-any.whl", hash = "sha256:54612cf55a62755ee71824ce692986f23c88ffa77207b30c1368eda4a7060f73", size = 6189483 }, +] + +[[package]] +name = "thealgorithms-python" +version = "0.0.1" +source = { virtual = "." } +dependencies = [ + { name = "beautifulsoup4" }, + { name = "fake-useragent" }, + { name = "imageio" }, + { name = "keras" }, + { name = "lxml" }, + { name = "matplotlib" }, + { name = "numpy" }, + { name = "opencv-python" }, + { name = "pandas" }, + { name = "pillow" }, + { name = "requests" }, + { name = "rich" }, + { name = "scikit-learn" }, + { name = "sphinx-pyproject" }, + { name = "statsmodels" }, + { name = "sympy" }, + { name = "tweepy" }, + { name = "typing-extensions" }, + { name = "xgboost" }, +] + +[package.dev-dependencies] +dev = [ + { name = "pytest" }, + { name = "pytest-cov" }, +] +docs = [ + { name = "myst-parser" }, + { name = "sphinx-autoapi" }, + { name = "sphinx-pyproject" }, +] +euler-validate = [ + { name = "numpy" }, + { name = "pytest" }, + { name = "pytest-cov" }, + { name = "requests" }, +] +lint = [ + { name = "codespell" }, + { name = "ruff" }, +] + +[package.metadata] +requires-dist = [ + { name = "beautifulsoup4", specifier = ">=4.12.3" }, + { name = "fake-useragent", specifier = ">=1.5.1" }, + { name = "imageio", specifier = ">=2.36.1" }, + { name = "keras", specifier = ">=3.7" }, + { name = "lxml", specifier = ">=5.3" }, + { name = "matplotlib", specifier = ">=3.9.3" }, + { name = "numpy", specifier = ">=2.1.3" }, + { name = "opencv-python", specifier = ">=4.10.0.84" }, + { name = "pandas", specifier = ">=2.2.3" }, + { name = "pillow", specifier = ">=11" }, + { name = "requests", specifier = ">=2.32.3" }, + { name = "rich", specifier = ">=13.9.4" }, + { name = "scikit-learn", specifier = ">=1.5.2" }, + { name = "sphinx-pyproject", specifier = ">=0.3" }, + { name = "statsmodels", specifier = ">=0.14.4" }, + { name = "sympy", specifier = ">=1.13.3" }, + { name = "tweepy", specifier = ">=4.14" }, + { name = "typing-extensions", specifier = ">=4.12.2" }, + { name = "xgboost", specifier = ">=2.1.3" }, +] + +[package.metadata.requires-dev] +dev = [ + { name = "pytest", specifier = ">=8.3.4" }, + { name = "pytest-cov", specifier = ">=6" }, +] +docs = [ + { name = "myst-parser", specifier = ">=4.0.0" }, + { name = "sphinx-autoapi", specifier = ">=3.4.0" }, + { name = "sphinx-pyproject", specifier = ">=0.3.0" }, +] +euler-validate = [ + { name = "numpy", specifier = ">=2.1.3" }, + { name = "pytest", specifier = ">=8.3.4" }, + { name = "pytest-cov", specifier = ">=6.0.0" }, + { name = "requests", specifier = ">=2.32.3" }, +] +lint = [ + { name = "codespell", specifier = ">=2.3" }, + { name = "ruff", specifier = ">=0.8.1" }, +] + +[[package]] +name = "threadpoolctl" +version = "3.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bd/55/b5148dcbf72f5cde221f8bfe3b6a540da7aa1842f6b491ad979a6c8b84af/threadpoolctl-3.5.0.tar.gz", hash = "sha256:082433502dd922bf738de0d8bcc4fdcbf0979ff44c42bd40f5af8a282f6fa107", size = 41936 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4b/2c/ffbf7a134b9ab11a67b0cf0726453cedd9c5043a4fe7a35d1cefa9a1bcfb/threadpoolctl-3.5.0-py3-none-any.whl", hash = "sha256:56c1e26c150397e58c4926da8eeee87533b1e32bef131bd4bf6a2f45f3185467", size = 18414 }, +] + +[[package]] +name = "tomli" +version = "2.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", size = 17175 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/90/2ee5f2e0362cb8a0b6499dc44f4d7d48f8fff06d28ba46e6f1eaa61a1388/tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7", size = 132708 }, + { url = "https://files.pythonhosted.org/packages/c0/ec/46b4108816de6b385141f082ba99e315501ccd0a2ea23db4a100dd3990ea/tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c", size = 123582 }, + { url = "https://files.pythonhosted.org/packages/a0/bd/b470466d0137b37b68d24556c38a0cc819e8febe392d5b199dcd7f578365/tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13", size = 232543 }, + { url = "https://files.pythonhosted.org/packages/d9/e5/82e80ff3b751373f7cead2815bcbe2d51c895b3c990686741a8e56ec42ab/tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281", size = 241691 }, + { url = "https://files.pythonhosted.org/packages/05/7e/2a110bc2713557d6a1bfb06af23dd01e7dde52b6ee7dadc589868f9abfac/tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272", size = 251170 }, + { url = "https://files.pythonhosted.org/packages/64/7b/22d713946efe00e0adbcdfd6d1aa119ae03fd0b60ebed51ebb3fa9f5a2e5/tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140", size = 236530 }, + { url = "https://files.pythonhosted.org/packages/38/31/3a76f67da4b0cf37b742ca76beaf819dca0ebef26d78fc794a576e08accf/tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2", size = 258666 }, + { url = "https://files.pythonhosted.org/packages/07/10/5af1293da642aded87e8a988753945d0cf7e00a9452d3911dd3bb354c9e2/tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744", size = 243954 }, + { url = "https://files.pythonhosted.org/packages/5b/b9/1ed31d167be802da0fc95020d04cd27b7d7065cc6fbefdd2f9186f60d7bd/tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec", size = 98724 }, + { url = "https://files.pythonhosted.org/packages/c7/32/b0963458706accd9afcfeb867c0f9175a741bf7b19cd424230714d722198/tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69", size = 109383 }, + { url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257 }, +] + +[[package]] +name = "tweepy" +version = "4.14.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "oauthlib" }, + { name = "requests" }, + { name = "requests-oauthlib" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/75/1c/0db8c3cf9d31bf63853ff612d201060ae78e6db03468a70e063bef0eda62/tweepy-4.14.0.tar.gz", hash = "sha256:1f9f1707d6972de6cff6c5fd90dfe6a449cd2e0d70bd40043ffab01e07a06c8c", size = 88623 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4d/78/ba0065d5636bbf4a35b78c4f81b74e7858b609cdf69e629d6da5c91b9d92/tweepy-4.14.0-py3-none-any.whl", hash = "sha256:db6d3844ccc0c6d27f339f12ba8acc89912a961da513c1ae50fa2be502a56afb", size = 98520 }, +] + +[[package]] +name = "typing-extensions" +version = "4.12.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 }, +] + +[[package]] +name = "tzdata" +version = "2024.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e1/34/943888654477a574a86a98e9896bae89c7aa15078ec29f490fef2f1e5384/tzdata-2024.2.tar.gz", hash = "sha256:7d85cc416e9382e69095b7bdf4afd9e3880418a2413feec7069d533d6b4e31cc", size = 193282 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a6/ab/7e5f53c3b9d14972843a647d8d7a853969a58aecc7559cb3267302c94774/tzdata-2024.2-py2.py3-none-any.whl", hash = "sha256:a48093786cdcde33cad18c2555e8532f34422074448fbc874186f0abd79565cd", size = 346586 }, +] + +[[package]] +name = "urllib3" +version = "2.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ed/63/22ba4ebfe7430b76388e7cd448d5478814d3032121827c12a2cc287e2260/urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9", size = 300677 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/d9/5f4c13cecde62396b0d3fe530a50ccea91e7dfc1ccf0e09c228841bb5ba8/urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac", size = 126338 }, +] + +[[package]] +name = "xgboost" +version = "2.1.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "nvidia-nccl-cu12", marker = "platform_machine != 'aarch64' and platform_system == 'Linux'" }, + { name = "scipy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/48/b0/131ffc4a15fd3acee9be3a7baa6b2fa6faa479799c51b880de9fc3ddf550/xgboost-2.1.3.tar.gz", hash = "sha256:7699ec4226156887d3afc665c63ab87469db9d46e361c702ba9fccd22535730c", size = 1090326 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cd/c6/773ebd84414879bd0566788868ae46a6574f6efaf81e694f01ea1fed3277/xgboost-2.1.3-py3-none-macosx_10_15_x86_64.macosx_11_0_x86_64.macosx_12_0_x86_64.whl", hash = "sha256:c9b0c92f13e3650e1e1cf92ff9ecef3efc6f5dc3d10ce17858df2081a89976ef", size = 2139909 }, + { url = "https://files.pythonhosted.org/packages/28/3c/ddf5d9eb742cdb7fbcd5c854bce07471bad01194ac37de91db64fbef0c58/xgboost-2.1.3-py3-none-macosx_12_0_arm64.whl", hash = "sha256:fcbf1912a852bd07a7007be350c8dc3a484c5e775b612f2b3cd082fc76240eb3", size = 1938631 }, + { url = "https://files.pythonhosted.org/packages/4a/3a/8cd69a216993fd9d54ceb079d1b357b7ef50678b3c2695d8a71962b8d0aa/xgboost-2.1.3-py3-none-manylinux2014_aarch64.whl", hash = "sha256:27af88df1162cee016c67f267a0a16c3db1c48f256e12f64c45c8f8edf9571cd", size = 4441261 }, + { url = "https://files.pythonhosted.org/packages/48/bc/05d7db90d421c5e3d681a12fd1eb087e37bf2e9bbe2b105422d6319ecc92/xgboost-2.1.3-py3-none-manylinux2014_x86_64.whl", hash = "sha256:32a43526208fe676527c698cb852e0e9515e6d7294143780e476d335290a131b", size = 4532380 }, + { url = "https://files.pythonhosted.org/packages/0f/c8/f679a816c06a4a6d23da3f4b448d5f0615b51de2886ad3e3e695d17121b3/xgboost-2.1.3-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:5d33090880f3d474f8cf5dda557c7bf8dbceefb62f2fd655c77efcabb9cac222", size = 4207000 }, + { url = "https://files.pythonhosted.org/packages/32/93/66826e2f50cefecbb0a44bd1e667316bf0a3c8e78cd1f0cdf52f5b2c5c6f/xgboost-2.1.3-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:8d85d38553855a1f8c40b8fbccca86af19202f91b244e2c7f77afbb2a6d9d785", size = 153894508 }, + { url = "https://files.pythonhosted.org/packages/70/58/2f94976df39470fb00eec2cb4f914dde44cd0df8d96483208bf7db4bc97e/xgboost-2.1.3-py3-none-win_amd64.whl", hash = "sha256:25c0ffcbd62aac5bc22c79e08b5b2edad1d5e37f16610ebefa5f06f3e2ea3d96", size = 124909665 }, +] From 98391e33ea2a87375a7f744eba3d57918237b4e7 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 12 Dec 2024 18:40:43 +0100 Subject: [PATCH 693/757] [pre-commit.ci] pre-commit autoupdate (#12428) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.8.1 → v0.8.2](https://github.com/astral-sh/ruff-pre-commit/compare/v0.8.1...v0.8.2) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index bef251749c19..884b10661a49 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.8.1 + rev: v0.8.2 hooks: - id: ruff - id: ruff-format From f8e595e048f1cbd763e0a1f8c0ffb4dff335b841 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 16 Dec 2024 20:53:36 +0100 Subject: [PATCH 694/757] [pre-commit.ci] pre-commit autoupdate (#12439) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.8.2 → v0.8.3](https://github.com/astral-sh/ruff-pre-commit/compare/v0.8.2...v0.8.3) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 884b10661a49..0c8108ac55be 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.8.2 + rev: v0.8.3 hooks: - id: ruff - id: ruff-format From 4abfce2791c081f65580bc1fefdf5a4d8ee7b5fc Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Mon, 23 Dec 2024 06:55:22 +0300 Subject: [PATCH 695/757] Fix sphinx/build_docs warnings for audio_filters (#12449) * updating DIRECTORY.md * Fix sphinx/build_docs warnings for audio_filters * Improve * Fix * Fix * Fix --------- Co-authored-by: MaximSmolskiy --- audio_filters/iir_filter.py | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/audio_filters/iir_filter.py b/audio_filters/iir_filter.py index f3c1ad43b001..fa3e6c54b33f 100644 --- a/audio_filters/iir_filter.py +++ b/audio_filters/iir_filter.py @@ -10,13 +10,17 @@ class IIRFilter: Implementation details: Based on the 2nd-order function from - https://en.wikipedia.org/wiki/Digital_biquad_filter, + https://en.wikipedia.org/wiki/Digital_biquad_filter, this generalized N-order function was made. Using the following transfer function - H(z)=\frac{b_{0}+b_{1}z^{-1}+b_{2}z^{-2}+...+b_{k}z^{-k}}{a_{0}+a_{1}z^{-1}+a_{2}z^{-2}+...+a_{k}z^{-k}} + .. math:: H(z)=\frac{b_{0}+b_{1}z^{-1}+b_{2}z^{-2}+...+b_{k}z^{-k}} + {a_{0}+a_{1}z^{-1}+a_{2}z^{-2}+...+a_{k}z^{-k}} + we can rewrite this to - y[n]={\frac{1}{a_{0}}}\left(\left(b_{0}x[n]+b_{1}x[n-1]+b_{2}x[n-2]+...+b_{k}x[n-k]\right)-\left(a_{1}y[n-1]+a_{2}y[n-2]+...+a_{k}y[n-k]\right)\right) + .. math:: y[n]={\frac{1}{a_{0}}} + \left(\left(b_{0}x[n]+b_{1}x[n-1]+b_{2}x[n-2]+...+b_{k}x[n-k]\right)- + \left(a_{1}y[n-1]+a_{2}y[n-2]+...+a_{k}y[n-k]\right)\right) """ def __init__(self, order: int) -> None: @@ -34,17 +38,19 @@ def __init__(self, order: int) -> None: def set_coefficients(self, a_coeffs: list[float], b_coeffs: list[float]) -> None: """ - Set the coefficients for the IIR filter. These should both be of size order + 1. - a_0 may be left out, and it will use 1.0 as default value. + Set the coefficients for the IIR filter. + These should both be of size `order` + 1. + :math:`a_0` may be left out, and it will use 1.0 as default value. This method works well with scipy's filter design functions - >>> # Make a 2nd-order 1000Hz butterworth lowpass filter - >>> import scipy.signal - >>> b_coeffs, a_coeffs = scipy.signal.butter(2, 1000, - ... btype='lowpass', - ... fs=48000) - >>> filt = IIRFilter(2) - >>> filt.set_coefficients(a_coeffs, b_coeffs) + + >>> # Make a 2nd-order 1000Hz butterworth lowpass filter + >>> import scipy.signal + >>> b_coeffs, a_coeffs = scipy.signal.butter(2, 1000, + ... btype='lowpass', + ... fs=48000) + >>> filt = IIRFilter(2) + >>> filt.set_coefficients(a_coeffs, b_coeffs) """ if len(a_coeffs) < self.order: a_coeffs = [1.0, *a_coeffs] @@ -68,7 +74,7 @@ def set_coefficients(self, a_coeffs: list[float], b_coeffs: list[float]) -> None def process(self, sample: float) -> float: """ - Calculate y[n] + Calculate :math:`y[n]` >>> filt = IIRFilter(2) >>> filt.process(0) From 47cd21a110d8e2fc038414bc7f3c7ca8e91d6653 Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Mon, 23 Dec 2024 14:56:42 +0300 Subject: [PATCH 696/757] Fix sphinx/build_docs warnings for cellular_automata (#12454) * updating DIRECTORY.md * Fix sphinx/build_docs warnings for cellular_automata * Fix * Improve --------- Co-authored-by: MaximSmolskiy --- cellular_automata/wa_tor.py | 54 ++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 28 deletions(-) diff --git a/cellular_automata/wa_tor.py b/cellular_automata/wa_tor.py index e423d1595bdb..29f7ea510bfe 100644 --- a/cellular_automata/wa_tor.py +++ b/cellular_automata/wa_tor.py @@ -1,9 +1,9 @@ """ Wa-Tor algorithm (1984) -@ https://en.wikipedia.org/wiki/Wa-Tor -@ https://beltoforion.de/en/wator/ -@ https://beltoforion.de/en/wator/images/wator_medium.webm +| @ https://en.wikipedia.org/wiki/Wa-Tor +| @ https://beltoforion.de/en/wator/ +| @ https://beltoforion.de/en/wator/images/wator_medium.webm This solution aims to completely remove any systematic approach to the Wa-Tor planet, and utilise fully random methods. @@ -97,8 +97,8 @@ class WaTor: :attr time_passed: A function that is called every time time passes (a chronon) in order to visually display - the new Wa-Tor planet. The time_passed function can block - using time.sleep to slow the algorithm progression. + the new Wa-Tor planet. The `time_passed` function can block + using ``time.sleep`` to slow the algorithm progression. >>> wt = WaTor(10, 15) >>> wt.width @@ -216,7 +216,7 @@ def get_surrounding_prey(self, entity: Entity) -> list[Entity]: """ Returns all the prey entities around (N, S, E, W) a predator entity. - Subtly different to the try_to_move_to_unoccupied square. + Subtly different to the `move_and_reproduce`. >>> wt = WaTor(WIDTH, HEIGHT) >>> wt.set_planet([ @@ -260,7 +260,7 @@ def move_and_reproduce( """ Attempts to move to an unoccupied neighbouring square in either of the four directions (North, South, East, West). - If the move was successful and the remaining_reproduction time is + If the move was successful and the `remaining_reproduction_time` is equal to 0, then a new prey or predator can also be created in the previous square. @@ -351,12 +351,12 @@ def perform_prey_actions( Performs the actions for a prey entity For prey the rules are: - 1. At each chronon, a prey moves randomly to one of the adjacent unoccupied - squares. If there are no free squares, no movement takes place. - 2. Once a prey has survived a certain number of chronons it may reproduce. - This is done as it moves to a neighbouring square, - leaving behind a new prey in its old position. - Its reproduction time is also reset to zero. + 1. At each chronon, a prey moves randomly to one of the adjacent unoccupied + squares. If there are no free squares, no movement takes place. + 2. Once a prey has survived a certain number of chronons it may reproduce. + This is done as it moves to a neighbouring square, + leaving behind a new prey in its old position. + Its reproduction time is also reset to zero. >>> wt = WaTor(WIDTH, HEIGHT) >>> reproducable_entity = Entity(True, coords=(0, 1)) @@ -382,15 +382,15 @@ def perform_predator_actions( :param occupied_by_prey_coords: Move to this location if there is prey there For predators the rules are: - 1. At each chronon, a predator moves randomly to an adjacent square occupied - by a prey. If there is none, the predator moves to a random adjacent - unoccupied square. If there are no free squares, no movement takes place. - 2. At each chronon, each predator is deprived of a unit of energy. - 3. Upon reaching zero energy, a predator dies. - 4. If a predator moves to a square occupied by a prey, - it eats the prey and earns a certain amount of energy. - 5. Once a predator has survived a certain number of chronons - it may reproduce in exactly the same way as the prey. + 1. At each chronon, a predator moves randomly to an adjacent square occupied + by a prey. If there is none, the predator moves to a random adjacent + unoccupied square. If there are no free squares, no movement takes place. + 2. At each chronon, each predator is deprived of a unit of energy. + 3. Upon reaching zero energy, a predator dies. + 4. If a predator moves to a square occupied by a prey, + it eats the prey and earns a certain amount of energy. + 5. Once a predator has survived a certain number of chronons + it may reproduce in exactly the same way as the prey. >>> wt = WaTor(WIDTH, HEIGHT) >>> wt.set_planet([[Entity(True, coords=(0, 0)), Entity(False, coords=(0, 1))]]) @@ -430,7 +430,7 @@ def perform_predator_actions( def run(self, *, iteration_count: int) -> None: """ - Emulate time passing by looping iteration_count times + Emulate time passing by looping `iteration_count` times >>> wt = WaTor(WIDTH, HEIGHT) >>> wt.run(iteration_count=PREDATOR_INITIAL_ENERGY_VALUE - 1) @@ -484,11 +484,9 @@ def visualise(wt: WaTor, iter_number: int, *, colour: bool = True) -> None: an ascii code in terminal to clear and re-print the Wa-Tor planet at intervals. - Uses ascii colour codes to colourfully display - the predators and prey. - - (0x60f197) Prey = # - (0xfffff) Predator = x + Uses ascii colour codes to colourfully display the predators and prey: + * (0x60f197) Prey = ``#`` + * (0xfffff) Predator = ``x`` >>> wt = WaTor(30, 30) >>> wt.set_planet([ From c5e603ae4234e5d516d700b01d47f78d42c18008 Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Mon, 23 Dec 2024 15:43:16 +0300 Subject: [PATCH 697/757] Fix sphinx/build_docs warnings for geodesy (#12462) * updating DIRECTORY.md * Fix sphinx/build_docs warnings for geodesy/haversine_distance.py * Improve --------- Co-authored-by: MaximSmolskiy --- geodesy/haversine_distance.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/geodesy/haversine_distance.py b/geodesy/haversine_distance.py index 93e625770f9d..39cd250af965 100644 --- a/geodesy/haversine_distance.py +++ b/geodesy/haversine_distance.py @@ -21,10 +21,11 @@ def haversine_distance(lat1: float, lon1: float, lat2: float, lon2: float) -> fl computation like Haversine can be handy for shorter range distances. Args: - lat1, lon1: latitude and longitude of coordinate 1 - lat2, lon2: latitude and longitude of coordinate 2 + * `lat1`, `lon1`: latitude and longitude of coordinate 1 + * `lat2`, `lon2`: latitude and longitude of coordinate 2 Returns: geographical distance between two points in metres + >>> from collections import namedtuple >>> point_2d = namedtuple("point_2d", "lat lon") >>> SAN_FRANCISCO = point_2d(37.774856, -122.424227) From b0cb13fea54854b3a60eced27026db9a9c5dc5ab Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Mon, 23 Dec 2024 16:11:58 +0300 Subject: [PATCH 698/757] Fix sphinx/build_docs warnings for greedy_methods (#12463) * updating DIRECTORY.md * Fix sphinx/build_docs warnings for greedy_methods * Improve --------- Co-authored-by: MaximSmolskiy --- greedy_methods/smallest_range.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/greedy_methods/smallest_range.py b/greedy_methods/smallest_range.py index e2b7f8d7e96a..9adb12bf9029 100644 --- a/greedy_methods/smallest_range.py +++ b/greedy_methods/smallest_range.py @@ -14,12 +14,13 @@ def smallest_range(nums: list[list[int]]) -> list[int]: Uses min heap for efficiency. The range includes at least one number from each list. Args: - nums: List of k sorted integer lists. + `nums`: List of k sorted integer lists. Returns: list: Smallest range as a two-element list. Examples: + >>> smallest_range([[4, 10, 15, 24, 26], [0, 9, 12, 20], [5, 18, 22, 30]]) [20, 24] >>> smallest_range([[1, 2, 3], [1, 2, 3], [1, 2, 3]]) From 04fbfd6eae38b9897c1b8ff6aee487dd2523665b Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Tue, 24 Dec 2024 03:14:11 +0300 Subject: [PATCH 699/757] Fix sphinx/build_docs warnings for maths/volume (#12464) * Fix sphinx/build_docs warnings for maths/volume * Fix * Fix * Fix * Fix * Fix * Fix * Fix --- maths/volume.py | 149 ++++++++++++++++++++++++++++++------------------ 1 file changed, 95 insertions(+), 54 deletions(-) diff --git a/maths/volume.py b/maths/volume.py index 23fcf6be6ef1..08bdf72b013b 100644 --- a/maths/volume.py +++ b/maths/volume.py @@ -1,5 +1,6 @@ """ Find the volume of various shapes. + * https://en.wikipedia.org/wiki/Volume * https://en.wikipedia.org/wiki/Spherical_cap """ @@ -12,6 +13,7 @@ def vol_cube(side_length: float) -> float: """ Calculate the Volume of a Cube. + >>> vol_cube(1) 1.0 >>> vol_cube(3) @@ -33,6 +35,7 @@ def vol_cube(side_length: float) -> float: def vol_spherical_cap(height: float, radius: float) -> float: """ Calculate the volume of the spherical cap. + >>> vol_spherical_cap(1, 2) 5.235987755982988 >>> vol_spherical_cap(1.6, 2.6) @@ -57,20 +60,29 @@ def vol_spherical_cap(height: float, radius: float) -> float: def vol_spheres_intersect( radius_1: float, radius_2: float, centers_distance: float ) -> float: - """ + r""" Calculate the volume of the intersection of two spheres. + The intersection is composed by two spherical caps and therefore its volume is the - sum of the volumes of the spherical caps. First, it calculates the heights (h1, h2) - of the spherical caps, then the two volumes and it returns the sum. + sum of the volumes of the spherical caps. + First, it calculates the heights :math:`(h_1, h_2)` of the spherical caps, + then the two volumes and it returns the sum. The height formulas are - h1 = (radius_1 - radius_2 + centers_distance) - * (radius_1 + radius_2 - centers_distance) - / (2 * centers_distance) - h2 = (radius_2 - radius_1 + centers_distance) - * (radius_2 + radius_1 - centers_distance) - / (2 * centers_distance) - if centers_distance is 0 then it returns the volume of the smallers sphere - :return vol_spherical_cap(h1, radius_2) + vol_spherical_cap(h2, radius_1) + + .. math:: + h_1 = \frac{(radius_1 - radius_2 + centers\_distance) + \cdot (radius_1 + radius_2 - centers\_distance)} + {2 \cdot centers\_distance} + + h_2 = \frac{(radius_2 - radius_1 + centers\_distance) + \cdot (radius_2 + radius_1 - centers\_distance)} + {2 \cdot centers\_distance} + + if `centers_distance` is 0 then it returns the volume of the smallers sphere + + :return: ``vol_spherical_cap`` (:math:`h_1`, :math:`radius_2`) + + ``vol_spherical_cap`` (:math:`h_2`, :math:`radius_1`) + >>> vol_spheres_intersect(2, 2, 1) 21.205750411731103 >>> vol_spheres_intersect(2.6, 2.6, 1.6) @@ -112,14 +124,18 @@ def vol_spheres_intersect( def vol_spheres_union( radius_1: float, radius_2: float, centers_distance: float ) -> float: - """ + r""" Calculate the volume of the union of two spheres that possibly intersect. - It is the sum of sphere A and sphere B minus their intersection. - First, it calculates the volumes (v1, v2) of the spheres, - then the volume of the intersection (i) and it returns the sum v1+v2-i. - If centers_distance is 0 then it returns the volume of the larger sphere - :return vol_sphere(radius_1) + vol_sphere(radius_2) - - vol_spheres_intersect(radius_1, radius_2, centers_distance) + + It is the sum of sphere :math:`A` and sphere :math:`B` minus their intersection. + First, it calculates the volumes :math:`(v_1, v_2)` of the spheres, + then the volume of the intersection :math:`i` and + it returns the sum :math:`v_1 + v_2 - i`. + If `centers_distance` is 0 then it returns the volume of the larger sphere + + :return: ``vol_sphere`` (:math:`radius_1`) + ``vol_sphere`` (:math:`radius_2`) + - ``vol_spheres_intersect`` + (:math:`radius_1`, :math:`radius_2`, :math:`centers\_distance`) >>> vol_spheres_union(2, 2, 1) 45.814892864851146 @@ -157,7 +173,9 @@ def vol_spheres_union( def vol_cuboid(width: float, height: float, length: float) -> float: """ Calculate the Volume of a Cuboid. - :return multiple of width, length and height + + :return: multiple of `width`, `length` and `height` + >>> vol_cuboid(1, 1, 1) 1.0 >>> vol_cuboid(1, 2, 3) @@ -185,10 +203,12 @@ def vol_cuboid(width: float, height: float, length: float) -> float: def vol_cone(area_of_base: float, height: float) -> float: - """ - Calculate the Volume of a Cone. - Wikipedia reference: https://en.wikipedia.org/wiki/Cone - :return (1/3) * area_of_base * height + r""" + | Calculate the Volume of a Cone. + | Wikipedia reference: https://en.wikipedia.org/wiki/Cone + + :return: :math:`\frac{1}{3} \cdot area\_of\_base \cdot height` + >>> vol_cone(10, 3) 10.0 >>> vol_cone(1, 1) @@ -212,10 +232,12 @@ def vol_cone(area_of_base: float, height: float) -> float: def vol_right_circ_cone(radius: float, height: float) -> float: - """ - Calculate the Volume of a Right Circular Cone. - Wikipedia reference: https://en.wikipedia.org/wiki/Cone - :return (1/3) * pi * radius^2 * height + r""" + | Calculate the Volume of a Right Circular Cone. + | Wikipedia reference: https://en.wikipedia.org/wiki/Cone + + :return: :math:`\frac{1}{3} \cdot \pi \cdot radius^2 \cdot height` + >>> vol_right_circ_cone(2, 3) 12.566370614359172 >>> vol_right_circ_cone(0, 0) @@ -237,10 +259,12 @@ def vol_right_circ_cone(radius: float, height: float) -> float: def vol_prism(area_of_base: float, height: float) -> float: - """ - Calculate the Volume of a Prism. - Wikipedia reference: https://en.wikipedia.org/wiki/Prism_(geometry) - :return V = Bh + r""" + | Calculate the Volume of a Prism. + | Wikipedia reference: https://en.wikipedia.org/wiki/Prism_(geometry) + + :return: :math:`V = B \cdot h` + >>> vol_prism(10, 2) 20.0 >>> vol_prism(11, 1) @@ -264,10 +288,12 @@ def vol_prism(area_of_base: float, height: float) -> float: def vol_pyramid(area_of_base: float, height: float) -> float: - """ - Calculate the Volume of a Pyramid. - Wikipedia reference: https://en.wikipedia.org/wiki/Pyramid_(geometry) - :return (1/3) * Bh + r""" + | Calculate the Volume of a Pyramid. + | Wikipedia reference: https://en.wikipedia.org/wiki/Pyramid_(geometry) + + :return: :math:`\frac{1}{3} \cdot B \cdot h` + >>> vol_pyramid(10, 3) 10.0 >>> vol_pyramid(1.5, 3) @@ -291,10 +317,12 @@ def vol_pyramid(area_of_base: float, height: float) -> float: def vol_sphere(radius: float) -> float: - """ - Calculate the Volume of a Sphere. - Wikipedia reference: https://en.wikipedia.org/wiki/Sphere - :return (4/3) * pi * r^3 + r""" + | Calculate the Volume of a Sphere. + | Wikipedia reference: https://en.wikipedia.org/wiki/Sphere + + :return: :math:`\frac{4}{3} \cdot \pi \cdot r^3` + >>> vol_sphere(5) 523.5987755982989 >>> vol_sphere(1) @@ -315,10 +343,13 @@ def vol_sphere(radius: float) -> float: def vol_hemisphere(radius: float) -> float: - """Calculate the volume of a hemisphere - Wikipedia reference: https://en.wikipedia.org/wiki/Hemisphere - Other references: https://www.cuemath.com/geometry/hemisphere - :return 2/3 * pi * radius^3 + r""" + | Calculate the volume of a hemisphere + | Wikipedia reference: https://en.wikipedia.org/wiki/Hemisphere + | Other references: https://www.cuemath.com/geometry/hemisphere + + :return: :math:`\frac{2}{3} \cdot \pi \cdot radius^3` + >>> vol_hemisphere(1) 2.0943951023931953 >>> vol_hemisphere(7) @@ -339,9 +370,12 @@ def vol_hemisphere(radius: float) -> float: def vol_circular_cylinder(radius: float, height: float) -> float: - """Calculate the Volume of a Circular Cylinder. - Wikipedia reference: https://en.wikipedia.org/wiki/Cylinder - :return pi * radius^2 * height + r""" + | Calculate the Volume of a Circular Cylinder. + | Wikipedia reference: https://en.wikipedia.org/wiki/Cylinder + + :return: :math:`\pi \cdot radius^2 \cdot height` + >>> vol_circular_cylinder(1, 1) 3.141592653589793 >>> vol_circular_cylinder(4, 3) @@ -368,7 +402,9 @@ def vol_circular_cylinder(radius: float, height: float) -> float: def vol_hollow_circular_cylinder( inner_radius: float, outer_radius: float, height: float ) -> float: - """Calculate the Volume of a Hollow Circular Cylinder. + """ + Calculate the Volume of a Hollow Circular Cylinder. + >>> vol_hollow_circular_cylinder(1, 2, 3) 28.274333882308138 >>> vol_hollow_circular_cylinder(1.6, 2.6, 3.6) @@ -405,8 +441,9 @@ def vol_hollow_circular_cylinder( def vol_conical_frustum(height: float, radius_1: float, radius_2: float) -> float: - """Calculate the Volume of a Conical Frustum. - Wikipedia reference: https://en.wikipedia.org/wiki/Frustum + """ + | Calculate the Volume of a Conical Frustum. + | Wikipedia reference: https://en.wikipedia.org/wiki/Frustum >>> vol_conical_frustum(45, 7, 28) 48490.482608158454 @@ -443,9 +480,12 @@ def vol_conical_frustum(height: float, radius_1: float, radius_2: float) -> floa def vol_torus(torus_radius: float, tube_radius: float) -> float: - """Calculate the Volume of a Torus. - Wikipedia reference: https://en.wikipedia.org/wiki/Torus - :return 2pi^2 * torus_radius * tube_radius^2 + r""" + | Calculate the Volume of a Torus. + | Wikipedia reference: https://en.wikipedia.org/wiki/Torus + + :return: :math:`2 \pi^2 \cdot torus\_radius \cdot tube\_radius^2` + >>> vol_torus(1, 1) 19.739208802178716 >>> vol_torus(4, 3) @@ -471,8 +511,9 @@ def vol_torus(torus_radius: float, tube_radius: float) -> float: def vol_icosahedron(tri_side: float) -> float: - """Calculate the Volume of an Icosahedron. - Wikipedia reference: https://en.wikipedia.org/wiki/Regular_icosahedron + """ + | Calculate the Volume of an Icosahedron. + | Wikipedia reference: https://en.wikipedia.org/wiki/Regular_icosahedron >>> from math import isclose >>> isclose(vol_icosahedron(2.5), 34.088984228514256) From e9721aad59743d01e82582017884db528bad3e21 Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Tue, 24 Dec 2024 06:06:59 +0300 Subject: [PATCH 700/757] Fix sphinx/build_docs warnings for physics/horizontal_projectile_motion (#12467) --- physics/horizontal_projectile_motion.py | 68 +++++++++++++++---------- 1 file changed, 41 insertions(+), 27 deletions(-) diff --git a/physics/horizontal_projectile_motion.py b/physics/horizontal_projectile_motion.py index 80f85a1b7146..60f21c2b39c4 100644 --- a/physics/horizontal_projectile_motion.py +++ b/physics/horizontal_projectile_motion.py @@ -1,15 +1,18 @@ """ Horizontal Projectile Motion problem in physics. + This algorithm solves a specific problem in which -the motion starts from the ground as can be seen below: - (v = 0) - * * - * * - * * - * * - * * - * * -GROUND GROUND +the motion starts from the ground as can be seen below:: + + (v = 0) + * * + * * + * * + * * + * * + * * + GROUND GROUND + For more info: https://en.wikipedia.org/wiki/Projectile_motion """ @@ -43,14 +46,17 @@ def check_args(init_velocity: float, angle: float) -> None: def horizontal_distance(init_velocity: float, angle: float) -> float: - """ + r""" Returns the horizontal distance that the object cover + Formula: - v_0^2 * sin(2 * alpha) - --------------------- - g - v_0 - initial velocity - alpha - angle + .. math:: + \frac{v_0^2 \cdot \sin(2 \alpha)}{g} + + v_0 - \text{initial velocity} + + \alpha - \text{angle} + >>> horizontal_distance(30, 45) 91.77 >>> horizontal_distance(100, 78) @@ -70,14 +76,17 @@ def horizontal_distance(init_velocity: float, angle: float) -> float: def max_height(init_velocity: float, angle: float) -> float: - """ + r""" Returns the maximum height that the object reach + Formula: - v_0^2 * sin^2(alpha) - -------------------- - 2g - v_0 - initial velocity - alpha - angle + .. math:: + \frac{v_0^2 \cdot \sin^2 (\alpha)}{2 g} + + v_0 - \text{initial velocity} + + \alpha - \text{angle} + >>> max_height(30, 45) 22.94 >>> max_height(100, 78) @@ -97,14 +106,17 @@ def max_height(init_velocity: float, angle: float) -> float: def total_time(init_velocity: float, angle: float) -> float: - """ + r""" Returns total time of the motion + Formula: - 2 * v_0 * sin(alpha) - -------------------- - g - v_0 - initial velocity - alpha - angle + .. math:: + \frac{2 v_0 \cdot \sin (\alpha)}{g} + + v_0 - \text{initial velocity} + + \alpha - \text{angle} + >>> total_time(30, 45) 4.33 >>> total_time(100, 78) @@ -125,6 +137,8 @@ def total_time(init_velocity: float, angle: float) -> float: def test_motion() -> None: """ + Test motion + >>> test_motion() """ v0, angle = 25, 20 From c36aaf0fbcbc0f1a6c82b689ee87e383104b9e96 Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Tue, 24 Dec 2024 11:48:37 +0300 Subject: [PATCH 701/757] Fix sphinx/build_docs warnings for graphs/check_bipatrite (#12469) * Fix sphinx/build_docs warnings for graphs/check_bipatrite * Fix --- graphs/check_bipatrite.py | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/graphs/check_bipatrite.py b/graphs/check_bipatrite.py index 10b9cc965251..213f3f9480b5 100644 --- a/graphs/check_bipatrite.py +++ b/graphs/check_bipatrite.py @@ -6,16 +6,17 @@ def is_bipartite_dfs(graph: defaultdict[int, list[int]]) -> bool: Check if a graph is bipartite using depth-first search (DFS). Args: - graph: Adjacency list representing the graph. + `graph`: Adjacency list representing the graph. Returns: - True if bipartite, False otherwise. + ``True`` if bipartite, ``False`` otherwise. Checks if the graph can be divided into two sets of vertices, such that no two vertices within the same set are connected by an edge. Examples: - # FIXME: This test should pass. + + >>> # FIXME: This test should pass. >>> is_bipartite_dfs(defaultdict(list, {0: [1, 2], 1: [0, 3], 2: [0, 4]})) Traceback (most recent call last): ... @@ -37,7 +38,7 @@ def is_bipartite_dfs(graph: defaultdict[int, list[int]]) -> bool: ... KeyError: 0 - # FIXME: This test should fails with KeyError: 4. + >>> # FIXME: This test should fails with KeyError: 4. >>> is_bipartite_dfs({0: [1, 3], 1: [0, 2], 2: [1, 3], 3: [0, 2], 9: [0]}) False >>> is_bipartite_dfs({0: [-1, 3], 1: [0, -2]}) @@ -51,7 +52,8 @@ def is_bipartite_dfs(graph: defaultdict[int, list[int]]) -> bool: ... KeyError: 0 - # FIXME: This test should fails with TypeError: list indices must be integers or... + >>> # FIXME: This test should fails with + >>> # TypeError: list indices must be integers or... >>> is_bipartite_dfs({0: [1.0, 3.0], 1.0: [0, 2.0], 2.0: [1.0, 3.0], 3.0: [0, 2.0]}) True >>> is_bipartite_dfs({"a": [1, 3], "b": [0, 2], "c": [1, 3], "d": [0, 2]}) @@ -95,16 +97,17 @@ def is_bipartite_bfs(graph: defaultdict[int, list[int]]) -> bool: Check if a graph is bipartite using a breadth-first search (BFS). Args: - graph: Adjacency list representing the graph. + `graph`: Adjacency list representing the graph. Returns: - True if bipartite, False otherwise. + ``True`` if bipartite, ``False`` otherwise. Check if the graph can be divided into two sets of vertices, such that no two vertices within the same set are connected by an edge. Examples: - # FIXME: This test should pass. + + >>> # FIXME: This test should pass. >>> is_bipartite_bfs(defaultdict(list, {0: [1, 2], 1: [0, 3], 2: [0, 4]})) Traceback (most recent call last): ... @@ -126,7 +129,7 @@ def is_bipartite_bfs(graph: defaultdict[int, list[int]]) -> bool: ... KeyError: 0 - # FIXME: This test should fails with KeyError: 4. + >>> # FIXME: This test should fails with KeyError: 4. >>> is_bipartite_bfs({0: [1, 3], 1: [0, 2], 2: [1, 3], 3: [0, 2], 9: [0]}) False >>> is_bipartite_bfs({0: [-1, 3], 1: [0, -2]}) @@ -140,7 +143,8 @@ def is_bipartite_bfs(graph: defaultdict[int, list[int]]) -> bool: ... KeyError: 0 - # FIXME: This test should fails with TypeError: list indices must be integers or... + >>> # FIXME: This test should fails with + >>> # TypeError: list indices must be integers or... >>> is_bipartite_bfs({0: [1.0, 3.0], 1.0: [0, 2.0], 2.0: [1.0, 3.0], 3.0: [0, 2.0]}) True >>> is_bipartite_bfs({"a": [1, 3], "b": [0, 2], "c": [1, 3], "d": [0, 2]}) From ae28fa7fe362c8cb0238dbb6b237d42179e8beb3 Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Tue, 24 Dec 2024 16:17:22 +0300 Subject: [PATCH 702/757] Fix sphinx/build_docs warnings for data_structures/binary_tree/mirror_binary_tree (#12470) --- .../binary_tree/mirror_binary_tree.py | 52 +++++++++++-------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/data_structures/binary_tree/mirror_binary_tree.py b/data_structures/binary_tree/mirror_binary_tree.py index 62e2f08dd4e0..f6611d66d676 100644 --- a/data_structures/binary_tree/mirror_binary_tree.py +++ b/data_structures/binary_tree/mirror_binary_tree.py @@ -56,6 +56,8 @@ def mirror(self) -> Node: def make_tree_seven() -> Node: r""" Return a binary tree with 7 nodes that looks like this: + :: + 1 / \ 2 3 @@ -81,13 +83,15 @@ def make_tree_seven() -> Node: def make_tree_nine() -> Node: r""" Return a binary tree with 9 nodes that looks like this: - 1 - / \ - 2 3 - / \ \ - 4 5 6 - / \ \ - 7 8 9 + :: + + 1 + / \ + 2 3 + / \ \ + 4 5 6 + / \ \ + 7 8 9 >>> tree_nine = make_tree_nine() >>> len(tree_nine) @@ -117,23 +121,25 @@ def main() -> None: >>> tuple(tree.mirror()) (6, 3, 1, 9, 5, 2, 8, 4, 7) - nine_tree: - 1 - / \ - 2 3 - / \ \ - 4 5 6 - / \ \ - 7 8 9 - - The mirrored tree looks like this: + nine_tree:: + + 1 + / \ + 2 3 + / \ \ + 4 5 6 + / \ \ + 7 8 9 + + The mirrored tree looks like this:: + 1 - / \ - 3 2 - / / \ - 6 5 4 - / / \ - 9 8 7 + / \ + 3 2 + / / \ + 6 5 4 + / / \ + 9 8 7 """ trees = {"zero": Node(0), "seven": make_tree_seven(), "nine": make_tree_nine()} for name, tree in trees.items(): From eb652cf3d48fbd3b51450e95640ce5aec63a066b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 28 Dec 2024 00:18:08 +0300 Subject: [PATCH 703/757] Bump astral-sh/setup-uv from 4 to 5 (#12445) Bumps [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv) from 4 to 5. - [Release notes](https://github.com/astral-sh/setup-uv/releases) - [Commits](https://github.com/astral-sh/setup-uv/compare/v4...v5) --- updated-dependencies: - dependency-name: astral-sh/setup-uv dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- .github/workflows/project_euler.yml | 4 ++-- .github/workflows/ruff.yml | 2 +- .github/workflows/sphinx.yml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a6f308715cc2..62829b2b45a5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: astral-sh/setup-uv@v4 + - uses: astral-sh/setup-uv@v5 with: enable-cache: true cache-dependency-glob: uv.lock diff --git a/.github/workflows/project_euler.yml b/.github/workflows/project_euler.yml index 84c55335451e..8d51ad8850cf 100644 --- a/.github/workflows/project_euler.yml +++ b/.github/workflows/project_euler.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: astral-sh/setup-uv@v4 + - uses: astral-sh/setup-uv@v5 - uses: actions/setup-python@v5 with: python-version: 3.x @@ -25,7 +25,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: astral-sh/setup-uv@v4 + - uses: astral-sh/setup-uv@v5 - uses: actions/setup-python@v5 with: python-version: 3.x diff --git a/.github/workflows/ruff.yml b/.github/workflows/ruff.yml index 2c6f92fcf7bf..cfe127b3521f 100644 --- a/.github/workflows/ruff.yml +++ b/.github/workflows/ruff.yml @@ -12,5 +12,5 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: astral-sh/setup-uv@v4 + - uses: astral-sh/setup-uv@v5 - run: uvx ruff check --output-format=github . diff --git a/.github/workflows/sphinx.yml b/.github/workflows/sphinx.yml index e3e2ce81a95d..d02435d98028 100644 --- a/.github/workflows/sphinx.yml +++ b/.github/workflows/sphinx.yml @@ -26,7 +26,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: astral-sh/setup-uv@v4 + - uses: astral-sh/setup-uv@v5 - uses: actions/setup-python@v5 with: python-version: 3.13 From 5bef6ac9296c20250db7d494bbbc9c8bf4bfccdc Mon Sep 17 00:00:00 2001 From: Scarfinos <158184182+Scarfinos@users.noreply.github.com> Date: Fri, 27 Dec 2024 23:22:36 +0100 Subject: [PATCH 704/757] Improve coverage special_numbers (#12414) * Improve coverage bell_numbers * improve more function * Update hamming_numbers.py --------- Co-authored-by: Maxim Smolskiy --- maths/special_numbers/bell_numbers.py | 4 ++++ maths/special_numbers/hamming_numbers.py | 6 +++++- maths/special_numbers/harshad_numbers.py | 8 ++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/maths/special_numbers/bell_numbers.py b/maths/special_numbers/bell_numbers.py index 5d99334d7add..d573e7a3962d 100644 --- a/maths/special_numbers/bell_numbers.py +++ b/maths/special_numbers/bell_numbers.py @@ -21,6 +21,10 @@ def bell_numbers(max_set_length: int) -> list[int]: list: A list of Bell numbers for sets of lengths from 0 to max_set_length. Examples: + >>> bell_numbers(-2) + Traceback (most recent call last): + ... + ValueError: max_set_length must be non-negative >>> bell_numbers(0) [1] >>> bell_numbers(1) diff --git a/maths/special_numbers/hamming_numbers.py b/maths/special_numbers/hamming_numbers.py index 4575119c8a95..a473cc93883b 100644 --- a/maths/special_numbers/hamming_numbers.py +++ b/maths/special_numbers/hamming_numbers.py @@ -13,6 +13,10 @@ def hamming(n_element: int) -> list: :param n_element: The number of elements on the list :return: The nth element of the list + >>> hamming(-5) + Traceback (most recent call last): + ... + ValueError: n_element should be a positive number >>> hamming(5) [1, 2, 3, 4, 5] >>> hamming(10) @@ -22,7 +26,7 @@ def hamming(n_element: int) -> list: """ n_element = int(n_element) if n_element < 1: - my_error = ValueError("a should be a positive number") + my_error = ValueError("n_element should be a positive number") raise my_error hamming_list = [1] diff --git a/maths/special_numbers/harshad_numbers.py b/maths/special_numbers/harshad_numbers.py index 61667adfa127..417120bd840e 100644 --- a/maths/special_numbers/harshad_numbers.py +++ b/maths/special_numbers/harshad_numbers.py @@ -11,6 +11,8 @@ def int_to_base(number: int, base: int) -> str: Where 'base' ranges from 2 to 36. Examples: + >>> int_to_base(0, 21) + '0' >>> int_to_base(23, 2) '10111' >>> int_to_base(58, 5) @@ -26,6 +28,10 @@ def int_to_base(number: int, base: int) -> str: Traceback (most recent call last): ... ValueError: 'base' must be between 2 and 36 inclusive + >>> int_to_base(-99, 16) + Traceback (most recent call last): + ... + ValueError: number must be a positive integer """ if base < 2 or base > 36: @@ -101,6 +107,8 @@ def harshad_numbers_in_base(limit: int, base: int) -> list[str]: Traceback (most recent call last): ... ValueError: 'base' must be between 2 and 36 inclusive + >>> harshad_numbers_in_base(-12, 6) + [] """ if base < 2 or base > 36: From 8bbe8caa256882ef2ebdbb3274e6f99f804716bd Mon Sep 17 00:00:00 2001 From: Scarfinos <158184182+Scarfinos@users.noreply.github.com> Date: Fri, 27 Dec 2024 23:40:35 +0100 Subject: [PATCH 705/757] Improve test coverage for matrix exponentiation (#12388) * #9943 : Adding coverage test for basic_graphs.py * #9943 : Adding coverage test for basic_graphs.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Solve problem of line too long * Improving coverage for matrix_exponentiation.py * fix more than one file * Update matrix_exponentiation.py * Update matrix_exponentiation.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Maxim Smolskiy --- maths/matrix_exponentiation.py | 36 ++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/maths/matrix_exponentiation.py b/maths/matrix_exponentiation.py index 7c37151c87ca..7cdac9d34674 100644 --- a/maths/matrix_exponentiation.py +++ b/maths/matrix_exponentiation.py @@ -39,6 +39,21 @@ def modular_exponentiation(a, b): def fibonacci_with_matrix_exponentiation(n, f1, f2): + """ + Returns the nth number of the Fibonacci sequence that + starts with f1 and f2 + Uses the matrix exponentiation + >>> fibonacci_with_matrix_exponentiation(1, 5, 6) + 5 + >>> fibonacci_with_matrix_exponentiation(2, 10, 11) + 11 + >>> fibonacci_with_matrix_exponentiation(13, 0, 1) + 144 + >>> fibonacci_with_matrix_exponentiation(10, 5, 9) + 411 + >>> fibonacci_with_matrix_exponentiation(9, 2, 3) + 89 + """ # Trivial Cases if n == 1: return f1 @@ -50,21 +65,34 @@ def fibonacci_with_matrix_exponentiation(n, f1, f2): def simple_fibonacci(n, f1, f2): + """ + Returns the nth number of the Fibonacci sequence that + starts with f1 and f2 + Uses the definition + >>> simple_fibonacci(1, 5, 6) + 5 + >>> simple_fibonacci(2, 10, 11) + 11 + >>> simple_fibonacci(13, 0, 1) + 144 + >>> simple_fibonacci(10, 5, 9) + 411 + >>> simple_fibonacci(9, 2, 3) + 89 + """ # Trivial Cases if n == 1: return f1 elif n == 2: return f2 - fn_1 = f1 - fn_2 = f2 n -= 2 while n > 0: - fn_1, fn_2 = fn_1 + fn_2, fn_1 + f2, f1 = f1 + f2, f2 n -= 1 - return fn_1 + return f2 def matrix_exponentiation_time(): From 76471819bd5b9df6fe5fde4c763396412ce45edc Mon Sep 17 00:00:00 2001 From: Scarfinos <158184182+Scarfinos@users.noreply.github.com> Date: Fri, 27 Dec 2024 23:52:40 +0100 Subject: [PATCH 706/757] Improve test coverage for armstrong numbers (#12327) --- maths/special_numbers/armstrong_numbers.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/maths/special_numbers/armstrong_numbers.py b/maths/special_numbers/armstrong_numbers.py index b2b4010a8f5b..a3cb69b814de 100644 --- a/maths/special_numbers/armstrong_numbers.py +++ b/maths/special_numbers/armstrong_numbers.py @@ -43,9 +43,9 @@ def armstrong_number(n: int) -> bool: def pluperfect_number(n: int) -> bool: """Return True if n is a pluperfect number or False if it is not - >>> all(armstrong_number(n) for n in PASSING) + >>> all(pluperfect_number(n) for n in PASSING) True - >>> any(armstrong_number(n) for n in FAILING) + >>> any(pluperfect_number(n) for n in FAILING) False """ if not isinstance(n, int) or n < 1: @@ -70,9 +70,9 @@ def pluperfect_number(n: int) -> bool: def narcissistic_number(n: int) -> bool: """Return True if n is a narcissistic number or False if it is not. - >>> all(armstrong_number(n) for n in PASSING) + >>> all(narcissistic_number(n) for n in PASSING) True - >>> any(armstrong_number(n) for n in FAILING) + >>> any(narcissistic_number(n) for n in FAILING) False """ if not isinstance(n, int) or n < 1: From 2ae9534fc68b1901d8056331aa2a4dbedc9d947e Mon Sep 17 00:00:00 2001 From: Anamaria Miranda Date: Sat, 28 Dec 2024 00:03:13 +0100 Subject: [PATCH 707/757] Added test to linear regression (#12353) --- machine_learning/linear_regression.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/machine_learning/linear_regression.py b/machine_learning/linear_regression.py index 839a5366d1cc..1d11e5a9cc2b 100644 --- a/machine_learning/linear_regression.py +++ b/machine_learning/linear_regression.py @@ -41,6 +41,14 @@ def run_steep_gradient_descent(data_x, data_y, len_data, alpha, theta): :param theta : Feature vector (weight's for our model) ;param return : Updated Feature's, using curr_features - alpha_ * gradient(w.r.t. feature) + >>> import numpy as np + >>> data_x = np.array([[1, 2], [3, 4]]) + >>> data_y = np.array([5, 6]) + >>> len_data = len(data_x) + >>> alpha = 0.01 + >>> theta = np.array([0.1, 0.2]) + >>> run_steep_gradient_descent(data_x, data_y, len_data, alpha, theta) + array([0.196, 0.343]) """ n = len_data @@ -58,6 +66,12 @@ def sum_of_square_error(data_x, data_y, len_data, theta): :param len_data : len of the dataset :param theta : contains the feature vector :return : sum of square error computed from given feature's + + Example: + >>> vc_x = np.array([[1.1], [2.1], [3.1]]) + >>> vc_y = np.array([1.2, 2.2, 3.2]) + >>> round(sum_of_square_error(vc_x, vc_y, 3, np.array([1])),3) + np.float64(0.005) """ prod = np.dot(theta, data_x.transpose()) prod -= data_y.transpose() @@ -93,6 +107,11 @@ def mean_absolute_error(predicted_y, original_y): :param predicted_y : contains the output of prediction (result vector) :param original_y : contains values of expected outcome :return : mean absolute error computed from given feature's + + >>> predicted_y = [3, -0.5, 2, 7] + >>> original_y = [2.5, 0.0, 2, 8] + >>> mean_absolute_error(predicted_y, original_y) + 0.5 """ total = sum(abs(y - predicted_y[i]) for i, y in enumerate(original_y)) return total / len(original_y) @@ -114,4 +133,7 @@ def main(): if __name__ == "__main__": + import doctest + + doctest.testmod() main() From 1652d05e9ee25d54eea5576976d537975dcad9bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julia=20Arag=C3=A3o?= <101305675+juliaaragao@users.noreply.github.com> Date: Sat, 28 Dec 2024 00:26:29 +0100 Subject: [PATCH 708/757] adding test to electronics/electric_power.py (#12387) * test electric_power * Update electric_power.py * Update electric_power.py * Update electric_power.py * Update electric_power.py --------- Co-authored-by: Julia Co-authored-by: Maxim Smolskiy --- electronics/electric_power.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/electronics/electric_power.py b/electronics/electric_power.py index 8b92e320ace3..8e3454e39c3f 100644 --- a/electronics/electric_power.py +++ b/electronics/electric_power.py @@ -23,20 +23,22 @@ def electric_power(voltage: float, current: float, power: float) -> tuple: >>> electric_power(voltage=2, current=4, power=2) Traceback (most recent call last): ... - ValueError: Only one argument must be 0 + ValueError: Exactly one argument must be 0 >>> electric_power(voltage=0, current=0, power=2) Traceback (most recent call last): ... - ValueError: Only one argument must be 0 + ValueError: Exactly one argument must be 0 >>> electric_power(voltage=0, current=2, power=-4) Traceback (most recent call last): ... ValueError: Power cannot be negative in any electrical/electronics system >>> electric_power(voltage=2.2, current=2.2, power=0) Result(name='power', value=4.84) + >>> electric_power(current=0, power=6, voltage=2) + Result(name='current', value=3.0) """ if (voltage, current, power).count(0) != 1: - raise ValueError("Only one argument must be 0") + raise ValueError("Exactly one argument must be 0") elif power < 0: raise ValueError( "Power cannot be negative in any electrical/electronics system" @@ -48,7 +50,7 @@ def electric_power(voltage: float, current: float, power: float) -> tuple: elif power == 0: return Result("power", float(round(abs(voltage * current), 2))) else: - raise ValueError("Exactly one argument must be 0") + raise AssertionError if __name__ == "__main__": From 929b7dc057cd56f90b260cd665fb67886bcadeea Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Sat, 28 Dec 2024 11:43:25 +0300 Subject: [PATCH 709/757] Fix Gaussian elimination pivoting (#11393) * updating DIRECTORY.md * Fix Gaussian elimination pivoting * Fix review issues * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: MaximSmolskiy Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .../src/gaussian_elimination_pivoting.py | 39 ++++++++----------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/linear_algebra/src/gaussian_elimination_pivoting.py b/linear_algebra/src/gaussian_elimination_pivoting.py index ecaacce19a31..efc1ddd64a2e 100644 --- a/linear_algebra/src/gaussian_elimination_pivoting.py +++ b/linear_algebra/src/gaussian_elimination_pivoting.py @@ -22,40 +22,33 @@ def solve_linear_system(matrix: np.ndarray) -> np.ndarray: >>> solution = solve_linear_system(np.column_stack((A, B))) >>> np.allclose(solution, np.array([2., 3., -1.])) True - >>> solve_linear_system(np.array([[0, 0], [0, 0]], dtype=float)) - array([nan, nan]) + >>> solve_linear_system(np.array([[0, 0, 0]], dtype=float)) + Traceback (most recent call last): + ... + ValueError: Matrix is not square + >>> solve_linear_system(np.array([[0, 0, 0], [0, 0, 0]], dtype=float)) + Traceback (most recent call last): + ... + ValueError: Matrix is singular """ ab = np.copy(matrix) num_of_rows = ab.shape[0] num_of_columns = ab.shape[1] - 1 x_lst: list[float] = [] - # Lead element search - for column_num in range(num_of_rows): - for i in range(column_num, num_of_columns): - if abs(ab[i][column_num]) > abs(ab[column_num][column_num]): - ab[[column_num, i]] = ab[[i, column_num]] - if ab[column_num, column_num] == 0.0: - raise ValueError("Matrix is not correct") - else: - pass - if column_num != 0: - for i in range(column_num, num_of_rows): - ab[i, :] -= ( - ab[i, column_num - 1] - / ab[column_num - 1, column_num - 1] - * ab[column_num - 1, :] - ) + if num_of_rows != num_of_columns: + raise ValueError("Matrix is not square") - # Upper triangular matrix for column_num in range(num_of_rows): + # Lead element search for i in range(column_num, num_of_columns): if abs(ab[i][column_num]) > abs(ab[column_num][column_num]): ab[[column_num, i]] = ab[[i, column_num]] - if ab[column_num, column_num] == 0.0: - raise ValueError("Matrix is not correct") - else: - pass + + # Upper triangular matrix + if abs(ab[column_num, column_num]) < 1e-8: + raise ValueError("Matrix is singular") + if column_num != 0: for i in range(column_num, num_of_rows): ab[i, :] -= ( From b5c8fbf2e8254b53056b741aacce3842736ba177 Mon Sep 17 00:00:00 2001 From: Joy Khandelwal <116290658+joy-programs@users.noreply.github.com> Date: Sat, 28 Dec 2024 14:21:28 +0530 Subject: [PATCH 710/757] Add additional doctests, fix grammatical errors for maths/perfect_number.py (#12477) * Add additional doctests for the perfect number algorithm and fix grammatical errors. Contributes to #9943 * Added newline at End of file * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Apply suggestions from code review --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Tianyi Zheng --- maths/perfect_number.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/maths/perfect_number.py b/maths/perfect_number.py index df6b6e3d91d8..52c816cc7895 100644 --- a/maths/perfect_number.py +++ b/maths/perfect_number.py @@ -46,17 +46,27 @@ def perfect(number: int) -> bool: False >>> perfect(-1) False + >>> perfect(33550336) # Large perfect number + True + >>> perfect(33550337) # Just above a large perfect number + False + >>> perfect(1) # Edge case: 1 is not a perfect number + False + >>> perfect("123") # String representation of a number + Traceback (most recent call last): + ... + ValueError: number must be an integer >>> perfect(12.34) Traceback (most recent call last): ... - ValueError: number must an integer + ValueError: number must be an integer >>> perfect("Hello") Traceback (most recent call last): ... - ValueError: number must an integer + ValueError: number must be an integer """ if not isinstance(number, int): - raise ValueError("number must an integer") + raise ValueError("number must be an integer") if number <= 0: return False return sum(i for i in range(1, number // 2 + 1) if number % i == 0) == number @@ -70,8 +80,7 @@ def perfect(number: int) -> bool: try: number = int(input("Enter a positive integer: ").strip()) except ValueError: - msg = "number must an integer" - print(msg) + msg = "number must be an integer" raise ValueError(msg) print(f"{number} is {'' if perfect(number) else 'not '}a Perfect Number.") From d496d5611fe266b7123b4a1f3efb4bcb7c2f38f2 Mon Sep 17 00:00:00 2001 From: Shi Entong <144505619+setbit123@users.noreply.github.com> Date: Sat, 28 Dec 2024 17:22:07 +0800 Subject: [PATCH 711/757] Remove inaccessible URL in computer_vision/README.md (#12383) Remove inaccessible URL. --- computer_vision/README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/computer_vision/README.md b/computer_vision/README.md index 1657128fd25e..61462567b662 100644 --- a/computer_vision/README.md +++ b/computer_vision/README.md @@ -8,4 +8,3 @@ Image processing and computer vision are a little different from each other. Ima While computer vision comes from modelling image processing using the techniques of machine learning, computer vision applies machine learning to recognize patterns for interpretation of images (much like the process of visual reasoning of human vision). * -* From 1909f2272f11ebe7626d2dee78c11a91134e39e7 Mon Sep 17 00:00:00 2001 From: jperezr <122382210+MRJPEREZR@users.noreply.github.com> Date: Sat, 28 Dec 2024 11:03:24 +0100 Subject: [PATCH 712/757] adding doctests to maths/trapezoidal_rule.py (#12193) * adding doctests to trapezoidal_rule.py * adding algorithm delta-star transformation * updating DIRECTORY.md * delete file star_delta_transform.py * updating DIRECTORY.md * modified: ../DIRECTORY.md --------- Co-authored-by: MRJPEREZR --- maths/trapezoidal_rule.py | 48 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 3 deletions(-) diff --git a/maths/trapezoidal_rule.py b/maths/trapezoidal_rule.py index 9a4ddc8af66b..0186629ee378 100644 --- a/maths/trapezoidal_rule.py +++ b/maths/trapezoidal_rule.py @@ -5,13 +5,25 @@ method 1: "extended trapezoidal rule" +int(f) = dx/2 * (f1 + 2f2 + ... + fn) """ def method_1(boundary, steps): - # "extended trapezoidal rule" - # int(f) = dx/2 * (f1 + 2f2 + ... + fn) + """ + Apply the extended trapezoidal rule to approximate the integral of function f(x) + over the interval defined by 'boundary' with the number of 'steps'. + + Args: + boundary (list of floats): A list containing the start and end values [a, b]. + steps (int): The number of steps or subintervals. + Returns: + float: Approximation of the integral of f(x) over [a, b]. + Examples: + >>> method_1([0, 1], 10) + 0.3349999999999999 + """ h = (boundary[1] - boundary[0]) / steps a = boundary[0] b = boundary[1] @@ -26,13 +38,40 @@ def method_1(boundary, steps): def make_points(a, b, h): + """ + Generates points between 'a' and 'b' with step size 'h', excluding the end points. + Args: + a (float): Start value + b (float): End value + h (float): Step size + Examples: + >>> list(make_points(0, 10, 2.5)) + [2.5, 5.0, 7.5] + + >>> list(make_points(0, 10, 2)) + [2, 4, 6, 8] + + >>> list(make_points(1, 21, 5)) + [6, 11, 16] + + >>> list(make_points(1, 5, 2)) + [3] + + >>> list(make_points(1, 4, 3)) + [] + """ x = a + h - while x < (b - h): + while x <= (b - h): yield x x = x + h def f(x): # enter your function here + """ + Example: + >>> f(2) + 4 + """ y = (x - 0) * (x - 0) return y @@ -47,4 +86,7 @@ def main(): if __name__ == "__main__": + import doctest + + doctest.testmod() main() From 2b58ab040295fbbf2e463ba8cd77ad935d942968 Mon Sep 17 00:00:00 2001 From: Andrwaa <165920381+Andrwaa@users.noreply.github.com> Date: Sat, 28 Dec 2024 12:17:48 +0100 Subject: [PATCH 713/757] compare-method added to Vector class in lib.py (#12448) * compare-method added to Vector class in lib.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Updated lib.py with suggestions * Updated lib.py with suggestions * Updated lib.py with __eq__ method --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- linear_algebra/src/lib.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/linear_algebra/src/lib.py b/linear_algebra/src/lib.py index 5af6c62e3ad4..0d6a348475cd 100644 --- a/linear_algebra/src/lib.py +++ b/linear_algebra/src/lib.py @@ -46,7 +46,6 @@ class Vector: change_component(pos: int, value: float): changes specified component euclidean_length(): returns the euclidean length of the vector angle(other: Vector, deg: bool): returns the angle between two vectors - TODO: compare-operator """ def __init__(self, components: Collection[float] | None = None) -> None: @@ -96,6 +95,16 @@ def __sub__(self, other: Vector) -> Vector: else: # error case raise Exception("must have the same size") + def __eq__(self, other: object) -> bool: + """ + performs the comparison between two vectors + """ + if not isinstance(other, Vector): + return NotImplemented + if len(self) != len(other): + return False + return all(self.component(i) == other.component(i) for i in range(len(self))) + @overload def __mul__(self, other: float) -> Vector: ... From 2d68bb50e5f12532b5a0d616305c4f805d2b8ff9 Mon Sep 17 00:00:00 2001 From: KICH Yassine Date: Sun, 29 Dec 2024 12:56:36 +0100 Subject: [PATCH 714/757] Fix split function to handle trailing delimiters correctly (#12423) * Fix split function to handle trailing delimiters correctly * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update split.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Maxim Smolskiy --- strings/split.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/strings/split.py b/strings/split.py index b62b86d2401f..ed194ec69c2f 100644 --- a/strings/split.py +++ b/strings/split.py @@ -14,6 +14,9 @@ def split(string: str, separator: str = " ") -> list: >>> split("12:43:39",separator = ":") ['12', '43', '39'] + + >>> split(";abbb;;c;", separator=';') + ['', 'abbb', '', 'c', ''] """ split_words = [] @@ -23,7 +26,7 @@ def split(string: str, separator: str = " ") -> list: if char == separator: split_words.append(string[last_index:index]) last_index = index + 1 - elif index + 1 == len(string): + if index + 1 == len(string): split_words.append(string[last_index : index + 1]) return split_words From 972a5c1e432e0a3fa9e990422318269219192a53 Mon Sep 17 00:00:00 2001 From: RajdeepBakolia2004 <144157867+RajdeepBakolia2004@users.noreply.github.com> Date: Sun, 29 Dec 2024 19:05:33 +0530 Subject: [PATCH 715/757] fixed the issue in strings/join.py (#12434) * fixed the issue in strings/join.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update join.py * Update join.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Maxim Smolskiy --- strings/join.py | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/strings/join.py b/strings/join.py index 5c02f65a20ce..cdcc3a1377f4 100644 --- a/strings/join.py +++ b/strings/join.py @@ -24,6 +24,8 @@ def join(separator: str, separated: list[str]) -> str: 'a' >>> join(" ", ["You", "are", "amazing!"]) 'You are amazing!' + >>> join(",", ["", "", ""]) + ',,' This example should raise an exception for non-string elements: @@ -37,15 +39,33 @@ def join(separator: str, separated: list[str]) -> str: 'apple-banana-cherry' """ - joined = "" + # Check that all elements are strings for word_or_phrase in separated: + # If the element is not a string, raise an exception if not isinstance(word_or_phrase, str): raise Exception("join() accepts only strings") + + joined: str = "" + """ + The last element of the list is not followed by the separator. + So, we need to iterate through the list and join each element + with the separator except the last element. + """ + last_index: int = len(separated) - 1 + """ + Iterate through the list and join each element with the separator. + Except the last element, all other elements are followed by the separator. + """ + for word_or_phrase in separated[:last_index]: + # join the element with the separator. joined += word_or_phrase + separator - # Remove the trailing separator - # by stripping it from the result - return joined.strip(separator) + # If the list is not empty, join the last element. + if separated != []: + joined += separated[last_index] + + # Return the joined string. + return joined if __name__ == "__main__": From d9092d88dd8b47323d14f87025195e0c76fe7889 Mon Sep 17 00:00:00 2001 From: Sankalpa Sarkar <137193167+sanks011@users.noreply.github.com> Date: Sun, 29 Dec 2024 19:23:31 +0530 Subject: [PATCH 716/757] fixes requirements error (#12438) * fixes join.py action * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fixes split.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fixed two requirements * Custom Implementation of join.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * updated join.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update split.py * Update join.py * Update join.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Maxim Smolskiy --- requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 4cc83f44987d..b104505e01bc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ beautifulsoup4 -fake_useragent +fake-useragent imageio keras lxml @@ -11,7 +11,7 @@ pillow requests rich scikit-learn -sphinx_pyproject +sphinx-pyproject statsmodels sympy tweepy From bfc804a41c6fb7f3c2e371b15d50ba4830bab3a7 Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Sun, 29 Dec 2024 18:41:28 +0300 Subject: [PATCH 717/757] Fix sphinx/build_docs warnings for physics/newtons_second_law_of_motion (#12480) * Fix sphinx/build_docs warnings for physics/newtons_second_law_of_motion * Fix * Fix * Fix review issue --- physics/newtons_second_law_of_motion.py | 83 ++++++++++++++----------- 1 file changed, 47 insertions(+), 36 deletions(-) diff --git a/physics/newtons_second_law_of_motion.py b/physics/newtons_second_law_of_motion.py index 53fab6ce78b9..4149e2494f31 100644 --- a/physics/newtons_second_law_of_motion.py +++ b/physics/newtons_second_law_of_motion.py @@ -1,18 +1,22 @@ -""" -Description : -Newton's second law of motion pertains to the behavior of objects for which -all existing forces are not balanced. -The second law states that the acceleration of an object is dependent upon two variables -- the net force acting upon the object and the mass of the object. -The acceleration of an object depends directly -upon the net force acting upon the object, -and inversely upon the mass of the object. -As the force acting upon an object is increased, -the acceleration of the object is increased. -As the mass of an object is increased, the acceleration of the object is decreased. +r""" +Description: + Newton's second law of motion pertains to the behavior of objects for which + all existing forces are not balanced. + The second law states that the acceleration of an object is dependent upon + two variables - the net force acting upon the object and the mass of the object. + The acceleration of an object depends directly + upon the net force acting upon the object, + and inversely upon the mass of the object. + As the force acting upon an object is increased, + the acceleration of the object is increased. + As the mass of an object is increased, the acceleration of the object is decreased. + Source: https://www.physicsclassroom.com/class/newtlaws/Lesson-3/Newton-s-Second-Law -Formulation: Fnet = m • a -Diagrammatic Explanation: + +Formulation: F_net = m • a + +Diagrammatic Explanation:: + Forces are unbalanced | | @@ -26,35 +30,42 @@ / \ / \ / \ - __________________ ____ ________________ - |The acceleration | |The acceleration | - |depends directly | |depends inversely | - |on the net Force | |upon the object's | - |_________________| |mass_______________| -Units: -1 Newton = 1 kg X meters / (seconds^2) + __________________ ____________________ + | The acceleration | | The acceleration | + | depends directly | | depends inversely | + | on the net force | | upon the object's | + | | | mass | + |__________________| |____________________| + +Units: 1 Newton = 1 kg • meters/seconds^2 + How to use? -Inputs: - ___________________________________________________ - |Name | Units | Type | - |-------------|-------------------------|-----------| - |mass | (in kgs) | float | - |-------------|-------------------------|-----------| - |acceleration | (in meters/(seconds^2)) | float | - |_____________|_________________________|___________| - -Output: - ___________________________________________________ - |Name | Units | Type | - |-------------|-------------------------|-----------| - |force | (in Newtons) | float | - |_____________|_________________________|___________| + +Inputs:: + + ______________ _____________________ ___________ + | Name | Units | Type | + |--------------|---------------------|-----------| + | mass | in kgs | float | + |--------------|---------------------|-----------| + | acceleration | in meters/seconds^2 | float | + |______________|_____________________|___________| + +Output:: + + ______________ _______________________ ___________ + | Name | Units | Type | + |--------------|-----------------------|-----------| + | force | in Newtons | float | + |______________|_______________________|___________| """ def newtons_second_law_of_motion(mass: float, acceleration: float) -> float: """ + Calculates force from `mass` and `acceleration` + >>> newtons_second_law_of_motion(10, 10) 100 >>> newtons_second_law_of_motion(2.0, 1) From c93288389d220297f972137293f4565c62131516 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 29 Dec 2024 18:16:45 +0100 Subject: [PATCH 718/757] [pre-commit.ci] pre-commit autoupdate (#12466) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/astral-sh/ruff-pre-commit: v0.8.3 → v0.8.4](https://github.com/astral-sh/ruff-pre-commit/compare/v0.8.3...v0.8.4) - [github.com/pre-commit/mirrors-mypy: v1.13.0 → v1.14.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.13.0...v1.14.0) * Update convert_number_to_words.py * Update convert_number_to_words.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- .pre-commit-config.yaml | 4 ++-- conversions/convert_number_to_words.py | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0c8108ac55be..71ac72c29b5f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.8.3 + rev: v0.8.4 hooks: - id: ruff - id: ruff-format @@ -47,7 +47,7 @@ repos: - id: validate-pyproject - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.13.0 + rev: v1.14.0 hooks: - id: mypy args: diff --git a/conversions/convert_number_to_words.py b/conversions/convert_number_to_words.py index dbab44c72e1f..6aa43738b9fe 100644 --- a/conversions/convert_number_to_words.py +++ b/conversions/convert_number_to_words.py @@ -1,5 +1,5 @@ from enum import Enum -from typing import ClassVar, Literal +from typing import Literal class NumberingSystem(Enum): @@ -54,7 +54,7 @@ def max_value(cls, system: str) -> int: class NumberWords(Enum): - ONES: ClassVar[dict[int, str]] = { + ONES = { # noqa: RUF012 0: "", 1: "one", 2: "two", @@ -67,7 +67,7 @@ class NumberWords(Enum): 9: "nine", } - TEENS: ClassVar[dict[int, str]] = { + TEENS = { # noqa: RUF012 0: "ten", 1: "eleven", 2: "twelve", @@ -80,7 +80,7 @@ class NumberWords(Enum): 9: "nineteen", } - TENS: ClassVar[dict[int, str]] = { + TENS = { # noqa: RUF012 2: "twenty", 3: "thirty", 4: "forty", From bfb0447efb73dd049c6a56331cea36cb1345686b Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Sun, 29 Dec 2024 20:29:48 +0300 Subject: [PATCH 719/757] Fix sphinx/build_docs warnings for maths/zellers_congruence (#12481) * Fix sphinx/build_docs warnings for maths/zellers_congruence * Fix --- maths/zellers_congruence.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/maths/zellers_congruence.py b/maths/zellers_congruence.py index 483fb000f86b..b958ed3b8659 100644 --- a/maths/zellers_congruence.py +++ b/maths/zellers_congruence.py @@ -4,13 +4,14 @@ def zeller(date_input: str) -> str: """ - Zellers Congruence Algorithm - Find the day of the week for nearly any Gregorian or Julian calendar date + | Zellers Congruence Algorithm + | Find the day of the week for nearly any Gregorian or Julian calendar date >>> zeller('01-31-2010') 'Your date 01-31-2010, is a Sunday!' - Validate out of range month + Validate out of range month: + >>> zeller('13-31-2010') Traceback (most recent call last): ... @@ -21,6 +22,7 @@ def zeller(date_input: str) -> str: ValueError: invalid literal for int() with base 10: '.2' Validate out of range date: + >>> zeller('01-33-2010') Traceback (most recent call last): ... @@ -31,30 +33,35 @@ def zeller(date_input: str) -> str: ValueError: invalid literal for int() with base 10: '.4' Validate second separator: + >>> zeller('01-31*2010') Traceback (most recent call last): ... ValueError: Date separator must be '-' or '/' Validate first separator: + >>> zeller('01^31-2010') Traceback (most recent call last): ... ValueError: Date separator must be '-' or '/' Validate out of range year: + >>> zeller('01-31-8999') Traceback (most recent call last): ... ValueError: Year out of range. There has to be some sort of limit...right? Test null input: + >>> zeller() Traceback (most recent call last): ... TypeError: zeller() missing 1 required positional argument: 'date_input' - Test length of date_input: + Test length of `date_input`: + >>> zeller('') Traceback (most recent call last): ... From ce036db2131626b86b94ab87854c82a9bc6c3d0e Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Sun, 29 Dec 2024 23:01:15 +0300 Subject: [PATCH 720/757] Fix sphinx/build_docs warnings for physics/speeds_of_gas_molecules (#12471) * Fix sphinx/build_docs warnings for physics/speeds_of_gas_molecules * Fix * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix * Fix review issue * Fix * Fix * Fix --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- physics/speeds_of_gas_molecules.py | 36 ++++++++++++++++-------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/physics/speeds_of_gas_molecules.py b/physics/speeds_of_gas_molecules.py index a50d1c0f6d76..42f90a9fd6f3 100644 --- a/physics/speeds_of_gas_molecules.py +++ b/physics/speeds_of_gas_molecules.py @@ -4,43 +4,43 @@ distribution is a probability distribution that describes the distribution of speeds of particles in an ideal gas. -The distribution is given by the following equation: +The distribution is given by the following equation:: ------------------------------------------------- | f(v) = (M/2πRT)^(3/2) * 4πv^2 * e^(-Mv^2/2RT) | ------------------------------------------------- where: - f(v) is the fraction of molecules with a speed v - M is the molar mass of the gas in kg/mol - R is the gas constant - T is the absolute temperature + * ``f(v)`` is the fraction of molecules with a speed ``v`` + * ``M`` is the molar mass of the gas in kg/mol + * ``R`` is the gas constant + * ``T`` is the absolute temperature More information about the Maxwell-Boltzmann distribution can be found here: https://en.wikipedia.org/wiki/Maxwell%E2%80%93Boltzmann_distribution The average speed can be calculated by integrating the Maxwell-Boltzmann distribution -from 0 to infinity and dividing by the total number of molecules. The result is: +from 0 to infinity and dividing by the total number of molecules. The result is:: - --------------------- - | vavg = √(8RT/πM) | - --------------------- + ---------------------- + | v_avg = √(8RT/πM) | + ---------------------- The most probable speed is the speed at which the Maxwell-Boltzmann distribution is at its maximum. This can be found by differentiating the Maxwell-Boltzmann -distribution with respect to v and setting the result equal to zero. The result is: +distribution with respect to ``v`` and setting the result equal to zero. The result is:: - --------------------- - | vmp = √(2RT/M) | - --------------------- + ---------------------- + | v_mp = √(2RT/M) | + ---------------------- The root-mean-square speed is another measure of the average speed of the molecules in a gas. It is calculated by taking the square root -of the average of the squares of the speeds of the molecules. The result is: +of the average of the squares of the speeds of the molecules. The result is:: - --------------------- - | vrms = √(3RT/M) | - --------------------- + ---------------------- + | v_rms = √(3RT/M) | + ---------------------- Here we have defined functions to calculate the average and most probable speeds of molecules in a gas given the @@ -57,6 +57,7 @@ def avg_speed_of_molecule(temperature: float, molar_mass: float) -> float: and returns the average speed of a molecule in the gas (in m/s). Examples: + >>> avg_speed_of_molecule(273, 0.028) # nitrogen at 273 K 454.3488755020387 >>> avg_speed_of_molecule(300, 0.032) # oxygen at 300 K @@ -84,6 +85,7 @@ def mps_speed_of_molecule(temperature: float, molar_mass: float) -> float: and returns the most probable speed of a molecule in the gas (in m/s). Examples: + >>> mps_speed_of_molecule(273, 0.028) # nitrogen at 273 K 402.65620701908966 >>> mps_speed_of_molecule(300, 0.032) # oxygen at 300 K From 3622e940c9db74ebac06a5b12f83fd638d7c5511 Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Sun, 29 Dec 2024 23:31:53 +0300 Subject: [PATCH 721/757] Fix sphinx/build_docs warnings for other (#12482) * Fix sphinx/build_docs warnings for other * Fix * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- other/bankers_algorithm.py | 16 +++-- other/davis_putnam_logemann_loveland.py | 94 ++++++++++++++----------- other/scoring_algorithm.py | 30 ++++---- 3 files changed, 77 insertions(+), 63 deletions(-) diff --git a/other/bankers_algorithm.py b/other/bankers_algorithm.py index d4254f479a4f..b1da851fc0f3 100644 --- a/other/bankers_algorithm.py +++ b/other/bankers_algorithm.py @@ -10,9 +10,10 @@ predetermined maximum possible amounts of all resources, and then makes a "s-state" check to test for possible deadlock conditions for all other pending activities, before deciding whether allocation should be allowed to continue. -[Source] Wikipedia -[Credit] Rosetta Code C implementation helped very much. - (https://rosettacode.org/wiki/Banker%27s_algorithm) + +| [Source] Wikipedia +| [Credit] Rosetta Code C implementation helped very much. +| (https://rosettacode.org/wiki/Banker%27s_algorithm) """ from __future__ import annotations @@ -75,7 +76,7 @@ def __available_resources(self) -> 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] + ``max_claim[i][j] - alloc_table[i][j] <= avail[j]`` """ return [ list(np.array(self.__maximum_claim_table[i]) - np.array(allocated_resource)) @@ -86,7 +87,9 @@ 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" - Return: {0: [a: int, b: int], 1: [c: int, d: int]} + + :Return: {0: [a: int, b: int], 1: [c: int, d: int]} + >>> index_control = BankersAlgorithm( ... test_claim_vector, test_allocated_res_table, test_maximum_claim_table ... )._BankersAlgorithm__need_index_manager() @@ -100,7 +103,8 @@ def __need_index_manager(self) -> dict[int, list[int]]: def main(self, **kwargs) -> None: """ Utilize various methods in this class to simulate the Banker's algorithm - Return: None + :Return: None + >>> BankersAlgorithm(test_claim_vector, test_allocated_res_table, ... test_maximum_claim_table).main(describe=True) Allocated Resource Table diff --git a/other/davis_putnam_logemann_loveland.py b/other/davis_putnam_logemann_loveland.py index 0f3100b1bc2e..e95bf371a817 100644 --- a/other/davis_putnam_logemann_loveland.py +++ b/other/davis_putnam_logemann_loveland.py @@ -17,13 +17,15 @@ class Clause: """ - A clause represented in Conjunctive Normal Form. - A clause is a set of literals, either complemented or otherwise. + | A clause represented in Conjunctive Normal Form. + | A clause is a set of literals, either complemented or otherwise. + For example: - {A1, A2, A3'} is the clause (A1 v A2 v A3') - {A5', A2', A1} is the clause (A5' v A2' v A1) + * {A1, A2, A3'} is the clause (A1 v A2 v A3') + * {A5', A2', A1} is the clause (A5' v A2' v A1) Create model + >>> clause = Clause(["A1", "A2'", "A3"]) >>> clause.evaluate({"A1": True}) True @@ -39,6 +41,7 @@ def __init__(self, literals: list[str]) -> None: def __str__(self) -> str: """ To print a clause as in Conjunctive Normal Form. + >>> str(Clause(["A1", "A2'", "A3"])) "{A1 , A2' , A3}" """ @@ -47,6 +50,7 @@ def __str__(self) -> str: def __len__(self) -> int: """ To print a clause as in Conjunctive Normal Form. + >>> len(Clause([])) 0 >>> len(Clause(["A1", "A2'", "A3"])) @@ -72,11 +76,13 @@ def assign(self, model: dict[str, bool | None]) -> None: def evaluate(self, model: dict[str, bool | None]) -> bool | None: """ Evaluates the clause with the assignments in model. + This has the following steps: - 1. Return True if both a literal and its complement exist in the clause. - 2. Return True if a single literal has the assignment True. - 3. Return None(unable to complete evaluation) if a literal has no assignment. - 4. Compute disjunction of all values assigned in clause. + 1. Return ``True`` if both a literal and its complement exist in the clause. + 2. Return ``True`` if a single literal has the assignment ``True``. + 3. Return ``None`` (unable to complete evaluation) + if a literal has no assignment. + 4. Compute disjunction of all values assigned in clause. """ for literal in self.literals: symbol = literal.rstrip("'") if literal.endswith("'") else literal + "'" @@ -92,10 +98,10 @@ def evaluate(self, model: dict[str, bool | None]) -> bool | None: class Formula: """ - A formula represented in Conjunctive Normal Form. - A formula is a set of clauses. - For example, - {{A1, A2, A3'}, {A5', A2', A1}} is ((A1 v A2 v A3') and (A5' v A2' v A1)) + | A formula represented in Conjunctive Normal Form. + | A formula is a set of clauses. + | For example, + | {{A1, A2, A3'}, {A5', A2', A1}} is ((A1 v A2 v A3') and (A5' v A2' v A1)) """ def __init__(self, clauses: Iterable[Clause]) -> None: @@ -107,7 +113,8 @@ def __init__(self, clauses: Iterable[Clause]) -> None: def __str__(self) -> str: """ To print a formula as in Conjunctive Normal Form. - str(Formula([Clause(["A1", "A2'", "A3"]), Clause(["A5'", "A2'", "A1"])])) + + >>> str(Formula([Clause(["A1", "A2'", "A3"]), Clause(["A5'", "A2'", "A1"])])) "{{A1 , A2' , A3} , {A5' , A2' , A1}}" """ return "{" + " , ".join(str(clause) for clause in self.clauses) + "}" @@ -115,8 +122,8 @@ def __str__(self) -> str: def generate_clause() -> Clause: """ - Randomly generate a clause. - All literals have the name Ax, where x is an integer from 1 to 5. + | Randomly generate a clause. + | All literals have the name Ax, where x is an integer from ``1`` to ``5``. """ literals = [] no_of_literals = random.randint(1, 5) @@ -149,11 +156,12 @@ def generate_formula() -> Formula: def generate_parameters(formula: Formula) -> tuple[list[Clause], list[str]]: """ - Return the clauses and symbols from a formula. - A symbol is the uncomplemented form of a literal. + | Return the clauses and symbols from a formula. + | A symbol is the uncomplemented form of a literal. + For example, - Symbol of A3 is A3. - Symbol of A5' is A5. + * Symbol of A3 is A3. + * Symbol of A5' is A5. >>> formula = Formula([Clause(["A1", "A2'", "A3"]), Clause(["A5'", "A2'", "A1"])]) >>> clauses, symbols = generate_parameters(formula) @@ -177,21 +185,20 @@ def find_pure_symbols( clauses: list[Clause], symbols: list[str], model: dict[str, bool | None] ) -> tuple[list[str], dict[str, bool | None]]: """ - Return pure symbols and their values to satisfy clause. - Pure symbols are symbols in a formula that exist only - in one form, either complemented or otherwise. - For example, - { { A4 , A3 , A5' , A1 , A3' } , { A4 } , { A3 } } has - pure symbols A4, A5' and A1. + | Return pure symbols and their values to satisfy clause. + | Pure symbols are symbols in a formula that exist only in one form, + | either complemented or otherwise. + | For example, + | {{A4 , A3 , A5' , A1 , A3'} , {A4} , {A3}} has pure symbols A4, A5' and A1. + This has the following steps: - 1. Ignore clauses that have already evaluated to be True. - 2. Find symbols that occur only in one form in the rest of the clauses. - 3. Assign value True or False depending on whether the symbols occurs - in normal or complemented form respectively. + 1. Ignore clauses that have already evaluated to be ``True``. + 2. Find symbols that occur only in one form in the rest of the clauses. + 3. Assign value ``True`` or ``False`` depending on whether the symbols occurs + in normal or complemented form respectively. >>> formula = Formula([Clause(["A1", "A2'", "A3"]), Clause(["A5'", "A2'", "A1"])]) >>> clauses, symbols = generate_parameters(formula) - >>> pure_symbols, values = find_pure_symbols(clauses, symbols, {}) >>> pure_symbols ['A1', 'A2', 'A3', 'A5'] @@ -231,20 +238,21 @@ def find_unit_clauses( ) -> tuple[list[str], dict[str, bool | None]]: """ Returns the unit symbols and their values to satisfy clause. + Unit symbols are symbols in a formula that are: - - Either the only symbol in a clause - - Or all other literals in that clause have been assigned False + - Either the only symbol in a clause + - Or all other literals in that clause have been assigned ``False`` + This has the following steps: - 1. Find symbols that are the only occurrences in a clause. - 2. Find symbols in a clause where all other literals are assigned False. - 3. Assign True or False depending on whether the symbols occurs in - normal or complemented form respectively. + 1. Find symbols that are the only occurrences in a clause. + 2. Find symbols in a clause where all other literals are assigned ``False``. + 3. Assign ``True`` or ``False`` depending on whether the symbols occurs in + normal or complemented form respectively. >>> clause1 = Clause(["A4", "A3", "A5'", "A1", "A3'"]) >>> clause2 = Clause(["A4"]) >>> clause3 = Clause(["A3"]) >>> clauses, symbols = generate_parameters(Formula([clause1, clause2, clause3])) - >>> unit_clauses, values = find_unit_clauses(clauses, {}) >>> unit_clauses ['A4', 'A3'] @@ -278,16 +286,16 @@ def dpll_algorithm( clauses: list[Clause], symbols: list[str], model: dict[str, bool | None] ) -> tuple[bool | None, dict[str, bool | None] | None]: """ - Returns the model if the formula is satisfiable, else None + Returns the model if the formula is satisfiable, else ``None`` + This has the following steps: - 1. If every clause in clauses is True, return True. - 2. If some clause in clauses is False, return False. - 3. Find pure symbols. - 4. Find unit symbols. + 1. If every clause in clauses is ``True``, return ``True``. + 2. If some clause in clauses is ``False``, return ``False``. + 3. Find pure symbols. + 4. Find unit symbols. >>> formula = Formula([Clause(["A4", "A3", "A5'", "A1", "A3'"]), Clause(["A4"])]) >>> clauses, symbols = generate_parameters(formula) - >>> soln, model = dpll_algorithm(clauses, symbols, {}) >>> soln True diff --git a/other/scoring_algorithm.py b/other/scoring_algorithm.py index af04f432e433..0185d7a2e0c0 100644 --- a/other/scoring_algorithm.py +++ b/other/scoring_algorithm.py @@ -1,25 +1,26 @@ """ -developed by: markmelnic -original repo: https://github.com/markmelnic/Scoring-Algorithm +| developed by: markmelnic +| original repo: https://github.com/markmelnic/Scoring-Algorithm Analyse data using a range based percentual proximity algorithm and calculate the linear maximum likelihood estimation. The basic principle is that all values supplied will be broken -down to a range from 0 to 1 and each column's score will be added +down to a range from ``0`` to ``1`` and each column's score will be added up to get the total score. -========== Example for data of vehicles -price|mileage|registration_year -20k |60k |2012 -22k |50k |2011 -23k |90k |2015 -16k |210k |2010 +:: + + price|mileage|registration_year + 20k |60k |2012 + 22k |50k |2011 + 23k |90k |2015 + 16k |210k |2010 We want the vehicle with the lowest price, lowest mileage but newest registration year. Thus the weights for each column are as follows: -[0, 0, 1] +``[0, 0, 1]`` """ @@ -97,10 +98,11 @@ def procentual_proximity( source_data: list[list[float]], weights: list[int] ) -> list[list[float]]: """ - weights - int list - possible values - 0 / 1 - 0 if lower values have higher weight in the data set - 1 if higher values have higher weight in the data set + | `weights` - ``int`` list + | possible values - ``0`` / ``1`` + + * ``0`` if lower values have higher weight in the data set + * ``1`` if higher values have higher weight in the data set >>> procentual_proximity([[20, 60, 2012],[23, 90, 2015],[22, 50, 2011]], [0, 0, 1]) [[20, 60, 2012, 2.0], [23, 90, 2015, 1.0], [22, 50, 2011, 1.3333333333333335]] From 94b3777936101bcc592fc5ef143ac08ad49195e7 Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Mon, 30 Dec 2024 00:35:34 +0300 Subject: [PATCH 722/757] Fix sphinx/build_docs warnings for linear_algebra (#12483) * Fix sphinx/build_docs warnings for linear_algebra/ * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- linear_algebra/gaussian_elimination.py | 28 +++++++++++++------ linear_algebra/lu_decomposition.py | 20 +++++++------ .../src/gaussian_elimination_pivoting.py | 7 +++-- linear_algebra/src/rank_of_matrix.py | 6 +++- linear_algebra/src/schur_complement.py | 13 +++++---- linear_algebra/src/transformations_2d.py | 14 ++++++---- 6 files changed, 54 insertions(+), 34 deletions(-) diff --git a/linear_algebra/gaussian_elimination.py b/linear_algebra/gaussian_elimination.py index 724773c0db98..6f4075b710fd 100644 --- a/linear_algebra/gaussian_elimination.py +++ b/linear_algebra/gaussian_elimination.py @@ -1,6 +1,6 @@ """ -Gaussian elimination method for solving a system of linear equations. -Gaussian elimination - https://en.wikipedia.org/wiki/Gaussian_elimination +| Gaussian elimination method for solving a system of linear equations. +| Gaussian elimination - https://en.wikipedia.org/wiki/Gaussian_elimination """ import numpy as np @@ -13,12 +13,17 @@ def retroactive_resolution( ) -> NDArray[float64]: """ This function performs a retroactive linear system resolution - for triangular matrix + for triangular matrix Examples: - 2x1 + 2x2 - 1x3 = 5 2x1 + 2x2 = -1 - 0x1 - 2x2 - 1x3 = -7 0x1 - 2x2 = -1 - 0x1 + 0x2 + 5x3 = 15 + 1. + * 2x1 + 2x2 - 1x3 = 5 + * 0x1 - 2x2 - 1x3 = -7 + * 0x1 + 0x2 + 5x3 = 15 + 2. + * 2x1 + 2x2 = -1 + * 0x1 - 2x2 = -1 + >>> gaussian_elimination([[2, 2, -1], [0, -2, -1], [0, 0, 5]], [[5], [-7], [15]]) array([[2.], [2.], @@ -45,9 +50,14 @@ def gaussian_elimination( This function performs Gaussian elimination method Examples: - 1x1 - 4x2 - 2x3 = -2 1x1 + 2x2 = 5 - 5x1 + 2x2 - 2x3 = -3 5x1 + 2x2 = 5 - 1x1 - 1x2 + 0x3 = 4 + 1. + * 1x1 - 4x2 - 2x3 = -2 + * 5x1 + 2x2 - 2x3 = -3 + * 1x1 - 1x2 + 0x3 = 4 + 2. + * 1x1 + 2x2 = 5 + * 5x1 + 2x2 = 5 + >>> gaussian_elimination([[1, -4, -2], [5, 2, -2], [1, -1, 0]], [[-2], [-3], [4]]) array([[ 2.3 ], [-1.7 ], diff --git a/linear_algebra/lu_decomposition.py b/linear_algebra/lu_decomposition.py index 3620674835cd..3d89b53a48fb 100644 --- a/linear_algebra/lu_decomposition.py +++ b/linear_algebra/lu_decomposition.py @@ -2,13 +2,14 @@ Lower-upper (LU) decomposition factors a matrix as a product of a lower triangular matrix and an upper triangular matrix. A square matrix has an LU decomposition under the following conditions: + - If the matrix is invertible, then it has an LU decomposition if and only - if all of its leading principal minors are non-zero (see - https://en.wikipedia.org/wiki/Minor_(linear_algebra) for an explanation of - leading principal minors of a matrix). + if all of its leading principal minors are non-zero (see + https://en.wikipedia.org/wiki/Minor_(linear_algebra) for an explanation of + leading principal minors of a matrix). - If the matrix is singular (i.e., not invertible) and it has a rank of k - (i.e., it has k linearly independent columns), then it has an LU - decomposition if its first k leading principal minors are non-zero. + (i.e., it has k linearly independent columns), then it has an LU + decomposition if its first k leading principal minors are non-zero. This algorithm will simply attempt to perform LU decomposition on any square matrix and raise an error if no such decomposition exists. @@ -25,6 +26,7 @@ def lower_upper_decomposition(table: np.ndarray) -> tuple[np.ndarray, np.ndarray """ Perform LU decomposition on a given matrix and raises an error if the matrix isn't square or if no such decomposition exists + >>> matrix = np.array([[2, -2, 1], [0, 1, 2], [5, 3, 1]]) >>> lower_mat, upper_mat = lower_upper_decomposition(matrix) >>> lower_mat @@ -45,7 +47,7 @@ def lower_upper_decomposition(table: np.ndarray) -> tuple[np.ndarray, np.ndarray array([[ 4. , 3. ], [ 0. , -1.5]]) - # Matrix is not square + >>> # Matrix is not square >>> matrix = np.array([[2, -2, 1], [0, 1, 2]]) >>> lower_mat, upper_mat = lower_upper_decomposition(matrix) Traceback (most recent call last): @@ -54,14 +56,14 @@ def lower_upper_decomposition(table: np.ndarray) -> tuple[np.ndarray, np.ndarray [[ 2 -2 1] [ 0 1 2]] - # Matrix is invertible, but its first leading principal minor is 0 + >>> # Matrix is invertible, but its first leading principal minor is 0 >>> matrix = np.array([[0, 1], [1, 0]]) >>> lower_mat, upper_mat = lower_upper_decomposition(matrix) Traceback (most recent call last): ... ArithmeticError: No LU decomposition exists - # Matrix is singular, but its first leading principal minor is 1 + >>> # Matrix is singular, but its first leading principal minor is 1 >>> matrix = np.array([[1, 0], [1, 0]]) >>> lower_mat, upper_mat = lower_upper_decomposition(matrix) >>> lower_mat @@ -71,7 +73,7 @@ def lower_upper_decomposition(table: np.ndarray) -> tuple[np.ndarray, np.ndarray array([[1., 0.], [0., 0.]]) - # Matrix is singular, but its first leading principal minor is 0 + >>> # Matrix is singular, but its first leading principal minor is 0 >>> matrix = np.array([[0, 1], [0, 1]]) >>> lower_mat, upper_mat = lower_upper_decomposition(matrix) Traceback (most recent call last): diff --git a/linear_algebra/src/gaussian_elimination_pivoting.py b/linear_algebra/src/gaussian_elimination_pivoting.py index efc1ddd64a2e..540f57b0cff6 100644 --- a/linear_algebra/src/gaussian_elimination_pivoting.py +++ b/linear_algebra/src/gaussian_elimination_pivoting.py @@ -6,17 +6,18 @@ def solve_linear_system(matrix: np.ndarray) -> np.ndarray: Solve a linear system of equations using Gaussian elimination with partial pivoting Args: - - matrix: Coefficient matrix with the last column representing the constants. + - `matrix`: Coefficient matrix with the last column representing the constants. Returns: - - Solution vector. + - Solution vector. Raises: - - ValueError: If the matrix is not correct (i.e., singular). + - ``ValueError``: If the matrix is not correct (i.e., singular). https://courses.engr.illinois.edu/cs357/su2013/lect.htm Lecture 7 Example: + >>> A = np.array([[2, 1, -1], [-3, -1, 2], [-2, 1, 2]], dtype=float) >>> B = np.array([8, -11, -3], dtype=float) >>> solution = solve_linear_system(np.column_stack((A, B))) diff --git a/linear_algebra/src/rank_of_matrix.py b/linear_algebra/src/rank_of_matrix.py index 7ff3c1699a69..2c4fe2a8d1da 100644 --- a/linear_algebra/src/rank_of_matrix.py +++ b/linear_algebra/src/rank_of_matrix.py @@ -8,11 +8,15 @@ def rank_of_matrix(matrix: list[list[int | float]]) -> int: """ Finds the rank of a matrix. + Args: - matrix: The matrix as a list of lists. + `matrix`: The matrix as a list of lists. + Returns: The rank of the matrix. + Example: + >>> matrix1 = [[1, 2, 3], ... [4, 5, 6], ... [7, 8, 9]] diff --git a/linear_algebra/src/schur_complement.py b/linear_algebra/src/schur_complement.py index 7c79bb70abfc..74ac75e3fce2 100644 --- a/linear_algebra/src/schur_complement.py +++ b/linear_algebra/src/schur_complement.py @@ -12,13 +12,14 @@ def schur_complement( ) -> np.ndarray: """ Schur complement of a symmetric matrix X given as a 2x2 block matrix - consisting of matrices A, B and C. - Matrix A must be quadratic and non-singular. - In case A is singular, a pseudo-inverse may be provided using - the pseudo_inv argument. + consisting of matrices `A`, `B` and `C`. + Matrix `A` must be quadratic and non-singular. + In case `A` is singular, a pseudo-inverse may be provided using + the `pseudo_inv` argument. + + | Link to Wiki: https://en.wikipedia.org/wiki/Schur_complement + | See also Convex Optimization - Boyd and Vandenberghe, A.5.5 - Link to Wiki: https://en.wikipedia.org/wiki/Schur_complement - See also Convex Optimization - Boyd and Vandenberghe, A.5.5 >>> import numpy as np >>> a = np.array([[1, 2], [2, 1]]) >>> b = np.array([[0, 3], [3, 0]]) diff --git a/linear_algebra/src/transformations_2d.py b/linear_algebra/src/transformations_2d.py index b4185cd2848f..5dee59024752 100644 --- a/linear_algebra/src/transformations_2d.py +++ b/linear_algebra/src/transformations_2d.py @@ -3,13 +3,15 @@ I have added the codes for reflection, projection, scaling and rotation 2D matrices. +.. code-block:: python + scaling(5) = [[5.0, 0.0], [0.0, 5.0]] - rotation(45) = [[0.5253219888177297, -0.8509035245341184], - [0.8509035245341184, 0.5253219888177297]] -projection(45) = [[0.27596319193541496, 0.446998331800279], - [0.446998331800279, 0.7240368080645851]] -reflection(45) = [[0.05064397763545947, 0.893996663600558], - [0.893996663600558, 0.7018070490682369]] + rotation(45) = [[0.5253219888177297, -0.8509035245341184], + [0.8509035245341184, 0.5253219888177297]] + projection(45) = [[0.27596319193541496, 0.446998331800279], + [0.446998331800279, 0.7240368080645851]] + reflection(45) = [[0.05064397763545947, 0.893996663600558], + [0.893996663600558, 0.7018070490682369]] """ from math import cos, sin From f45e392cf6e94259eca8c47b13cd3ae22bcd901e Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Mon, 30 Dec 2024 12:56:24 +0300 Subject: [PATCH 723/757] Fix sphinx/build_docs warnings for ciphers (#12485) * Fix sphinx/build_docs warnings for ciphers * Fix --- ciphers/autokey.py | 7 +- ciphers/caesar_cipher.py | 77 ++++++++++------- ciphers/decrypt_caesar_with_chi_squared.py | 99 +++++++++++----------- ciphers/enigma_machine2.py | 64 +++++++------- ciphers/rsa_factorization.py | 13 +-- ciphers/simple_keyword_cypher.py | 10 ++- ciphers/trifid_cipher.py | 25 +++--- 7 files changed, 170 insertions(+), 125 deletions(-) diff --git a/ciphers/autokey.py b/ciphers/autokey.py index 05d8c066b139..7751a32d7546 100644 --- a/ciphers/autokey.py +++ b/ciphers/autokey.py @@ -1,5 +1,6 @@ """ https://en.wikipedia.org/wiki/Autokey_cipher + An autokey cipher (also known as the autoclave cipher) is a cipher that incorporates the message (the plaintext) into the key. The key is generated from the message in some automated fashion, @@ -10,8 +11,9 @@ def encrypt(plaintext: str, key: str) -> str: """ - Encrypt a given plaintext (string) and key (string), returning the + Encrypt a given `plaintext` (string) and `key` (string), returning the encrypted ciphertext. + >>> encrypt("hello world", "coffee") 'jsqqs avvwo' >>> encrypt("coffee is good as python", "TheAlgorithms") @@ -74,8 +76,9 @@ def encrypt(plaintext: str, key: str) -> str: def decrypt(ciphertext: str, key: str) -> str: """ - Decrypt a given ciphertext (string) and key (string), returning the decrypted + Decrypt a given `ciphertext` (string) and `key` (string), returning the decrypted ciphertext. + >>> decrypt("jsqqs avvwo", "coffee") 'hello world' >>> decrypt("vvjfpk wj ohvp su ddylsv", "TheAlgorithms") diff --git a/ciphers/caesar_cipher.py b/ciphers/caesar_cipher.py index d19b9a337221..9c096fe8a7da 100644 --- a/ciphers/caesar_cipher.py +++ b/ciphers/caesar_cipher.py @@ -7,24 +7,29 @@ def encrypt(input_string: str, key: int, alphabet: str | None = None) -> str: """ encrypt ======= + Encodes a given string with the caesar cipher and returns the encoded message Parameters: ----------- - * input_string: the plain-text that needs to be encoded - * key: the number of letters to shift the message by + + * `input_string`: the plain-text that needs to be encoded + * `key`: the number of letters to shift the message by Optional: - * alphabet (None): the alphabet used to encode the cipher, if not + + * `alphabet` (``None``): the alphabet used to encode the cipher, if not specified, the standard english alphabet with upper and lowercase letters is used Returns: + * A string containing the encoded cipher-text More on the caesar cipher ========================= + The caesar cipher is named after Julius Caesar who used it when sending secret military messages to his troops. This is a simple substitution cipher where every character in the plain-text is shifted by a certain number known @@ -32,26 +37,28 @@ def encrypt(input_string: str, key: int, alphabet: str | None = None) -> str: Example: Say we have the following message: - "Hello, captain" + ``Hello, captain`` And our alphabet is made up of lower and uppercase letters: - "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + ``abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ`` - And our shift is "2" + And our shift is ``2`` - We can then encode the message, one letter at a time. "H" would become "J", - since "J" is two letters away, and so on. If the shift is ever two large, or + We can then encode the message, one letter at a time. ``H`` would become ``J``, + since ``J`` is two letters away, and so on. If the shift is ever two large, or our letter is at the end of the alphabet, we just start at the beginning - ("Z" would shift to "a" then "b" and so on). + (``Z`` would shift to ``a`` then ``b`` and so on). - Our final message would be "Jgnnq, ecrvckp" + Our final message would be ``Jgnnq, ecrvckp`` Further reading =============== + * https://en.m.wikipedia.org/wiki/Caesar_cipher Doctests ======== + >>> encrypt('The quick brown fox jumps over the lazy dog', 8) 'bpm yCqks jzwEv nwF rCuxA wDmz Bpm tiHG lwo' @@ -85,23 +92,28 @@ def decrypt(input_string: str, key: int, alphabet: str | None = None) -> str: """ decrypt ======= + Decodes a given string of cipher-text and returns the decoded plain-text Parameters: ----------- - * input_string: the cipher-text that needs to be decoded - * key: the number of letters to shift the message backwards by to decode + + * `input_string`: the cipher-text that needs to be decoded + * `key`: the number of letters to shift the message backwards by to decode Optional: - * alphabet (None): the alphabet used to decode the cipher, if not + + * `alphabet` (``None``): the alphabet used to decode the cipher, if not specified, the standard english alphabet with upper and lowercase letters is used Returns: + * A string containing the decoded plain-text More on the caesar cipher ========================= + The caesar cipher is named after Julius Caesar who used it when sending secret military messages to his troops. This is a simple substitution cipher where very character in the plain-text is shifted by a certain number known @@ -110,27 +122,29 @@ def decrypt(input_string: str, key: int, alphabet: str | None = None) -> str: Example: Say we have the following cipher-text: - "Jgnnq, ecrvckp" + ``Jgnnq, ecrvckp`` And our alphabet is made up of lower and uppercase letters: - "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + ``abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ`` - And our shift is "2" + And our shift is ``2`` To decode the message, we would do the same thing as encoding, but in - reverse. The first letter, "J" would become "H" (remember: we are decoding) - because "H" is two letters in reverse (to the left) of "J". We would - continue doing this. A letter like "a" would shift back to the end of - the alphabet, and would become "Z" or "Y" and so on. + reverse. The first letter, ``J`` would become ``H`` (remember: we are decoding) + because ``H`` is two letters in reverse (to the left) of ``J``. We would + continue doing this. A letter like ``a`` would shift back to the end of + the alphabet, and would become ``Z`` or ``Y`` and so on. - Our final message would be "Hello, captain" + Our final message would be ``Hello, captain`` Further reading =============== + * https://en.m.wikipedia.org/wiki/Caesar_cipher Doctests ======== + >>> decrypt('bpm yCqks jzwEv nwF rCuxA wDmz Bpm tiHG lwo', 8) 'The quick brown fox jumps over the lazy dog' @@ -150,41 +164,44 @@ def brute_force(input_string: str, alphabet: str | None = None) -> dict[int, str """ brute_force =========== + Returns all the possible combinations of keys and the decoded strings in the form of a dictionary Parameters: ----------- - * input_string: the cipher-text that needs to be used during brute-force + + * `input_string`: the cipher-text that needs to be used during brute-force Optional: - * alphabet: (None): the alphabet used to decode the cipher, if not + + * `alphabet` (``None``): the alphabet used to decode the cipher, if not specified, the standard english alphabet with upper and lowercase letters is used More about brute force ====================== + Brute force is when a person intercepts a message or password, not knowing the key and tries every single combination. This is easy with the caesar cipher since there are only all the letters in the alphabet. The more complex the cipher, the larger amount of time it will take to do brute force Ex: - Say we have a 5 letter alphabet (abcde), for simplicity and we intercepted the - following message: - - "dbc" - + Say we have a ``5`` letter alphabet (``abcde``), for simplicity and we intercepted + the following message: ``dbc``, we could then just write out every combination: - ecd... and so on, until we reach a combination that makes sense: - "cab" + ``ecd``... and so on, until we reach a combination that makes sense: + ``cab`` Further reading =============== + * https://en.wikipedia.org/wiki/Brute_force Doctests ======== + >>> brute_force("jFyuMy xIH'N vLONy zILwy Gy!")[20] "Please don't brute force me!" diff --git a/ciphers/decrypt_caesar_with_chi_squared.py b/ciphers/decrypt_caesar_with_chi_squared.py index 10832203e531..fb95c0f90628 100644 --- a/ciphers/decrypt_caesar_with_chi_squared.py +++ b/ciphers/decrypt_caesar_with_chi_squared.py @@ -11,33 +11,31 @@ def decrypt_caesar_with_chi_squared( """ Basic Usage =========== + Arguments: - * ciphertext (str): the text to decode (encoded with the caesar cipher) + * `ciphertext` (str): the text to decode (encoded with the caesar cipher) Optional Arguments: - * cipher_alphabet (list): the alphabet used for the cipher (each letter is - a string separated by commas) - * frequencies_dict (dict): a dictionary of word frequencies where keys are - the letters and values are a percentage representation of the frequency as - a decimal/float - * case_sensitive (bool): a boolean value: True if the case matters during - decryption, False if it doesn't + * `cipher_alphabet` (list): the alphabet used for the cipher (each letter is + a string separated by commas) + * `frequencies_dict` (dict): a dictionary of word frequencies where keys are + the letters and values are a percentage representation of the frequency as + a decimal/float + * `case_sensitive` (bool): a boolean value: ``True`` if the case matters during + decryption, ``False`` if it doesn't Returns: - * A tuple in the form of: - ( - most_likely_cipher, - most_likely_cipher_chi_squared_value, - decoded_most_likely_cipher - ) + * A tuple in the form of: + (`most_likely_cipher`, `most_likely_cipher_chi_squared_value`, + `decoded_most_likely_cipher`) - where... - - most_likely_cipher is an integer representing the shift of the smallest - chi-squared statistic (most likely key) - - most_likely_cipher_chi_squared_value is a float representing the - chi-squared statistic of the most likely shift - - decoded_most_likely_cipher is a string with the decoded cipher - (decoded by the most_likely_cipher key) + where... + - `most_likely_cipher` is an integer representing the shift of the smallest + chi-squared statistic (most likely key) + - `most_likely_cipher_chi_squared_value` is a float representing the + chi-squared statistic of the most likely shift + - `decoded_most_likely_cipher` is a string with the decoded cipher + (decoded by the most_likely_cipher key) The Chi-squared test @@ -45,52 +43,57 @@ def decrypt_caesar_with_chi_squared( The caesar cipher ----------------- + The caesar cipher is a very insecure encryption algorithm, however it has been used since Julius Caesar. The cipher is a simple substitution cipher where each character in the plain text is replaced by a character in the alphabet a certain number of characters after the original character. The number of characters away is called the shift or key. For example: - Plain text: hello - Key: 1 - Cipher text: ifmmp - (each letter in hello has been shifted one to the right in the eng. alphabet) + | Plain text: ``hello`` + | Key: ``1`` + | Cipher text: ``ifmmp`` + | (each letter in ``hello`` has been shifted one to the right in the eng. alphabet) As you can imagine, this doesn't provide lots of security. In fact decrypting ciphertext by brute-force is extremely easy even by hand. However - one way to do that is the chi-squared test. + one way to do that is the chi-squared test. The chi-squared test - ------------------- + -------------------- + Each letter in the english alphabet has a frequency, or the amount of times it shows up compared to other letters (usually expressed as a decimal representing the percentage likelihood). The most common letter in the - english language is "e" with a frequency of 0.11162 or 11.162%. The test is - completed in the following fashion. + english language is ``e`` with a frequency of ``0.11162`` or ``11.162%``. + The test is completed in the following fashion. 1. The ciphertext is decoded in a brute force way (every combination of the - 26 possible combinations) + ``26`` possible combinations) 2. For every combination, for each letter in the combination, the average amount of times the letter should appear the message is calculated by - multiplying the total number of characters by the frequency of the letter + multiplying the total number of characters by the frequency of the letter. + + | For example: + | In a message of ``100`` characters, ``e`` should appear around ``11.162`` + times. - For example: - In a message of 100 characters, e should appear around 11.162 times. + 3. Then, to calculate the margin of error (the amount of times the letter + SHOULD appear with the amount of times the letter DOES appear), we use + the chi-squared test. The following formula is used: - 3. Then, to calculate the margin of error (the amount of times the letter - SHOULD appear with the amount of times the letter DOES appear), we use - the chi-squared test. The following formula is used: + Let: + - n be the number of times the letter actually appears + - p be the predicted value of the number of times the letter should + appear (see item ``2``) + - let v be the chi-squared test result (referred to here as chi-squared + value/statistic) - Let: - - n be the number of times the letter actually appears - - p be the predicted value of the number of times the letter should - appear (see #2) - - let v be the chi-squared test result (referred to here as chi-squared - value/statistic) + :: - (n - p)^2 - --------- = v - p + (n - p)^2 + --------- = v + p 4. Each chi squared value for each letter is then added up to the total. The total is the chi-squared statistic for that encryption key. @@ -98,16 +101,16 @@ def decrypt_caesar_with_chi_squared( to be the decoded answer. Further Reading - ================ + =============== - * http://practicalcryptography.com/cryptanalysis/text-characterisation/chi-squared- - statistic/ + * http://practicalcryptography.com/cryptanalysis/text-characterisation/chi-squared-statistic/ * https://en.wikipedia.org/wiki/Letter_frequency * https://en.wikipedia.org/wiki/Chi-squared_test * https://en.m.wikipedia.org/wiki/Caesar_cipher Doctests ======== + >>> decrypt_caesar_with_chi_squared( ... 'dof pz aol jhlzhy jpwoly zv wvwbshy? pa pz avv lhzf av jyhjr!' ... ) # doctest: +NORMALIZE_WHITESPACE diff --git a/ciphers/enigma_machine2.py b/ciphers/enigma_machine2.py index 163aa7172c11..e42fdd82ed41 100644 --- a/ciphers/enigma_machine2.py +++ b/ciphers/enigma_machine2.py @@ -1,14 +1,16 @@ """ -Wikipedia: https://en.wikipedia.org/wiki/Enigma_machine -Video explanation: https://youtu.be/QwQVMqfoB2E -Also check out Numberphile's and Computerphile's videos on this topic +| Wikipedia: https://en.wikipedia.org/wiki/Enigma_machine +| Video explanation: https://youtu.be/QwQVMqfoB2E +| Also check out Numberphile's and Computerphile's videos on this topic -This module contains function 'enigma' which emulates +This module contains function ``enigma`` which emulates the famous Enigma machine from WWII. + Module includes: -- enigma function + +- ``enigma`` function - showcase of function usage -- 9 randomly generated rotors +- ``9`` randomly generated rotors - reflector (aka static rotor) - original alphabet @@ -73,7 +75,7 @@ def _validator( rotpos: RotorPositionT, rotsel: RotorSelectionT, pb: str ) -> tuple[RotorPositionT, RotorSelectionT, dict[str, str]]: """ - Checks if the values can be used for the 'enigma' function + Checks if the values can be used for the ``enigma`` function >>> _validator((1,1,1), (rotor1, rotor2, rotor3), 'POLAND') ((1, 1, 1), ('EGZWVONAHDCLFQMSIPJBYUKXTR', 'FOBHMDKEXQNRAULPGSJVTYICZW', \ @@ -83,7 +85,7 @@ def _validator( :param rotpos: rotor_positon :param rotsel: rotor_selection :param pb: plugb -> validated and transformed - :return: (rotpos, rotsel, pb) + :return: (`rotpos`, `rotsel`, `pb`) """ # Checks if there are 3 unique rotors @@ -118,9 +120,10 @@ def _plugboard(pbstring: str) -> dict[str, str]: >>> _plugboard('POLAND') {'P': 'O', 'O': 'P', 'L': 'A', 'A': 'L', 'N': 'D', 'D': 'N'} - In the code, 'pb' stands for 'plugboard' + In the code, ``pb`` stands for ``plugboard`` Pairs can be separated by spaces + :param pbstring: string containing plugboard setting for the Enigma machine :return: dictionary containing converted pairs """ @@ -168,31 +171,34 @@ def enigma( plugb: str = "", ) -> str: """ - The only difference with real-world enigma is that I allowed string input. + The only difference with real-world enigma is that ``I`` allowed string input. All characters are converted to uppercase. (non-letter symbol are ignored) - How it works: - (for every letter in the message) + + | How it works: + | (for every letter in the message) - Input letter goes into the plugboard. - If it is connected to another one, switch it. + If it is connected to another one, switch it. + + - Letter goes through ``3`` rotors. + Each rotor can be represented as ``2`` sets of symbol, where one is shuffled. + Each symbol from the first set has corresponding symbol in + the second set and vice versa. - - Letter goes through 3 rotors. - Each rotor can be represented as 2 sets of symbol, where one is shuffled. - Each symbol from the first set has corresponding symbol in - the second set and vice versa. + example:: - example: - | ABCDEFGHIJKLMNOPQRSTUVWXYZ | e.g. F=D and D=F - | VKLEPDBGRNWTFCJOHQAMUZYIXS | + | ABCDEFGHIJKLMNOPQRSTUVWXYZ | e.g. F=D and D=F + | VKLEPDBGRNWTFCJOHQAMUZYIXS | - Symbol then goes through reflector (static rotor). - There it is switched with paired symbol - The reflector can be represented as2 sets, each with half of the alphanet. - There are usually 10 pairs of letters. + There it is switched with paired symbol. + The reflector can be represented as ``2`` sets, each with half of the alphanet. + There are usually ``10`` pairs of letters. + + Example:: - Example: - | ABCDEFGHIJKLM | e.g. E is paired to X - | ZYXWVUTSRQPON | so when E goes in X goes out and vice versa + | ABCDEFGHIJKLM | e.g. E is paired to X + | ZYXWVUTSRQPON | so when E goes in X goes out and vice versa - Letter then goes through the rotors again @@ -211,9 +217,9 @@ def enigma( :param text: input message - :param rotor_position: tuple with 3 values in range 1..26 - :param rotor_selection: tuple with 3 rotors () - :param plugb: string containing plugboard configuration (default '') + :param rotor_position: tuple with ``3`` values in range ``1``.. ``26`` + :param rotor_selection: tuple with ``3`` rotors + :param plugb: string containing plugboard configuration (default ``''``) :return: en/decrypted string """ diff --git a/ciphers/rsa_factorization.py b/ciphers/rsa_factorization.py index 0a358a4fc2d4..585b21fac856 100644 --- a/ciphers/rsa_factorization.py +++ b/ciphers/rsa_factorization.py @@ -3,8 +3,10 @@ The program can efficiently factor RSA prime number given the private key d and public key e. -Source: on page 3 of https://crypto.stanford.edu/~dabo/papers/RSA-survey.pdf -More readable source: https://www.di-mgt.com.au/rsa_factorize_n.html + +| Source: on page ``3`` of https://crypto.stanford.edu/~dabo/papers/RSA-survey.pdf +| 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. """ @@ -17,13 +19,14 @@ def rsafactor(d: int, e: int, n: int) -> list[int]: """ This function returns the factors of N, where p*q=N - Return: [p, q] + + Return: [p, q] We call N the RSA modulus, e the encryption exponent, and d the decryption exponent. The pair (N, e) is the public key. As its name suggests, it is public and is used to - encrypt messages. + encrypt messages. The pair (N, d) is the secret key or private key and is known only to the recipient - of encrypted messages. + of encrypted messages. >>> rsafactor(3, 16971, 25777) [149, 173] diff --git a/ciphers/simple_keyword_cypher.py b/ciphers/simple_keyword_cypher.py index 9dc624e7762c..bde137d826c3 100644 --- a/ciphers/simple_keyword_cypher.py +++ b/ciphers/simple_keyword_cypher.py @@ -1,9 +1,11 @@ def remove_duplicates(key: str) -> str: """ Removes duplicate alphabetic characters in a keyword (letter is ignored after its - first appearance). + first appearance). + :param key: Keyword to use :return: String with duplicates removed + >>> remove_duplicates('Hello World!!') 'Helo Wrd' """ @@ -18,6 +20,7 @@ def remove_duplicates(key: str) -> str: def create_cipher_map(key: str) -> dict[str, str]: """ Returns a cipher map given a keyword. + :param key: keyword to use :return: dictionary cipher map """ @@ -43,9 +46,11 @@ def create_cipher_map(key: str) -> dict[str, str]: def encipher(message: str, cipher_map: dict[str, str]) -> str: """ Enciphers a message given a cipher map. + :param message: Message to encipher :param cipher_map: Cipher map :return: enciphered string + >>> encipher('Hello World!!', create_cipher_map('Goodbye!!')) 'CYJJM VMQJB!!' """ @@ -55,9 +60,11 @@ def encipher(message: str, cipher_map: dict[str, str]) -> str: def decipher(message: str, cipher_map: dict[str, str]) -> str: """ Deciphers a message given a cipher map + :param message: Message to decipher :param cipher_map: Dictionary mapping to use :return: Deciphered string + >>> cipher_map = create_cipher_map('Goodbye!!') >>> decipher(encipher('Hello World!!', cipher_map), cipher_map) 'HELLO WORLD!!' @@ -70,6 +77,7 @@ def decipher(message: str, cipher_map: dict[str, str]) -> str: def main() -> None: """ Handles I/O + :return: void """ message = input("Enter message to encode or decode: ").strip() diff --git a/ciphers/trifid_cipher.py b/ciphers/trifid_cipher.py index 16b9faf67688..9613cee0669d 100644 --- a/ciphers/trifid_cipher.py +++ b/ciphers/trifid_cipher.py @@ -22,7 +22,7 @@ def __encrypt_part(message_part: str, character_to_number: dict[str, str]) -> str: """ - Arrange the triagram value of each letter of 'message_part' vertically and join + Arrange the triagram value of each letter of `message_part` vertically and join them horizontally. >>> __encrypt_part('ASK', TEST_CHARACTER_TO_NUMBER) @@ -65,8 +65,8 @@ def __prepare( """ A helper function that generates the triagrams and assigns each letter of the alphabet to its corresponding triagram and stores this in a dictionary - ("character_to_number" and "number_to_character") after confirming if the - alphabet's length is 27. + (`character_to_number` and `number_to_character`) after confirming if the + alphabet's length is ``27``. >>> test = __prepare('I aM a BOy','abCdeFghijkLmnopqrStuVwxYZ+') >>> expected = ('IAMABOY','ABCDEFGHIJKLMNOPQRSTUVWXYZ+', @@ -75,24 +75,28 @@ def __prepare( True Testing with incomplete alphabet + >>> __prepare('I aM a BOy','abCdeFghijkLmnopqrStuVw') Traceback (most recent call last): ... KeyError: 'Length of alphabet has to be 27.' Testing with extra long alphabets + >>> __prepare('I aM a BOy','abCdeFghijkLmnopqrStuVwxyzzwwtyyujjgfd') Traceback (most recent call last): ... KeyError: 'Length of alphabet has to be 27.' Testing with punctuations that are not in the given alphabet + >>> __prepare('am i a boy?','abCdeFghijkLmnopqrStuVwxYZ+') Traceback (most recent call last): ... ValueError: Each message character has to be included in alphabet! Testing with numbers + >>> __prepare(500,'abCdeFghijkLmnopqrStuVwxYZ+') Traceback (most recent call last): ... @@ -130,9 +134,9 @@ def encrypt_message( PARAMETERS ---------- - * message: The message you want to encrypt. - * alphabet (optional): The characters to be used for the cipher . - * period (optional): The number of characters you want in a group whilst + * `message`: The message you want to encrypt. + * `alphabet` (optional): The characters to be used for the cipher . + * `period` (optional): The number of characters you want in a group whilst encrypting. >>> encrypt_message('I am a boy') @@ -169,20 +173,21 @@ def decrypt_message( decrypt_message =============== - Decrypts a trifid_cipher encrypted message . + Decrypts a trifid_cipher encrypted message. PARAMETERS ---------- - * message: The message you want to decrypt . - * alphabet (optional): The characters used for the cipher. - * period (optional): The number of characters used in grouping when it + * `message`: The message you want to decrypt. + * `alphabet` (optional): The characters used for the cipher. + * `period` (optional): The number of characters used in grouping when it was encrypted. >>> decrypt_message('BCDGBQY') 'IAMABOY' Decrypting with your own alphabet and period + >>> decrypt_message('FMJFVOISSUFTFPUFEQQC','FELIXMARDSTBCGHJKNOPQUVWYZ+',5) 'AIDETOILECIELTAIDERA' """ From 68b4c6b4793867126f71ebf2a399402b02472edb Mon Sep 17 00:00:00 2001 From: mahdi tavasoli Date: Mon, 30 Dec 2024 13:52:20 +0330 Subject: [PATCH 724/757] fix is_ip_v4_address_valid.py (#12394) * fix is_ip_v4_address_valid * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update is_ip_v4_address_valid.py --------- Co-authored-by: m.tavasoli Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Maxim Smolskiy --- maths/is_ip_v4_address_valid.py | 37 +++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/maths/is_ip_v4_address_valid.py b/maths/is_ip_v4_address_valid.py index 0ae8e021ead1..305afabffed3 100644 --- a/maths/is_ip_v4_address_valid.py +++ b/maths/is_ip_v4_address_valid.py @@ -1,13 +1,15 @@ """ +wiki: https://en.wikipedia.org/wiki/IPv4 + Is IP v4 address valid? A valid IP address must be four octets in the form of A.B.C.D, -where A,B,C and D are numbers from 0-254 -for example: 192.168.23.1, 172.254.254.254 are valid IP address - 192.168.255.0, 255.192.3.121 are invalid IP address +where A, B, C and D are numbers from 0-255 +for example: 192.168.23.1, 172.255.255.255 are valid IP address + 192.168.256.0, 256.192.3.121 are invalid IP address """ -def is_ip_v4_address_valid(ip_v4_address: str) -> bool: +def is_ip_v4_address_valid(ip: str) -> bool: """ print "Valid IP address" If IP is valid. or @@ -16,13 +18,13 @@ def is_ip_v4_address_valid(ip_v4_address: str) -> bool: >>> is_ip_v4_address_valid("192.168.0.23") True - >>> is_ip_v4_address_valid("192.255.15.8") + >>> is_ip_v4_address_valid("192.256.15.8") False >>> is_ip_v4_address_valid("172.100.0.8") True - >>> is_ip_v4_address_valid("254.255.0.255") + >>> is_ip_v4_address_valid("255.256.0.256") False >>> is_ip_v4_address_valid("1.2.33333333.4") @@ -45,12 +47,29 @@ def is_ip_v4_address_valid(ip_v4_address: str) -> bool: >>> is_ip_v4_address_valid("1.2.3.") False + + >>> is_ip_v4_address_valid("1.2.3.05") + False """ - octets = [int(i) for i in ip_v4_address.split(".") if i.isdigit()] - return len(octets) == 4 and all(0 <= int(octet) <= 254 for octet in octets) + octets = ip.split(".") + if len(octets) != 4: + return False + + for octet in octets: + if not octet.isdigit(): + return False + + number = int(octet) + if len(str(number)) != len(octet): + return False + + if not 0 <= number <= 255: + return False + + return True if __name__ == "__main__": ip = input().strip() valid_or_invalid = "valid" if is_ip_v4_address_valid(ip) else "invalid" - print(f"{ip} is a {valid_or_invalid} IP v4 address.") + print(f"{ip} is a {valid_or_invalid} IPv4 address.") From 2ca96b7c8ec2134a7282bed13f1cc93358c13c45 Mon Sep 17 00:00:00 2001 From: jperezr <122382210+MRJPEREZR@users.noreply.github.com> Date: Mon, 30 Dec 2024 11:37:21 +0100 Subject: [PATCH 725/757] current_stock_price test added (#12390) * adding test to web_programming/current_stock_price * adding test to web_programming/current_stock_price * Update current_stock_price.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update current_stock_price.py --------- Co-authored-by: Maxim Smolskiy Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- web_programming/current_stock_price.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/web_programming/current_stock_price.py b/web_programming/current_stock_price.py index 9567c05b0558..d0a65e9aac84 100644 --- a/web_programming/current_stock_price.py +++ b/web_programming/current_stock_price.py @@ -1,14 +1,30 @@ import requests from bs4 import BeautifulSoup +""" +Get the HTML code of finance yahoo and select the current qsp-price +Current AAPL stock price is 228.43 +Current AMZN stock price is 201.85 +Current IBM stock price is 210.30 +Current GOOG stock price is 177.86 +Current MSFT stock price is 414.82 +Current ORCL stock price is 188.87 +""" + def stock_price(symbol: str = "AAPL") -> str: + """ + >>> stock_price("EEEE") + '-' + >>> isinstance(float(stock_price("GOOG")),float) + True + """ url = f"https://finance.yahoo.com/quote/{symbol}?p={symbol}" yahoo_finance_source = requests.get( url, headers={"USER-AGENT": "Mozilla/5.0"}, timeout=10 ).text soup = BeautifulSoup(yahoo_finance_source, "html.parser") - specific_fin_streamer_tag = soup.find("fin-streamer", {"data-test": "qsp-price"}) + specific_fin_streamer_tag = soup.find("fin-streamer", {"data-testid": "qsp-price"}) if specific_fin_streamer_tag: text = specific_fin_streamer_tag.get_text() @@ -18,5 +34,9 @@ def stock_price(symbol: str = "AAPL") -> str: # Search for the symbol at https://finance.yahoo.com/lookup if __name__ == "__main__": + from doctest import testmod + + testmod() + for symbol in "AAPL AMZN IBM GOOG MSFT ORCL".split(): print(f"Current {symbol:<4} stock price is {stock_price(symbol):>8}") From 24923ee635973a05f7713dd672fea07361fa0466 Mon Sep 17 00:00:00 2001 From: Shikhar Maheshwari <83123897+shikhar-sm@users.noreply.github.com> Date: Mon, 30 Dec 2024 16:21:10 +0530 Subject: [PATCH 726/757] Add doctest to maths/numerical_analysis/intersection.py (#12148) --- maths/numerical_analysis/intersection.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/maths/numerical_analysis/intersection.py b/maths/numerical_analysis/intersection.py index 826c0ead0a00..325abeaca996 100644 --- a/maths/numerical_analysis/intersection.py +++ b/maths/numerical_analysis/intersection.py @@ -42,6 +42,11 @@ def intersection(function: Callable[[float], float], x0: float, x1: float) -> fl def f(x: float) -> float: + """ + function is f(x) = x^3 - 2x - 5 + >>> f(2) + -1.0 + """ return math.pow(x, 3) - (2 * x) - 5 From da587d06ac88e338e7db8f10fa8ca2ae556e7bae Mon Sep 17 00:00:00 2001 From: Sankabapur <152031570+Parthjhalani07@users.noreply.github.com> Date: Mon, 30 Dec 2024 16:29:03 +0530 Subject: [PATCH 727/757] Added doctest to /maths/power_using_recursion.py (#11994) --- maths/power_using_recursion.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/maths/power_using_recursion.py b/maths/power_using_recursion.py index 29283ca0f67c..eb775b161ae8 100644 --- a/maths/power_using_recursion.py +++ b/maths/power_using_recursion.py @@ -38,6 +38,14 @@ def power(base: int, exponent: int) -> float: Traceback (most recent call last): ... RecursionError: maximum recursion depth exceeded + >>> power(0, 0) + 1 + >>> power(0, 1) + 0 + >>> power(5,6) + 15625 + >>> power(23, 12) + 21914624432020321 """ return base * power(base, (exponent - 1)) if exponent else 1 From 493a7c153c1ca1805e60c109842ee1a1ee63cde2 Mon Sep 17 00:00:00 2001 From: Jeel Rupapara Date: Mon, 30 Dec 2024 16:40:44 +0530 Subject: [PATCH 728/757] feat: add testcase of assemble_transformation (#11810) --- strings/min_cost_string_conversion.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/strings/min_cost_string_conversion.py b/strings/min_cost_string_conversion.py index 93791e2a7ed3..87eb5189e16a 100644 --- a/strings/min_cost_string_conversion.py +++ b/strings/min_cost_string_conversion.py @@ -91,6 +91,14 @@ def assemble_transformation(ops: list[list[str]], i: int, j: int) -> list[str]: >>> y1 = len(ops1[0]) - 1 >>> assemble_transformation(ops1, x1, y1) [] + + >>> ops2 = [['0', 'I1', 'I2', 'I3'], + ... ['D1', 'C1', 'I2', 'I3'], + ... ['D2', 'D2', 'R23', 'R23']] + >>> x2 = len(ops2) - 1 + >>> y2 = len(ops2[0]) - 1 + >>> assemble_transformation(ops2, x2, y2) + ['C1', 'I2', 'R23'] """ if i == 0 and j == 0: return [] From 7fa9b4bf1bc9822517bb0046aebc2e8b2997d3e1 Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Mon, 30 Dec 2024 14:52:03 +0300 Subject: [PATCH 729/757] Fix sphinx/build_docs warnings for dynamic_programming (#12484) * Fix sphinx/build_docs warnings for dynamic_programming * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix * Fix * Fix * Fix * Fix * Fix --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- dynamic_programming/all_construct.py | 7 +- dynamic_programming/combination_sum_iv.py | 23 +- dynamic_programming/fizz_buzz.py | 11 +- dynamic_programming/knapsack.py | 40 ++-- .../longest_common_substring.py | 14 +- .../longest_increasing_subsequence.py | 13 +- .../matrix_chain_multiplication.py | 89 ++++---- dynamic_programming/max_product_subarray.py | 3 +- .../minimum_squares_to_represent_a_number.py | 1 + dynamic_programming/regex_match.py | 22 +- dynamic_programming/rod_cutting.py | 83 ++++--- dynamic_programming/subset_generation.py | 63 +++--- dynamic_programming/viterbi.py | 212 ++++++++---------- 13 files changed, 295 insertions(+), 286 deletions(-) diff --git a/dynamic_programming/all_construct.py b/dynamic_programming/all_construct.py index 5d585fc7fcec..ca00f2beb06a 100644 --- a/dynamic_programming/all_construct.py +++ b/dynamic_programming/all_construct.py @@ -8,9 +8,10 @@ def all_construct(target: str, word_bank: list[str] | None = None) -> list[list[str]]: """ - returns the list containing all the possible - combinations a string(target) can be constructed from - the given list of substrings(word_bank) + returns the list containing all the possible + combinations a string(`target`) can be constructed from + the given list of substrings(`word_bank`) + >>> all_construct("hello", ["he", "l", "o"]) [['he', 'l', 'l', 'o']] >>> all_construct("purple",["purp","p","ur","le","purpl"]) diff --git a/dynamic_programming/combination_sum_iv.py b/dynamic_programming/combination_sum_iv.py index 113c06a27a9e..ed8dcd88e6fd 100644 --- a/dynamic_programming/combination_sum_iv.py +++ b/dynamic_programming/combination_sum_iv.py @@ -1,24 +1,25 @@ """ Question: -You are given an array of distinct integers and you have to tell how many -different ways of selecting the elements from the array are there such that -the sum of chosen elements is equal to the target number tar. + You are given an array of distinct integers and you have to tell how many + different ways of selecting the elements from the array are there such that + the sum of chosen elements is equal to the target number tar. Example Input: -N = 3 -target = 5 -array = [1, 2, 5] + * N = 3 + * target = 5 + * array = [1, 2, 5] Output: -9 + 9 Approach: -The basic idea is to go over recursively to find the way such that the sum -of chosen elements is “tar”. For every element, we have two choices - 1. Include the element in our set of chosen elements. - 2. Don't include the element in our set of chosen elements. + The basic idea is to go over recursively to find the way such that the sum + of chosen elements is `target`. For every element, we have two choices + + 1. Include the element in our set of chosen elements. + 2. Don't include the element in our set of chosen elements. """ diff --git a/dynamic_programming/fizz_buzz.py b/dynamic_programming/fizz_buzz.py index e29116437a93..0cb48897875b 100644 --- a/dynamic_programming/fizz_buzz.py +++ b/dynamic_programming/fizz_buzz.py @@ -3,11 +3,12 @@ def fizz_buzz(number: int, iterations: int) -> str: """ - Plays FizzBuzz. - Prints Fizz if number is a multiple of 3. - Prints Buzz if its a multiple of 5. - Prints FizzBuzz if its a multiple of both 3 and 5 or 15. - Else Prints The Number Itself. + | Plays FizzBuzz. + | Prints Fizz if number is a multiple of ``3``. + | Prints Buzz if its a multiple of ``5``. + | Prints FizzBuzz if its a multiple of both ``3`` and ``5`` or ``15``. + | Else Prints The Number Itself. + >>> fizz_buzz(1,7) '1 2 Fizz 4 Buzz Fizz 7 ' >>> fizz_buzz(1,0) diff --git a/dynamic_programming/knapsack.py b/dynamic_programming/knapsack.py index 489b5ada450a..28c5b19dbe36 100644 --- a/dynamic_programming/knapsack.py +++ b/dynamic_programming/knapsack.py @@ -11,7 +11,7 @@ def mf_knapsack(i, wt, val, j): """ This code involves the concept of memory functions. Here we solve the subproblems which are needed unlike the below example - F is a 2D array with -1s filled up + F is a 2D array with ``-1`` s filled up """ global f # a global dp table for knapsack if f[i][j] < 0: @@ -45,22 +45,24 @@ def knapsack_with_example_solution(w: int, wt: list, val: list): the several possible optimal subsets. Parameters - --------- + ---------- - W: int, the total maximum weight for the given knapsack problem. - wt: list, the vector of weights for all items where wt[i] is the weight - of the i-th item. - val: list, the vector of values for all items where val[i] is the value - of the i-th item + * `w`: int, the total maximum weight for the given knapsack problem. + * `wt`: list, the vector of weights for all items where ``wt[i]`` is the weight + of the ``i``-th item. + * `val`: list, the vector of values for all items where ``val[i]`` is the value + of the ``i``-th item Returns ------- - optimal_val: float, the optimal value for the given knapsack problem - example_optional_set: set, the indices of one of the optimal subsets - which gave rise to the optimal value. + + * `optimal_val`: float, the optimal value for the given knapsack problem + * `example_optional_set`: set, the indices of one of the optimal subsets + which gave rise to the optimal value. Examples - ------- + -------- + >>> knapsack_with_example_solution(10, [1, 3, 5, 2], [10, 20, 100, 22]) (142, {2, 3, 4}) >>> knapsack_with_example_solution(6, [4, 3, 2, 3], [3, 2, 4, 4]) @@ -104,19 +106,19 @@ def _construct_solution(dp: list, wt: list, i: int, j: int, optimal_set: set): a filled DP table and the vector of weights Parameters - --------- - - dp: list of list, the table of a solved integer weight dynamic programming problem + ---------- - wt: list or tuple, the vector of weights of the items - i: int, the index of the item under consideration - j: int, the current possible maximum weight - optimal_set: set, the optimal subset so far. This gets modified by the function. + * `dp`: list of list, the table of a solved integer weight dynamic programming + problem + * `wt`: list or tuple, the vector of weights of the items + * `i`: int, the index of the item under consideration + * `j`: int, the current possible maximum weight + * `optimal_set`: set, the optimal subset so far. This gets modified by the function. Returns ------- - None + ``None`` """ # for the current item i at a maximum weight j to be part of an optimal subset, # the optimal value at (i, j) must be greater than the optimal value at (i-1, j). diff --git a/dynamic_programming/longest_common_substring.py b/dynamic_programming/longest_common_substring.py index e2f944a5e336..ea5233eb2d17 100644 --- a/dynamic_programming/longest_common_substring.py +++ b/dynamic_programming/longest_common_substring.py @@ -1,15 +1,19 @@ """ -Longest Common Substring Problem Statement: Given two sequences, find the -longest common substring present in both of them. A substring is -necessarily continuous. -Example: "abcdef" and "xabded" have two longest common substrings, "ab" or "de". -Therefore, algorithm should return any one of them. +Longest Common Substring Problem Statement: + Given two sequences, find the + longest common substring present in both of them. A substring is + necessarily continuous. + +Example: + ``abcdef`` and ``xabded`` have two longest common substrings, ``ab`` or ``de``. + Therefore, algorithm should return any one of them. """ def longest_common_substring(text1: str, text2: str) -> str: """ Finds the longest common substring between two strings. + >>> longest_common_substring("", "") '' >>> longest_common_substring("a","") diff --git a/dynamic_programming/longest_increasing_subsequence.py b/dynamic_programming/longest_increasing_subsequence.py index 2a78e2e7ad1d..d839757f6da5 100644 --- a/dynamic_programming/longest_increasing_subsequence.py +++ b/dynamic_programming/longest_increasing_subsequence.py @@ -4,11 +4,13 @@ This is a pure Python implementation of Dynamic Programming solution to the longest increasing subsequence of a given sequence. -The problem is : -Given an array, to find the longest and increasing sub-array in that given array and -return it. -Example: [10, 22, 9, 33, 21, 50, 41, 60, 80] as input will return - [10, 22, 33, 41, 60, 80] as output +The problem is: + Given an array, to find the longest and increasing sub-array in that given array and + return it. + +Example: + ``[10, 22, 9, 33, 21, 50, 41, 60, 80]`` as input will return + ``[10, 22, 33, 41, 60, 80]`` as output """ from __future__ import annotations @@ -17,6 +19,7 @@ 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]) [10, 22, 33, 41, 60, 80] >>> longest_subsequence([4, 8, 7, 5, 1, 12, 2, 3, 9]) diff --git a/dynamic_programming/matrix_chain_multiplication.py b/dynamic_programming/matrix_chain_multiplication.py index da6e525ce816..10e136b9f0db 100644 --- a/dynamic_programming/matrix_chain_multiplication.py +++ b/dynamic_programming/matrix_chain_multiplication.py @@ -1,42 +1,48 @@ """ -Find the minimum number of multiplications needed to multiply chain of matrices. -Reference: https://www.geeksforgeeks.org/matrix-chain-multiplication-dp-8/ +| Find the minimum number of multiplications needed to multiply chain of matrices. +| Reference: https://www.geeksforgeeks.org/matrix-chain-multiplication-dp-8/ -The algorithm has interesting real-world applications. Example: -1. Image transformations in Computer Graphics as images are composed of matrix. -2. Solve complex polynomial equations in the field of algebra using least processing - power. -3. Calculate overall impact of macroeconomic decisions as economic equations involve a - number of variables. -4. Self-driving car navigation can be made more accurate as matrix multiplication can - accurately determine position and orientation of obstacles in short time. +The algorithm has interesting real-world applications. -Python doctests can be run with the following command: -python -m doctest -v matrix_chain_multiply.py +Example: + 1. Image transformations in Computer Graphics as images are composed of matrix. + 2. Solve complex polynomial equations in the field of algebra using least processing + power. + 3. Calculate overall impact of macroeconomic decisions as economic equations involve a + number of variables. + 4. Self-driving car navigation can be made more accurate as matrix multiplication can + accurately determine position and orientation of obstacles in short time. -Given a sequence arr[] that represents chain of 2D matrices such that the dimension of -the ith matrix is arr[i-1]*arr[i]. -So suppose arr = [40, 20, 30, 10, 30] means we have 4 matrices of dimensions -40*20, 20*30, 30*10 and 10*30. +Python doctests can be run with the following command:: -matrix_chain_multiply() returns an integer denoting minimum number of multiplications to -multiply the chain. + python -m doctest -v matrix_chain_multiply.py + +Given a sequence ``arr[]`` that represents chain of 2D matrices such that the dimension +of the ``i`` th matrix is ``arr[i-1]*arr[i]``. +So suppose ``arr = [40, 20, 30, 10, 30]`` means we have ``4`` matrices of dimensions +``40*20``, ``20*30``, ``30*10`` and ``10*30``. + +``matrix_chain_multiply()`` returns an integer denoting minimum number of +multiplications to multiply the chain. We do not need to perform actual multiplication here. We only need to decide the order in which to perform the multiplication. Hints: -1. Number of multiplications (ie cost) to multiply 2 matrices -of size m*p and p*n is m*p*n. -2. Cost of matrix multiplication is associative ie (M1*M2)*M3 != M1*(M2*M3) -3. Matrix multiplication is not commutative. So, M1*M2 does not mean M2*M1 can be done. -4. To determine the required order, we can try different combinations. + 1. Number of multiplications (ie cost) to multiply ``2`` matrices + of size ``m*p`` and ``p*n`` is ``m*p*n``. + 2. Cost of matrix multiplication is not associative ie ``(M1*M2)*M3 != M1*(M2*M3)`` + 3. Matrix multiplication is not commutative. So, ``M1*M2`` does not mean ``M2*M1`` + can be done. + 4. To determine the required order, we can try different combinations. + So, this problem has overlapping sub-problems and can be solved using recursion. We use Dynamic Programming for optimal time complexity. Example input: -arr = [40, 20, 30, 10, 30] -output: 26000 + ``arr = [40, 20, 30, 10, 30]`` +output: + ``26000`` """ from collections.abc import Iterator @@ -50,25 +56,25 @@ def matrix_chain_multiply(arr: list[int]) -> int: Find the minimum number of multiplcations required to multiply the chain of matrices Args: - arr: The input array of integers. + `arr`: The input array of integers. Returns: Minimum number of multiplications needed to multiply the chain Examples: - >>> matrix_chain_multiply([1, 2, 3, 4, 3]) - 30 - >>> matrix_chain_multiply([10]) - 0 - >>> matrix_chain_multiply([10, 20]) - 0 - >>> matrix_chain_multiply([19, 2, 19]) - 722 - >>> matrix_chain_multiply(list(range(1, 100))) - 323398 - - # >>> matrix_chain_multiply(list(range(1, 251))) - # 2626798 + + >>> matrix_chain_multiply([1, 2, 3, 4, 3]) + 30 + >>> matrix_chain_multiply([10]) + 0 + >>> matrix_chain_multiply([10, 20]) + 0 + >>> matrix_chain_multiply([19, 2, 19]) + 722 + >>> matrix_chain_multiply(list(range(1, 100))) + 323398 + >>> # matrix_chain_multiply(list(range(1, 251))) + # 2626798 """ if len(arr) < 2: return 0 @@ -93,8 +99,10 @@ def matrix_chain_multiply(arr: list[int]) -> int: def matrix_chain_order(dims: list[int]) -> int: """ Source: https://en.wikipedia.org/wiki/Matrix_chain_multiplication + The dynamic programming solution is faster than cached the recursive solution and can handle larger inputs. + >>> matrix_chain_order([1, 2, 3, 4, 3]) 30 >>> matrix_chain_order([10]) @@ -105,8 +113,7 @@ def matrix_chain_order(dims: list[int]) -> int: 722 >>> matrix_chain_order(list(range(1, 100))) 323398 - - # >>> matrix_chain_order(list(range(1, 251))) # Max before RecursionError is raised + >>> # matrix_chain_order(list(range(1, 251))) # Max before RecursionError is raised # 2626798 """ diff --git a/dynamic_programming/max_product_subarray.py b/dynamic_programming/max_product_subarray.py index 425859bc03e3..6f4f38e38942 100644 --- a/dynamic_programming/max_product_subarray.py +++ b/dynamic_programming/max_product_subarray.py @@ -1,9 +1,10 @@ def max_product_subarray(numbers: list[int]) -> int: """ Returns the maximum product that can be obtained by multiplying a - contiguous subarray of the given integer list `nums`. + contiguous subarray of the given integer list `numbers`. Example: + >>> max_product_subarray([2, 3, -2, 4]) 6 >>> max_product_subarray((-2, 0, -1)) diff --git a/dynamic_programming/minimum_squares_to_represent_a_number.py b/dynamic_programming/minimum_squares_to_represent_a_number.py index bf5849f5bcb3..98c0602fa831 100644 --- a/dynamic_programming/minimum_squares_to_represent_a_number.py +++ b/dynamic_programming/minimum_squares_to_represent_a_number.py @@ -5,6 +5,7 @@ def minimum_squares_to_represent_a_number(number: int) -> int: """ Count the number of minimum squares to represent a number + >>> minimum_squares_to_represent_a_number(25) 1 >>> minimum_squares_to_represent_a_number(37) diff --git a/dynamic_programming/regex_match.py b/dynamic_programming/regex_match.py index 200a882831c0..e94d82093c8b 100644 --- a/dynamic_programming/regex_match.py +++ b/dynamic_programming/regex_match.py @@ -1,23 +1,25 @@ """ Regex matching check if a text matches pattern or not. Pattern: - '.' Matches any single character. - '*' Matches zero or more of the preceding element. + + 1. ``.`` Matches any single character. + 2. ``*`` Matches zero or more of the preceding element. + More info: https://medium.com/trick-the-interviwer/regular-expression-matching-9972eb74c03 """ def recursive_match(text: str, pattern: str) -> bool: - """ + r""" Recursive matching algorithm. - Time complexity: O(2 ^ (|text| + |pattern|)) - Space complexity: Recursion depth is O(|text| + |pattern|). + | Time complexity: O(2^(\|text\| + \|pattern\|)) + | Space complexity: Recursion depth is O(\|text\| + \|pattern\|). :param text: Text to match. :param pattern: Pattern to match. - :return: True if text matches pattern, False otherwise. + :return: ``True`` if `text` matches `pattern`, ``False`` otherwise. >>> recursive_match('abc', 'a.c') True @@ -48,15 +50,15 @@ def recursive_match(text: str, pattern: str) -> bool: def dp_match(text: str, pattern: str) -> bool: - """ + r""" Dynamic programming matching algorithm. - Time complexity: O(|text| * |pattern|) - Space complexity: O(|text| * |pattern|) + | Time complexity: O(\|text\| * \|pattern\|) + | Space complexity: O(\|text\| * \|pattern\|) :param text: Text to match. :param pattern: Pattern to match. - :return: True if text matches pattern, False otherwise. + :return: ``True`` if `text` matches `pattern`, ``False`` otherwise. >>> dp_match('abc', 'a.c') True diff --git a/dynamic_programming/rod_cutting.py b/dynamic_programming/rod_cutting.py index f80fa440ae86..d12c759dc928 100644 --- a/dynamic_programming/rod_cutting.py +++ b/dynamic_programming/rod_cutting.py @@ -1,7 +1,7 @@ """ This module provides two implementations for the rod-cutting problem: -1. A naive recursive implementation which has an exponential runtime -2. Two dynamic programming implementations which have quadratic runtime + 1. A naive recursive implementation which has an exponential runtime + 2. Two dynamic programming implementations which have quadratic runtime The rod-cutting problem is the problem of finding the maximum possible revenue obtainable from a rod of length ``n`` given a list of prices for each integral piece @@ -20,18 +20,21 @@ def naive_cut_rod_recursive(n: int, prices: list): Runtime: O(2^n) Arguments - ------- - n: int, the length of the rod - prices: list, the prices for each piece of rod. ``p[i-i]`` is the - price for a rod of length ``i`` + --------- + + * `n`: int, the length of the rod + * `prices`: list, the prices for each piece of rod. ``p[i-i]`` is the + price for a rod of length ``i`` Returns ------- - The maximum revenue obtainable for a rod of length n given the list of prices + + The maximum revenue obtainable for a rod of length `n` given the list of prices for each piece. Examples -------- + >>> naive_cut_rod_recursive(4, [1, 5, 8, 9]) 10 >>> naive_cut_rod_recursive(10, [1, 5, 8, 9, 10, 17, 17, 20, 24, 30]) @@ -54,28 +57,30 @@ def top_down_cut_rod(n: int, prices: list): """ Constructs a top-down dynamic programming solution for the rod-cutting problem via memoization. This function serves as a wrapper for - _top_down_cut_rod_recursive + ``_top_down_cut_rod_recursive`` Runtime: O(n^2) Arguments - -------- - n: int, the length of the rod - prices: list, the prices for each piece of rod. ``p[i-i]`` is the - price for a rod of length ``i`` + --------- - Note - ---- - For convenience and because Python's lists using 0-indexing, length(max_rev) = - n + 1, to accommodate for the revenue obtainable from a rod of length 0. + * `n`: int, the length of the rod + * `prices`: list, the prices for each piece of rod. ``p[i-i]`` is the + price for a rod of length ``i`` + + .. note:: + For convenience and because Python's lists using ``0``-indexing, ``length(max_rev) + = n + 1``, to accommodate for the revenue obtainable from a rod of length ``0``. Returns ------- - The maximum revenue obtainable for a rod of length n given the list of prices + + The maximum revenue obtainable for a rod of length `n` given the list of prices for each piece. Examples - ------- + -------- + >>> top_down_cut_rod(4, [1, 5, 8, 9]) 10 >>> top_down_cut_rod(10, [1, 5, 8, 9, 10, 17, 17, 20, 24, 30]) @@ -94,16 +99,18 @@ def _top_down_cut_rod_recursive(n: int, prices: list, max_rev: list): Runtime: O(n^2) Arguments - -------- - n: int, the length of the rod - prices: list, the prices for each piece of rod. ``p[i-i]`` is the - price for a rod of length ``i`` - max_rev: list, the computed maximum revenue for a piece of rod. - ``max_rev[i]`` is the maximum revenue obtainable for a rod of length ``i`` + --------- + + * `n`: int, the length of the rod + * `prices`: list, the prices for each piece of rod. ``p[i-i]`` is the + price for a rod of length ``i`` + * `max_rev`: list, the computed maximum revenue for a piece of rod. + ``max_rev[i]`` is the maximum revenue obtainable for a rod of length ``i`` Returns ------- - The maximum revenue obtainable for a rod of length n given the list of prices + + The maximum revenue obtainable for a rod of length `n` given the list of prices for each piece. """ if max_rev[n] >= 0: @@ -130,18 +137,21 @@ def bottom_up_cut_rod(n: int, prices: list): Runtime: O(n^2) Arguments - ---------- - n: int, the maximum length of the rod. - prices: list, the prices for each piece of rod. ``p[i-i]`` is the - price for a rod of length ``i`` + --------- + + * `n`: int, the maximum length of the rod. + * `prices`: list, the prices for each piece of rod. ``p[i-i]`` is the + price for a rod of length ``i`` Returns ------- - The maximum revenue obtainable from cutting a rod of length n given + + The maximum revenue obtainable from cutting a rod of length `n` given the prices for each piece of rod p. Examples - ------- + -------- + >>> bottom_up_cut_rod(4, [1, 5, 8, 9]) 10 >>> bottom_up_cut_rod(10, [1, 5, 8, 9, 10, 17, 17, 20, 24, 30]) @@ -168,13 +178,12 @@ def _enforce_args(n: int, prices: list): """ Basic checks on the arguments to the rod-cutting algorithms - n: int, the length of the rod - prices: list, the price list for each piece of rod. - - Throws ValueError: + * `n`: int, the length of the rod + * `prices`: list, the price list for each piece of rod. - if n is negative or there are fewer items in the price list than the length of - the rod + Throws ``ValueError``: + if `n` is negative or there are fewer items in the price list than the length of + the rod """ if n < 0: msg = f"n must be greater than or equal to 0. Got n = {n}" diff --git a/dynamic_programming/subset_generation.py b/dynamic_programming/subset_generation.py index d490bca737ba..08daaac6f88a 100644 --- a/dynamic_programming/subset_generation.py +++ b/dynamic_programming/subset_generation.py @@ -1,38 +1,41 @@ def subset_combinations(elements: list[int], n: int) -> list: """ Compute n-element combinations from a given list using dynamic programming. + Args: - elements: The list of elements from which combinations will be generated. - n: The number of elements in each combination. + * `elements`: The list of elements from which combinations will be generated. + * `n`: The number of elements in each combination. + Returns: - A list of tuples, each representing a combination of n elements. - >>> subset_combinations(elements=[10, 20, 30, 40], n=2) - [(10, 20), (10, 30), (10, 40), (20, 30), (20, 40), (30, 40)] - >>> subset_combinations(elements=[1, 2, 3], n=1) - [(1,), (2,), (3,)] - >>> subset_combinations(elements=[1, 2, 3], n=3) - [(1, 2, 3)] - >>> subset_combinations(elements=[42], n=1) - [(42,)] - >>> subset_combinations(elements=[6, 7, 8, 9], n=4) - [(6, 7, 8, 9)] - >>> subset_combinations(elements=[10, 20, 30, 40, 50], n=0) - [()] - >>> subset_combinations(elements=[1, 2, 3, 4], n=2) - [(1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)] - >>> subset_combinations(elements=[1, 'apple', 3.14], n=2) - [(1, 'apple'), (1, 3.14), ('apple', 3.14)] - >>> subset_combinations(elements=['single'], n=0) - [()] - >>> subset_combinations(elements=[], n=9) - [] - >>> from itertools import combinations - >>> all(subset_combinations(items, n) == list(combinations(items, n)) - ... for items, n in ( - ... ([10, 20, 30, 40], 2), ([1, 2, 3], 1), ([1, 2, 3], 3), ([42], 1), - ... ([6, 7, 8, 9], 4), ([10, 20, 30, 40, 50], 1), ([1, 2, 3, 4], 2), - ... ([1, 'apple', 3.14], 2), (['single'], 0), ([], 9))) - True + A list of tuples, each representing a combination of `n` elements. + + >>> subset_combinations(elements=[10, 20, 30, 40], n=2) + [(10, 20), (10, 30), (10, 40), (20, 30), (20, 40), (30, 40)] + >>> subset_combinations(elements=[1, 2, 3], n=1) + [(1,), (2,), (3,)] + >>> subset_combinations(elements=[1, 2, 3], n=3) + [(1, 2, 3)] + >>> subset_combinations(elements=[42], n=1) + [(42,)] + >>> subset_combinations(elements=[6, 7, 8, 9], n=4) + [(6, 7, 8, 9)] + >>> subset_combinations(elements=[10, 20, 30, 40, 50], n=0) + [()] + >>> subset_combinations(elements=[1, 2, 3, 4], n=2) + [(1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)] + >>> subset_combinations(elements=[1, 'apple', 3.14], n=2) + [(1, 'apple'), (1, 3.14), ('apple', 3.14)] + >>> subset_combinations(elements=['single'], n=0) + [()] + >>> subset_combinations(elements=[], n=9) + [] + >>> from itertools import combinations + >>> all(subset_combinations(items, n) == list(combinations(items, n)) + ... for items, n in ( + ... ([10, 20, 30, 40], 2), ([1, 2, 3], 1), ([1, 2, 3], 3), ([42], 1), + ... ([6, 7, 8, 9], 4), ([10, 20, 30, 40, 50], 1), ([1, 2, 3, 4], 2), + ... ([1, 'apple', 3.14], 2), (['single'], 0), ([], 9))) + True """ r = len(elements) if n > r: diff --git a/dynamic_programming/viterbi.py b/dynamic_programming/viterbi.py index 764d45dc2c05..5b78fa9e46d0 100644 --- a/dynamic_programming/viterbi.py +++ b/dynamic_programming/viterbi.py @@ -9,119 +9,102 @@ def viterbi( emission_probabilities: dict, ) -> list: """ - Viterbi Algorithm, to find the most likely path of - states from the start and the expected output. - https://en.wikipedia.org/wiki/Viterbi_algorithm - sdafads - Wikipedia example - >>> observations = ["normal", "cold", "dizzy"] - >>> states = ["Healthy", "Fever"] - >>> start_p = {"Healthy": 0.6, "Fever": 0.4} - >>> trans_p = { - ... "Healthy": {"Healthy": 0.7, "Fever": 0.3}, - ... "Fever": {"Healthy": 0.4, "Fever": 0.6}, - ... } - >>> emit_p = { - ... "Healthy": {"normal": 0.5, "cold": 0.4, "dizzy": 0.1}, - ... "Fever": {"normal": 0.1, "cold": 0.3, "dizzy": 0.6}, - ... } - >>> viterbi(observations, states, start_p, trans_p, emit_p) - ['Healthy', 'Healthy', 'Fever'] + Viterbi Algorithm, to find the most likely path of + states from the start and the expected output. - >>> viterbi((), states, start_p, trans_p, emit_p) - Traceback (most recent call last): - ... - ValueError: There's an empty parameter - - >>> viterbi(observations, (), start_p, trans_p, emit_p) - Traceback (most recent call last): - ... - ValueError: There's an empty parameter - - >>> viterbi(observations, states, {}, trans_p, emit_p) - Traceback (most recent call last): - ... - ValueError: There's an empty parameter - - >>> viterbi(observations, states, start_p, {}, emit_p) - Traceback (most recent call last): - ... - ValueError: There's an empty parameter - - >>> viterbi(observations, states, start_p, trans_p, {}) - Traceback (most recent call last): - ... - ValueError: There's an empty parameter - - >>> viterbi("invalid", states, start_p, trans_p, emit_p) - Traceback (most recent call last): - ... - ValueError: observations_space must be a list + https://en.wikipedia.org/wiki/Viterbi_algorithm - >>> viterbi(["valid", 123], states, start_p, trans_p, emit_p) - Traceback (most recent call last): - ... - ValueError: observations_space must be a list of strings - - >>> viterbi(observations, "invalid", start_p, trans_p, emit_p) - Traceback (most recent call last): - ... - ValueError: states_space must be a list - - >>> viterbi(observations, ["valid", 123], start_p, trans_p, emit_p) - Traceback (most recent call last): - ... - ValueError: states_space must be a list of strings - - >>> viterbi(observations, states, "invalid", trans_p, emit_p) - Traceback (most recent call last): - ... - ValueError: initial_probabilities must be a dict - - >>> viterbi(observations, states, {2:2}, trans_p, emit_p) - Traceback (most recent call last): - ... - ValueError: initial_probabilities all keys must be strings - - >>> viterbi(observations, states, {"a":2}, trans_p, emit_p) - Traceback (most recent call last): - ... - ValueError: initial_probabilities all values must be float + Wikipedia example - >>> viterbi(observations, states, start_p, "invalid", emit_p) - Traceback (most recent call last): - ... - ValueError: transition_probabilities must be a dict - - >>> viterbi(observations, states, start_p, {"a":2}, emit_p) - Traceback (most recent call last): - ... - ValueError: transition_probabilities all values must be dict - - >>> viterbi(observations, states, start_p, {2:{2:2}}, emit_p) - Traceback (most recent call last): - ... - ValueError: transition_probabilities all keys must be strings - - >>> viterbi(observations, states, start_p, {"a":{2:2}}, emit_p) - Traceback (most recent call last): - ... - ValueError: transition_probabilities all keys must be strings - - >>> viterbi(observations, states, start_p, {"a":{"b":2}}, emit_p) - Traceback (most recent call last): - ... - ValueError: transition_probabilities nested dictionary all values must be float - - >>> viterbi(observations, states, start_p, trans_p, "invalid") - Traceback (most recent call last): - ... - ValueError: emission_probabilities must be a dict - - >>> viterbi(observations, states, start_p, trans_p, None) - Traceback (most recent call last): - ... - ValueError: There's an empty parameter + >>> observations = ["normal", "cold", "dizzy"] + >>> states = ["Healthy", "Fever"] + >>> start_p = {"Healthy": 0.6, "Fever": 0.4} + >>> trans_p = { + ... "Healthy": {"Healthy": 0.7, "Fever": 0.3}, + ... "Fever": {"Healthy": 0.4, "Fever": 0.6}, + ... } + >>> emit_p = { + ... "Healthy": {"normal": 0.5, "cold": 0.4, "dizzy": 0.1}, + ... "Fever": {"normal": 0.1, "cold": 0.3, "dizzy": 0.6}, + ... } + >>> viterbi(observations, states, start_p, trans_p, emit_p) + ['Healthy', 'Healthy', 'Fever'] + >>> viterbi((), states, start_p, trans_p, emit_p) + Traceback (most recent call last): + ... + ValueError: There's an empty parameter + >>> viterbi(observations, (), start_p, trans_p, emit_p) + Traceback (most recent call last): + ... + ValueError: There's an empty parameter + >>> viterbi(observations, states, {}, trans_p, emit_p) + Traceback (most recent call last): + ... + ValueError: There's an empty parameter + >>> viterbi(observations, states, start_p, {}, emit_p) + Traceback (most recent call last): + ... + ValueError: There's an empty parameter + >>> viterbi(observations, states, start_p, trans_p, {}) + Traceback (most recent call last): + ... + ValueError: There's an empty parameter + >>> viterbi("invalid", states, start_p, trans_p, emit_p) + Traceback (most recent call last): + ... + ValueError: observations_space must be a list + >>> viterbi(["valid", 123], states, start_p, trans_p, emit_p) + Traceback (most recent call last): + ... + ValueError: observations_space must be a list of strings + >>> viterbi(observations, "invalid", start_p, trans_p, emit_p) + Traceback (most recent call last): + ... + ValueError: states_space must be a list + >>> viterbi(observations, ["valid", 123], start_p, trans_p, emit_p) + Traceback (most recent call last): + ... + ValueError: states_space must be a list of strings + >>> viterbi(observations, states, "invalid", trans_p, emit_p) + Traceback (most recent call last): + ... + ValueError: initial_probabilities must be a dict + >>> viterbi(observations, states, {2:2}, trans_p, emit_p) + Traceback (most recent call last): + ... + ValueError: initial_probabilities all keys must be strings + >>> viterbi(observations, states, {"a":2}, trans_p, emit_p) + Traceback (most recent call last): + ... + ValueError: initial_probabilities all values must be float + >>> viterbi(observations, states, start_p, "invalid", emit_p) + Traceback (most recent call last): + ... + ValueError: transition_probabilities must be a dict + >>> viterbi(observations, states, start_p, {"a":2}, emit_p) + Traceback (most recent call last): + ... + ValueError: transition_probabilities all values must be dict + >>> viterbi(observations, states, start_p, {2:{2:2}}, emit_p) + Traceback (most recent call last): + ... + ValueError: transition_probabilities all keys must be strings + >>> viterbi(observations, states, start_p, {"a":{2:2}}, emit_p) + Traceback (most recent call last): + ... + ValueError: transition_probabilities all keys must be strings + >>> viterbi(observations, states, start_p, {"a":{"b":2}}, emit_p) + Traceback (most recent call last): + ... + ValueError: transition_probabilities nested dictionary all values must be float + >>> viterbi(observations, states, start_p, trans_p, "invalid") + Traceback (most recent call last): + ... + ValueError: emission_probabilities must be a dict + >>> viterbi(observations, states, start_p, trans_p, None) + Traceback (most recent call last): + ... + ValueError: There's an empty parameter """ _validation( @@ -213,7 +196,6 @@ def _validation( ... "Fever": {"normal": 0.1, "cold": 0.3, "dizzy": 0.6}, ... } >>> _validation(observations, states, start_p, trans_p, emit_p) - >>> _validation([], states, start_p, trans_p, emit_p) Traceback (most recent call last): ... @@ -242,7 +224,6 @@ def _validate_not_empty( """ >>> _validate_not_empty(["a"], ["b"], {"c":0.5}, ... {"d": {"e": 0.6}}, {"f": {"g": 0.7}}) - >>> _validate_not_empty(["a"], ["b"], {"c":0.5}, {}, {"f": {"g": 0.7}}) Traceback (most recent call last): ... @@ -267,12 +248,10 @@ def _validate_not_empty( def _validate_lists(observations_space: Any, states_space: Any) -> None: """ >>> _validate_lists(["a"], ["b"]) - >>> _validate_lists(1234, ["b"]) Traceback (most recent call last): ... ValueError: observations_space must be a list - >>> _validate_lists(["a"], [3]) Traceback (most recent call last): ... @@ -285,7 +264,6 @@ def _validate_lists(observations_space: Any, states_space: Any) -> None: def _validate_list(_object: Any, var_name: str) -> None: """ >>> _validate_list(["a"], "mock_name") - >>> _validate_list("a", "mock_name") Traceback (most recent call last): ... @@ -294,7 +272,6 @@ def _validate_list(_object: Any, var_name: str) -> None: Traceback (most recent call last): ... ValueError: mock_name must be a list of strings - """ if not isinstance(_object, list): msg = f"{var_name} must be a list" @@ -313,7 +290,6 @@ def _validate_dicts( ) -> None: """ >>> _validate_dicts({"c":0.5}, {"d": {"e": 0.6}}, {"f": {"g": 0.7}}) - >>> _validate_dicts("invalid", {"d": {"e": 0.6}}, {"f": {"g": 0.7}}) Traceback (most recent call last): ... @@ -339,7 +315,6 @@ def _validate_dicts( def _validate_nested_dict(_object: Any, var_name: str) -> None: """ >>> _validate_nested_dict({"a":{"b": 0.5}}, "mock_name") - >>> _validate_nested_dict("invalid", "mock_name") Traceback (most recent call last): ... @@ -367,7 +342,6 @@ def _validate_dict( ) -> None: """ >>> _validate_dict({"b": 0.5}, "mock_name", float) - >>> _validate_dict("invalid", "mock_name", float) Traceback (most recent call last): ... From a2be5adf673c32720bbfb649c368af8fd7ae9dbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julia=20Arag=C3=A3o?= <101305675+juliaaragao@users.noreply.github.com> Date: Mon, 30 Dec 2024 13:36:55 +0100 Subject: [PATCH 730/757] Tests electronics/electric_conductivity.py #9943 (#12437) * Function conversion rectangular number to polar * #9943 : adding test to elelectronics/electric_conductivity.py * updating DIRECTORY.md * Apply suggestions from code review * updating DIRECTORY.md * Rename rec_to_pol.py to rectangular_to_polar.py * updating DIRECTORY.md * Update conversions/rectangular_to_polar.py * Update conversions/rectangular_to_polar.py --------- Co-authored-by: Julia Co-authored-by: juliaaragao Co-authored-by: Christian Clauss Co-authored-by: cclauss --- DIRECTORY.md | 1 + conversions/rectangular_to_polar.py | 32 ++++++++++++++++++++++++++++ electronics/electric_conductivity.py | 20 +++++++++++++++++ 3 files changed, 53 insertions(+) create mode 100644 conversions/rectangular_to_polar.py diff --git a/DIRECTORY.md b/DIRECTORY.md index d234d366df06..44d0414a37c8 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -169,6 +169,7 @@ * [Prefix Conversions](conversions/prefix_conversions.py) * [Prefix Conversions String](conversions/prefix_conversions_string.py) * [Pressure Conversions](conversions/pressure_conversions.py) + * [Rectangular To Polar](conversions/rectangular_to_polar.py) * [Rgb Cmyk Conversion](conversions/rgb_cmyk_conversion.py) * [Rgb Hsv Conversion](conversions/rgb_hsv_conversion.py) * [Roman Numerals](conversions/roman_numerals.py) diff --git a/conversions/rectangular_to_polar.py b/conversions/rectangular_to_polar.py new file mode 100644 index 000000000000..bed97d7410ec --- /dev/null +++ b/conversions/rectangular_to_polar.py @@ -0,0 +1,32 @@ +import math + + +def rectangular_to_polar(real: float, img: float) -> tuple[float, float]: + """ + https://en.wikipedia.org/wiki/Polar_coordinate_system + + >>> rectangular_to_polar(5,-5) + (7.07, -45.0) + >>> rectangular_to_polar(-1,1) + (1.41, 135.0) + >>> rectangular_to_polar(-1,-1) + (1.41, -135.0) + >>> rectangular_to_polar(1e-10,1e-10) + (0.0, 45.0) + >>> rectangular_to_polar(-1e-10,1e-10) + (0.0, 135.0) + >>> rectangular_to_polar(9.75,5.93) + (11.41, 31.31) + >>> rectangular_to_polar(10000,99999) + (100497.76, 84.29) + """ + + mod = round(math.sqrt((real**2) + (img**2)), 2) + ang = round(math.degrees(math.atan2(img, real)), 2) + return (mod, ang) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() diff --git a/electronics/electric_conductivity.py b/electronics/electric_conductivity.py index 11f2a607d214..65bb6c5ceaf0 100644 --- a/electronics/electric_conductivity.py +++ b/electronics/electric_conductivity.py @@ -21,6 +21,26 @@ def electric_conductivity( ('conductivity', 5.12672e-14) >>> electric_conductivity(conductivity=1000, electron_conc=0, mobility=1200) ('electron_conc', 5.201506356240767e+18) + >>> electric_conductivity(conductivity=-10, electron_conc=100, mobility=0) + Traceback (most recent call last): + ... + ValueError: Conductivity cannot be negative + >>> electric_conductivity(conductivity=50, electron_conc=-10, mobility=0) + Traceback (most recent call last): + ... + ValueError: Electron concentration cannot be negative + >>> electric_conductivity(conductivity=50, electron_conc=0, mobility=-10) + Traceback (most recent call last): + ... + ValueError: mobility cannot be negative + >>> electric_conductivity(conductivity=50, electron_conc=0, mobility=0) + Traceback (most recent call last): + ... + ValueError: You cannot supply more or less than 2 values + >>> electric_conductivity(conductivity=50, electron_conc=200, mobility=300) + Traceback (most recent call last): + ... + ValueError: You cannot supply more or less than 2 values """ if (conductivity, electron_conc, mobility).count(0) != 1: raise ValueError("You cannot supply more or less than 2 values") From f24ddba5b2600486f7c3a4c5807cf2aeed421870 Mon Sep 17 00:00:00 2001 From: Matej <83732219+IsxImattI@users.noreply.github.com> Date: Mon, 30 Dec 2024 16:04:28 +0100 Subject: [PATCH 731/757] Implemented doctests for geometry-related classes (#12368) * Implemented doctests for geometry-related classes * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Removed unused noqa directive * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * refactored sudoku_solver.py * refactored sudoku_solver.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * context manager for file handling changed too in from_file function --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Tianyi Zheng --- data_structures/arrays/sudoku_solver.py | 5 +++-- geometry/geometry.py | 29 +++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/data_structures/arrays/sudoku_solver.py b/data_structures/arrays/sudoku_solver.py index 7e38e1465728..fd1a4f3e37b8 100644 --- a/data_structures/arrays/sudoku_solver.py +++ b/data_structures/arrays/sudoku_solver.py @@ -23,7 +23,7 @@ def cross(items_a, items_b): + [cross(rs, cs) for rs in ("ABC", "DEF", "GHI") for cs in ("123", "456", "789")] ) units = {s: [u for u in unitlist if s in u] for s in squares} -peers = {s: set(sum(units[s], [])) - {s} for s in squares} # noqa: RUF017 +peers = {s: {x for u in units[s] for x in u} - {s} for s in squares} def test(): @@ -172,7 +172,8 @@ def unitsolved(unit): def from_file(filename, sep="\n"): "Parse a file into a list of strings, separated by sep." - return open(filename).read().strip().split(sep) + with open(filename) as file: + return file.read().strip().split(sep) def random_puzzle(assignments=17): diff --git a/geometry/geometry.py b/geometry/geometry.py index 9e353dee17a7..a0be8eb3befc 100644 --- a/geometry/geometry.py +++ b/geometry/geometry.py @@ -48,6 +48,18 @@ class Side: Side(length=5, angle=Angle(degrees=45.6), next_side=None) >>> Side(5, Angle(45.6), Side(1, Angle(2))) # doctest: +ELLIPSIS Side(length=5, angle=Angle(degrees=45.6), next_side=Side(length=1, angle=Angle(d... + >>> Side(-1) + Traceback (most recent call last): + ... + TypeError: length must be a positive numeric value. + >>> Side(5, None) + Traceback (most recent call last): + ... + TypeError: angle must be an Angle object. + >>> Side(5, Angle(90), "Invalid next_side") + Traceback (most recent call last): + ... + TypeError: next_side must be a Side or None. """ length: float @@ -162,6 +174,19 @@ class Polygon: >>> Polygon() Polygon(sides=[]) + >>> polygon = Polygon() + >>> polygon.add_side(Side(5)).get_side(0) + Side(length=5, angle=Angle(degrees=90), next_side=None) + >>> polygon.get_side(1) + Traceback (most recent call last): + ... + IndexError: list index out of range + >>> polygon.set_side(0, Side(10)).get_side(0) + Side(length=10, angle=Angle(degrees=90), next_side=None) + >>> polygon.set_side(1, Side(10)) + Traceback (most recent call last): + ... + IndexError: list assignment index out of range """ sides: list[Side] = field(default_factory=list) @@ -207,6 +232,10 @@ class Rectangle(Polygon): 30 >>> rectangle_one.area() 50 + >>> Rectangle(-5, 10) + Traceback (most recent call last): + ... + TypeError: length must be a positive numeric value. """ def __init__(self, short_side_length: float, long_side_length: float) -> None: From 77425364c87908bf061ad78b770ec840086b4efb Mon Sep 17 00:00:00 2001 From: SUDO_USER <110802232+AtharvMalusare@users.noreply.github.com> Date: Mon, 30 Dec 2024 20:42:04 +0530 Subject: [PATCH 732/757] Intensity_based_Segmentation (#12491) * Add files via upload * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update intensity-based_segmentation.py * Update and rename intensity-based_segmentation.py to intensity_based_segmentation.py * Update intensity_based_segmentation.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Apply suggestions from code review * [0, 1, 1]], dtype=int32) --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- .../intensity_based_segmentation.py | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 computer_vision/intensity_based_segmentation.py diff --git a/computer_vision/intensity_based_segmentation.py b/computer_vision/intensity_based_segmentation.py new file mode 100644 index 000000000000..7f2b1141acc4 --- /dev/null +++ b/computer_vision/intensity_based_segmentation.py @@ -0,0 +1,62 @@ +# Source: "https://www.ijcse.com/docs/IJCSE11-02-03-117.pdf" + +# Importing necessary libraries +import matplotlib.pyplot as plt +import numpy as np +from PIL import Image + + +def segment_image(image: np.ndarray, thresholds: list[int]) -> np.ndarray: + """ + Performs image segmentation based on intensity thresholds. + + Args: + image: Input grayscale image as a 2D array. + thresholds: Intensity thresholds to define segments. + + Returns: + A labeled 2D array where each region corresponds to a threshold range. + + Example: + >>> img = np.array([[80, 120, 180], [40, 90, 150], [20, 60, 100]]) + >>> segment_image(img, [50, 100, 150]) + array([[1, 2, 3], + [0, 1, 2], + [0, 1, 1]], dtype=int32) + """ + # Initialize segmented array with zeros + segmented = np.zeros_like(image, dtype=np.int32) + + # Assign labels based on thresholds + for i, threshold in enumerate(thresholds): + segmented[image > threshold] = i + 1 + + return segmented + + +if __name__ == "__main__": + # Load the image + image_path = "path_to_image" # Replace with your image path + original_image = Image.open(image_path).convert("L") + image_array = np.array(original_image) + + # Define thresholds + thresholds = [50, 100, 150, 200] + + # Perform segmentation + segmented_image = segment_image(image_array, thresholds) + + # Display the results + plt.figure(figsize=(10, 5)) + + plt.subplot(1, 2, 1) + plt.title("Original Image") + plt.imshow(image_array, cmap="gray") + plt.axis("off") + + plt.subplot(1, 2, 2) + plt.title("Segmented Image") + plt.imshow(segmented_image, cmap="tab20") + plt.axis("off") + + plt.show() From 75c5c411133f7e0f339c8d68c7c76c8054eb4249 Mon Sep 17 00:00:00 2001 From: Scarfinos <158184182+Scarfinos@users.noreply.github.com> Date: Mon, 30 Dec 2024 16:12:26 +0100 Subject: [PATCH 733/757] #9943 : Adding coverage test for basic_graphs.py (#12354) * #9943 : Adding coverage test for basic_graphs.py * #9943 : Adding coverage test for basic_graphs.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Solve problem of line too long --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- graphs/basic_graphs.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/graphs/basic_graphs.py b/graphs/basic_graphs.py index 567fa65040ae..286e9b195796 100644 --- a/graphs/basic_graphs.py +++ b/graphs/basic_graphs.py @@ -77,6 +77,14 @@ def initialize_weighted_undirected_graph( def dfs(g, s): + """ + >>> dfs({1: [2, 3], 2: [4, 5], 3: [], 4: [], 5: []}, 1) + 1 + 2 + 4 + 5 + 3 + """ vis, _s = {s}, [s] print(s) while _s: @@ -104,6 +112,17 @@ def dfs(g, s): def bfs(g, s): + """ + >>> bfs({1: [2, 3], 2: [4, 5], 3: [6, 7], 4: [], 5: [8], 6: [], 7: [], 8: []}, 1) + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + """ vis, q = {s}, deque([s]) print(s) while q: @@ -128,6 +147,19 @@ def bfs(g, s): def dijk(g, s): + """ + dijk({1: [(2, 7), (3, 9), (6, 14)], + 2: [(1, 7), (3, 10), (4, 15)], + 3: [(1, 9), (2, 10), (4, 11), (6, 2)], + 4: [(2, 15), (3, 11), (5, 6)], + 5: [(4, 6), (6, 9)], + 6: [(1, 14), (3, 2), (5, 9)]}, 1) + 7 + 9 + 11 + 20 + 20 + """ dist, known, path = {s: 0}, set(), {s: 0} while True: if len(known) == len(g) - 1: From 7e55fb6474a06ecf0000fe11494fe5eefeeb54ab Mon Sep 17 00:00:00 2001 From: Jeffrey Yancey Date: Mon, 30 Dec 2024 12:00:30 -0700 Subject: [PATCH 734/757] - Implemented `find_lanczos_eigenvectors` to approximate the largest eigenvalues and corresponding eigenvectors of a graph based on its adjacency list. (#11906) - Utilized `lanczos_iteration` to construct tridiagonal matrices, optimized for large, sparse matrices. - Added `multiply_matrix_vector` for efficient matrix-vector multiplication using adjacency lists. - Included `validate_adjacency_list` for input validation. - Supports varied graph analysis applications, particularly for analyzing graph centrality. - Included type hints, comprehensive docstrings, and doctests. - PEP-8 compliant, with optimized handling of inputs and outputs. This module provides essential tools for eigenvalue-based graph analysis, ideal for centrality insights and structural assessments. --- graphs/lanczos_eigenvectors.py | 206 +++++++++++++++++++++++++++++++++ 1 file changed, 206 insertions(+) create mode 100644 graphs/lanczos_eigenvectors.py diff --git a/graphs/lanczos_eigenvectors.py b/graphs/lanczos_eigenvectors.py new file mode 100644 index 000000000000..581a81a1127f --- /dev/null +++ b/graphs/lanczos_eigenvectors.py @@ -0,0 +1,206 @@ +""" +Lanczos Method for Finding Eigenvalues and Eigenvectors of a Graph. + +This module demonstrates the Lanczos method to approximate the largest eigenvalues +and corresponding eigenvectors of a symmetric matrix represented as a graph's +adjacency list. The method efficiently handles large, sparse matrices by converting +the graph to a tridiagonal matrix, whose eigenvalues and eigenvectors are then +computed. + +Key Functions: +- `find_lanczos_eigenvectors`: Computes the k largest eigenvalues and vectors. +- `lanczos_iteration`: Constructs the tridiagonal matrix and orthonormal basis vectors. +- `multiply_matrix_vector`: Multiplies an adjacency list graph with a vector. + +Complexity: +- Time: O(k * n), where k is the number of eigenvalues and n is the matrix size. +- Space: O(n), due to sparse representation and tridiagonal matrix structure. + +Further Reading: +- Lanczos Algorithm: https://en.wikipedia.org/wiki/Lanczos_algorithm +- Eigenvector Centrality: https://en.wikipedia.org/wiki/Eigenvector_centrality + +Example Usage: +Given a graph represented by an adjacency list, the `find_lanczos_eigenvectors` +function returns the largest eigenvalues and eigenvectors. This can be used to +analyze graph centrality. +""" + +import numpy as np + + +def validate_adjacency_list(graph: list[list[int | None]]) -> None: + """Validates the adjacency list format for the graph. + + Args: + graph: A list of lists where each sublist contains the neighbors of a node. + + Raises: + ValueError: If the graph is not a list of lists, or if any node has + invalid neighbors (e.g., out-of-range or non-integer values). + + >>> validate_adjacency_list([[1, 2], [0], [0, 1]]) + >>> validate_adjacency_list([[]]) # No neighbors, valid case + >>> validate_adjacency_list([[1], [2], [-1]]) # Invalid neighbor + Traceback (most recent call last): + ... + ValueError: Invalid neighbor -1 in node 2 adjacency list. + """ + if not isinstance(graph, list): + raise ValueError("Graph should be a list of lists.") + + for node_index, neighbors in enumerate(graph): + if not isinstance(neighbors, list): + no_neighbors_message: str = ( + f"Node {node_index} should have a list of neighbors." + ) + raise ValueError(no_neighbors_message) + for neighbor_index in neighbors: + if ( + not isinstance(neighbor_index, int) + or neighbor_index < 0 + or neighbor_index >= len(graph) + ): + invalid_neighbor_message: str = ( + f"Invalid neighbor {neighbor_index} in node {node_index} " + f"adjacency list." + ) + raise ValueError(invalid_neighbor_message) + + +def lanczos_iteration( + graph: list[list[int | None]], num_eigenvectors: int +) -> tuple[np.ndarray, np.ndarray]: + """Constructs the tridiagonal matrix and orthonormal basis vectors using the + Lanczos method. + + Args: + graph: The graph represented as a list of adjacency lists. + num_eigenvectors: The number of largest eigenvalues and eigenvectors + to approximate. + + Returns: + A tuple containing: + - tridiagonal_matrix: A (num_eigenvectors x num_eigenvectors) symmetric + matrix. + - orthonormal_basis: A (num_nodes x num_eigenvectors) matrix of orthonormal + basis vectors. + + Raises: + ValueError: If num_eigenvectors is less than 1 or greater than the number of + nodes. + + >>> graph = [[1, 2], [0, 2], [0, 1]] + >>> T, Q = lanczos_iteration(graph, 2) + >>> T.shape == (2, 2) and Q.shape == (3, 2) + True + """ + num_nodes: int = len(graph) + if not (1 <= num_eigenvectors <= num_nodes): + raise ValueError( + "Number of eigenvectors must be between 1 and the number of " + "nodes in the graph." + ) + + orthonormal_basis: np.ndarray = np.zeros((num_nodes, num_eigenvectors)) + tridiagonal_matrix: np.ndarray = np.zeros((num_eigenvectors, num_eigenvectors)) + + rng = np.random.default_rng() + initial_vector: np.ndarray = rng.random(num_nodes) + initial_vector /= np.sqrt(np.dot(initial_vector, initial_vector)) + orthonormal_basis[:, 0] = initial_vector + + prev_beta: float = 0.0 + for iter_index in range(num_eigenvectors): + result_vector: np.ndarray = multiply_matrix_vector( + graph, orthonormal_basis[:, iter_index] + ) + if iter_index > 0: + result_vector -= prev_beta * orthonormal_basis[:, iter_index - 1] + alpha_value: float = np.dot(orthonormal_basis[:, iter_index], result_vector) + result_vector -= alpha_value * orthonormal_basis[:, iter_index] + + prev_beta = np.sqrt(np.dot(result_vector, result_vector)) + if iter_index < num_eigenvectors - 1 and prev_beta > 1e-10: + orthonormal_basis[:, iter_index + 1] = result_vector / prev_beta + tridiagonal_matrix[iter_index, iter_index] = alpha_value + if iter_index < num_eigenvectors - 1: + tridiagonal_matrix[iter_index, iter_index + 1] = prev_beta + tridiagonal_matrix[iter_index + 1, iter_index] = prev_beta + return tridiagonal_matrix, orthonormal_basis + + +def multiply_matrix_vector( + graph: list[list[int | None]], vector: np.ndarray +) -> np.ndarray: + """Performs multiplication of a graph's adjacency list representation with a vector. + + Args: + graph: The adjacency list of the graph. + vector: A 1D numpy array representing the vector to multiply. + + Returns: + A numpy array representing the product of the adjacency list and the vector. + + Raises: + ValueError: If the vector's length does not match the number of nodes in the + graph. + + >>> multiply_matrix_vector([[1, 2], [0, 2], [0, 1]], np.array([1, 1, 1])) + array([2., 2., 2.]) + >>> multiply_matrix_vector([[1, 2], [0, 2], [0, 1]], np.array([0, 1, 0])) + array([1., 0., 1.]) + """ + num_nodes: int = len(graph) + if vector.shape[0] != num_nodes: + raise ValueError("Vector length must match the number of nodes in the graph.") + + result: np.ndarray = np.zeros(num_nodes) + for node_index, neighbors in enumerate(graph): + for neighbor_index in neighbors: + result[node_index] += vector[neighbor_index] + return result + + +def find_lanczos_eigenvectors( + graph: list[list[int | None]], num_eigenvectors: int +) -> tuple[np.ndarray, np.ndarray]: + """Computes the largest eigenvalues and their corresponding eigenvectors using the + Lanczos method. + + Args: + graph: The graph as a list of adjacency lists. + num_eigenvectors: Number of largest eigenvalues and eigenvectors to compute. + + Returns: + A tuple containing: + - eigenvalues: 1D array of the largest eigenvalues in descending order. + - eigenvectors: 2D array where each column is an eigenvector corresponding + to an eigenvalue. + + Raises: + ValueError: If the graph format is invalid or num_eigenvectors is out of bounds. + + >>> eigenvalues, eigenvectors = find_lanczos_eigenvectors( + ... [[1, 2], [0, 2], [0, 1]], 2 + ... ) + >>> len(eigenvalues) == 2 and eigenvectors.shape[1] == 2 + True + """ + validate_adjacency_list(graph) + tridiagonal_matrix, orthonormal_basis = lanczos_iteration(graph, num_eigenvectors) + eigenvalues, eigenvectors = np.linalg.eigh(tridiagonal_matrix) + return eigenvalues[::-1], np.dot(orthonormal_basis, eigenvectors[:, ::-1]) + + +def main() -> None: + """ + Main driver function for testing the implementation with doctests. + """ + import doctest + + doctest.testmod() + + +if __name__ == "__main__": + main() From 8921b56a8517cdc9455d764d7cddb10b2d7f2145 Mon Sep 17 00:00:00 2001 From: Melih Mehmet Sahin Date: Mon, 30 Dec 2024 19:53:50 +0000 Subject: [PATCH 735/757] Adding tests to monotonic_array.py (#12073) * Contributes to #9943 by adding tests to monotonic_array.py Addeded doctest in the if __name__. Checks for negaitves and an array of same integers * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- data_structures/arrays/monotonic_array.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/data_structures/arrays/monotonic_array.py b/data_structures/arrays/monotonic_array.py index c50a21530814..342d443a9cfc 100644 --- a/data_structures/arrays/monotonic_array.py +++ b/data_structures/arrays/monotonic_array.py @@ -9,6 +9,16 @@ def is_monotonic(nums: list[int]) -> bool: True >>> is_monotonic([1, 3, 2]) False + >>> is_monotonic([1,2,3,4,5,6,5]) + False + >>> is_monotonic([-3,-2,-1]) + True + >>> is_monotonic([-5,-6,-7]) + True + >>> is_monotonic([0,0,0]) + True + >>> is_monotonic([-100,0,100]) + True """ return all(nums[i] <= nums[i + 1] for i in range(len(nums) - 1)) or all( nums[i] >= nums[i + 1] for i in range(len(nums) - 1) @@ -21,3 +31,7 @@ def is_monotonic(nums: list[int]) -> bool: print(is_monotonic([1, 2, 2, 3])) # Output: True print(is_monotonic([6, 5, 4, 4])) # Output: True print(is_monotonic([1, 3, 2])) # Output: False + + import doctest + + doctest.testmod() From 5942059cb571b213e5ec82fe9b45e5a9bef4864b Mon Sep 17 00:00:00 2001 From: Giulio Tantaro Date: Mon, 30 Dec 2024 21:03:31 +0100 Subject: [PATCH 736/757] add doctest for quick_sort_3_partition (#11779) --- sorts/quick_sort_3_partition.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/sorts/quick_sort_3_partition.py b/sorts/quick_sort_3_partition.py index 1a6db6a364f0..279b9a68f5a6 100644 --- a/sorts/quick_sort_3_partition.py +++ b/sorts/quick_sort_3_partition.py @@ -1,4 +1,27 @@ def quick_sort_3partition(sorting: list, left: int, right: int) -> None: + """ " + Python implementation of quick sort algorithm with 3-way partition. + The idea of 3-way quick sort is based on "Dutch National Flag algorithm". + + :param sorting: sort list + :param left: left endpoint of sorting + :param right: right endpoint of sorting + :return: None + + Examples: + >>> array1 = [5, -1, -1, 5, 5, 24, 0] + >>> quick_sort_3partition(array1, 0, 6) + >>> array1 + [-1, -1, 0, 5, 5, 5, 24] + >>> array2 = [9, 0, 2, 6] + >>> quick_sort_3partition(array2, 0, 3) + >>> array2 + [0, 2, 6, 9] + >>> array3 = [] + >>> quick_sort_3partition(array3, 0, 0) + >>> array3 + [] + """ if right <= left: return a = i = left From 8767d1d72436b8aff89f9c11d045ad95bec02ba4 Mon Sep 17 00:00:00 2001 From: Rodrigo Castro Date: Mon, 30 Dec 2024 21:36:41 -0300 Subject: [PATCH 737/757] add some documentation for heap sort (#9949) * add some documentation * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix typing * Update heap_sort.py * Update heap_sort.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- sorts/heap_sort.py | 47 +++++++++++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/sorts/heap_sort.py b/sorts/heap_sort.py index 4dca879bd89c..44ee1d4b39f1 100644 --- a/sorts/heap_sort.py +++ b/sorts/heap_sort.py @@ -1,17 +1,22 @@ """ -This is a pure Python implementation of the heap sort algorithm. - -For doctests run following command: -python -m doctest -v heap_sort.py -or -python3 -m doctest -v heap_sort.py - -For manual testing run: -python heap_sort.py +A pure Python implementation of the heap sort algorithm. """ -def heapify(unsorted, index, heap_size): +def heapify(unsorted: list[int], index: int, heap_size: int) -> None: + """ + :param unsorted: unsorted list containing integers numbers + :param index: index + :param heap_size: size of the heap + :return: None + >>> unsorted = [1, 4, 3, 5, 2] + >>> heapify(unsorted, 0, len(unsorted)) + >>> unsorted + [4, 5, 3, 1, 2] + >>> heapify(unsorted, 0, len(unsorted)) + >>> unsorted + [5, 4, 3, 1, 2] + """ largest = index left_index = 2 * index + 1 right_index = 2 * index + 2 @@ -22,26 +27,26 @@ def heapify(unsorted, index, heap_size): largest = right_index if largest != index: - unsorted[largest], unsorted[index] = unsorted[index], unsorted[largest] + unsorted[largest], unsorted[index] = (unsorted[index], unsorted[largest]) heapify(unsorted, largest, heap_size) -def heap_sort(unsorted): +def heap_sort(unsorted: list[int]) -> list[int]: """ - Pure implementation of the heap sort algorithm in Python - :param collection: some mutable ordered collection with heterogeneous - comparable items inside + A pure Python implementation of the heap sort algorithm + + :param collection: a mutable ordered collection of heterogeneous comparable items :return: the same collection ordered by ascending Examples: >>> heap_sort([0, 5, 3, 2, 2]) [0, 2, 2, 3, 5] - >>> heap_sort([]) [] - >>> heap_sort([-2, -5, -45]) [-45, -5, -2] + >>> heap_sort([3, 7, 9, 28, 123, -5, 8, -30, -200, 0, 4]) + [-200, -30, -5, 0, 3, 4, 7, 8, 9, 28, 123] """ n = len(unsorted) for i in range(n // 2 - 1, -1, -1): @@ -53,6 +58,10 @@ def heap_sort(unsorted): if __name__ == "__main__": + import doctest + + doctest.testmod() user_input = input("Enter numbers separated by a comma:\n").strip() - unsorted = [int(item) for item in user_input.split(",")] - print(heap_sort(unsorted)) + if user_input: + unsorted = [int(item) for item in user_input.split(",")] + print(f"{heap_sort(unsorted) = }") From 8439fa8d1da94370250d153cd57f9bdcc382a062 Mon Sep 17 00:00:00 2001 From: Paarth Goyal <138299656+pluto-tofu@users.noreply.github.com> Date: Tue, 31 Dec 2024 06:17:41 +0530 Subject: [PATCH 738/757] Added the algorithm to compute the time period of a simple pendulum (#10265) * Added the algorithm to compute the time period of a simple pendulum * imported g form scipy and changed doctests accordingly * fixed formatting * applied all suggested changes from code review * Apply suggestions from code review * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: Tianyi Zheng Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- physics/period_of_pendulum.py | 53 +++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 physics/period_of_pendulum.py diff --git a/physics/period_of_pendulum.py b/physics/period_of_pendulum.py new file mode 100644 index 000000000000..2e3c7bc3ef1e --- /dev/null +++ b/physics/period_of_pendulum.py @@ -0,0 +1,53 @@ +""" +Title : Computing the time period of a simple pendulum + +The simple pendulum is a mechanical system that sways or moves in an +oscillatory motion. The simple pendulum comprises of a small bob of +mass m suspended by a thin string of length L and secured to a platform +at its upper end. Its motion occurs in a vertical plane and is mainly +driven by gravitational force. The period of the pendulum depends on the +length of the string and the amplitude (the maximum angle) of oscillation. +However, the effect of the amplitude can be ignored if the amplitude is +small. It should be noted that the period does not depend on the mass of +the bob. + +For small amplitudes, the period of a simple pendulum is given by the +following approximation: +T ≈ 2π * √(L / g) + +where: +L = length of string from which the bob is hanging (in m) +g = acceleration due to gravity (approx 9.8 m/s²) + +Reference : https://byjus.com/jee/simple-pendulum/ +""" + +from math import pi + +from scipy.constants import g + + +def period_of_pendulum(length: float) -> float: + """ + >>> period_of_pendulum(1.23) + 2.2252155506257845 + >>> period_of_pendulum(2.37) + 3.0888278441908574 + >>> period_of_pendulum(5.63) + 4.76073193364765 + >>> period_of_pendulum(-12) + Traceback (most recent call last): + ... + ValueError: The length should be non-negative + >>> period_of_pendulum(0) + 0.0 + """ + if length < 0: + raise ValueError("The length should be non-negative") + return 2 * pi * (length / g) ** 0.5 + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From a4399022e516dfba1097f91cedc0b7f4213bab84 Mon Sep 17 00:00:00 2001 From: Julien Richard Date: Tue, 31 Dec 2024 02:11:29 +0100 Subject: [PATCH 739/757] chore: improve comments and add tests to trapezoidal rule (#11640) * chore: improve comments and add tests to trapezoidal rule * fix: too much characters in line * Update maths/trapezoidal_rule.py Co-authored-by: Tianyi Zheng * Update maths/trapezoidal_rule.py Co-authored-by: Tianyi Zheng * Update maths/trapezoidal_rule.py Co-authored-by: Tianyi Zheng * Update maths/trapezoidal_rule.py Co-authored-by: Tianyi Zheng * fix: change function name in calls * modify tests, changes numbers to remove coma * updating DIRECTORY.md * Fix doctest whitespace * Try to fix line length in doctest --------- Co-authored-by: Tianyi Zheng Co-authored-by: tianyizheng02 --- DIRECTORY.md | 3 ++ maths/trapezoidal_rule.py | 97 ++++++++++++++++++++++----------------- 2 files changed, 58 insertions(+), 42 deletions(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index 44d0414a37c8..1248a290d294 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -142,6 +142,7 @@ * [Haralick Descriptors](computer_vision/haralick_descriptors.py) * [Harris Corner](computer_vision/harris_corner.py) * [Horn Schunck](computer_vision/horn_schunck.py) + * [Intensity Based Segmentation](computer_vision/intensity_based_segmentation.py) * [Mean Threshold](computer_vision/mean_threshold.py) * [Mosaic Augmentation](computer_vision/mosaic_augmentation.py) * [Pooling Functions](computer_vision/pooling_functions.py) @@ -507,6 +508,7 @@ * [Kahns Algorithm Long](graphs/kahns_algorithm_long.py) * [Kahns Algorithm Topo](graphs/kahns_algorithm_topo.py) * [Karger](graphs/karger.py) + * [Lanczos Eigenvectors](graphs/lanczos_eigenvectors.py) * [Markov Chain](graphs/markov_chain.py) * [Matching Min Vertex Cover](graphs/matching_min_vertex_cover.py) * [Minimum Path Sum](graphs/minimum_path_sum.py) @@ -886,6 +888,7 @@ * [N Body Simulation](physics/n_body_simulation.py) * [Newtons Law Of Gravitation](physics/newtons_law_of_gravitation.py) * [Newtons Second Law Of Motion](physics/newtons_second_law_of_motion.py) + * [Period Of Pendulum](physics/period_of_pendulum.py) * [Photoelectric Effect](physics/photoelectric_effect.py) * [Potential Energy](physics/potential_energy.py) * [Rainfall Intensity](physics/rainfall_intensity.py) diff --git a/maths/trapezoidal_rule.py b/maths/trapezoidal_rule.py index 0186629ee378..21b10b239b5f 100644 --- a/maths/trapezoidal_rule.py +++ b/maths/trapezoidal_rule.py @@ -1,28 +1,25 @@ """ Numerical integration or quadrature for a smooth function f with known values at x_i - -This method is the classical approach of suming 'Equally Spaced Abscissas' - -method 1: -"extended trapezoidal rule" -int(f) = dx/2 * (f1 + 2f2 + ... + fn) - """ -def method_1(boundary, steps): +def trapezoidal_rule(boundary, steps): """ - Apply the extended trapezoidal rule to approximate the integral of function f(x) - over the interval defined by 'boundary' with the number of 'steps'. - - Args: - boundary (list of floats): A list containing the start and end values [a, b]. - steps (int): The number of steps or subintervals. - Returns: - float: Approximation of the integral of f(x) over [a, b]. - Examples: - >>> method_1([0, 1], 10) - 0.3349999999999999 + Implements the extended trapezoidal rule for numerical integration. + The function f(x) is provided below. + + :param boundary: List containing the lower and upper bounds of integration [a, b] + :param steps: The number of steps (intervals) used in the approximation + :return: The numerical approximation of the integral + + >>> abs(trapezoidal_rule([0, 1], 10) - 0.33333) < 0.01 + True + >>> abs(trapezoidal_rule([0, 1], 100) - 0.33333) < 0.01 + True + >>> abs(trapezoidal_rule([0, 2], 1000) - 2.66667) < 0.01 + True + >>> abs(trapezoidal_rule([1, 2], 1000) - 2.33333) < 0.01 + True """ h = (boundary[1] - boundary[0]) / steps a = boundary[0] @@ -31,7 +28,6 @@ def method_1(boundary, steps): y = 0.0 y += (h / 2.0) * f(a) for i in x_i: - # print(i) y += h * f(i) y += (h / 2.0) * f(b) return y @@ -39,49 +35,66 @@ def method_1(boundary, steps): def make_points(a, b, h): """ - Generates points between 'a' and 'b' with step size 'h', excluding the end points. - Args: - a (float): Start value - b (float): End value - h (float): Step size - Examples: + Generates points between a and b with step size h for trapezoidal integration. + + :param a: The lower bound of integration + :param b: The upper bound of integration + :param h: The step size + :yield: The next x-value in the range (a, b) + + >>> list(make_points(0, 1, 0.1)) # doctest: +NORMALIZE_WHITESPACE + [0.1, 0.2, 0.30000000000000004, 0.4, 0.5, 0.6, 0.7, 0.7999999999999999, \ + 0.8999999999999999] >>> list(make_points(0, 10, 2.5)) [2.5, 5.0, 7.5] - >>> list(make_points(0, 10, 2)) [2, 4, 6, 8] - >>> list(make_points(1, 21, 5)) [6, 11, 16] - >>> list(make_points(1, 5, 2)) [3] - >>> list(make_points(1, 4, 3)) [] """ x = a + h while x <= (b - h): yield x - x = x + h + x += h -def f(x): # enter your function here +def f(x): """ - Example: - >>> f(2) - 4 + This is the function to integrate, f(x) = (x - 0)^2 = x^2. + + :param x: The input value + :return: The value of f(x) + + >>> f(0) + 0 + >>> f(1) + 1 + >>> f(0.5) + 0.25 """ - y = (x - 0) * (x - 0) - return y + return x**2 def main(): - a = 0.0 # Lower bound of integration - b = 1.0 # Upper bound of integration - steps = 10.0 # define number of steps or resolution - boundary = [a, b] # define boundary of integration - y = method_1(boundary, steps) + """ + Main function to test the trapezoidal rule. + :a: Lower bound of integration + :b: Upper bound of integration + :steps: define number of steps or resolution + :boundary: define boundary of integration + + >>> main() + y = 0.3349999999999999 + """ + a = 0.0 + b = 1.0 + steps = 10.0 + boundary = [a, b] + y = trapezoidal_rule(boundary, steps) print(f"y = {y}") From 91a22c2e36477623b1f81518ff18c6f8617f81fb Mon Sep 17 00:00:00 2001 From: SEIKH NABAB UDDIN <93948993+nababuddin@users.noreply.github.com> Date: Tue, 31 Dec 2024 07:39:14 +0530 Subject: [PATCH 740/757] Create digital differential analyzer_line.py (#10929) * Create DDA_line_drawing.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Rename DDA_line_drawing.py to digital differential analyzer_line_drawing.py * Rename DDA_line_drawing.py to digital_differential_analyzer_line_drawing.py * Update digital_differential_analyzer_line_drawing.py * Update digital_differential_analyzer_line_drawing.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update digital_differential_analyzer_line_drawing.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update digital_differential_analyzer_line_drawing.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update digital_differential_analyzer_line_drawing.py * Update digital_differential_analyzer_line_drawing.py * Update digital_differential_analyzer_line_drawing.py * Update digital_differential_analyzer_line_drawing.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Apply suggestions from code review Co-authored-by: Tianyi Zheng * Update and rename digital_differential_analyzer_line_drawing.py to digital_differential_analyzer_line.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update digital_differential_analyzer_line.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update digital_differential_analyzer_line.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update digital_differential_analyzer_line.py * Update digital_differential_analyzer_line.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update digital_differential_analyzer_line.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update digital_differential_analyzer_line.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update digital_differential_analyzer_line.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Apply suggestions from code review * Fix doctest * Trigger GH workflows * Fix function call in main block --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Tianyi Zheng --- .../digital_differential_analyzer_line.py | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 graphics/digital_differential_analyzer_line.py diff --git a/graphics/digital_differential_analyzer_line.py b/graphics/digital_differential_analyzer_line.py new file mode 100644 index 000000000000..a51cb0b8dc37 --- /dev/null +++ b/graphics/digital_differential_analyzer_line.py @@ -0,0 +1,52 @@ +import matplotlib.pyplot as plt + + +def digital_differential_analyzer_line( + p1: tuple[int, int], p2: tuple[int, int] +) -> list[tuple[int, int]]: + """ + Draws a line between two points using the DDA algorithm. + + Args: + - p1: Coordinates of the starting point. + - p2: Coordinates of the ending point. + Returns: + - List of coordinate points that form the line. + + >>> digital_differential_analyzer_line((1, 1), (4, 4)) + [(2, 2), (3, 3), (4, 4)] + """ + x1, y1 = p1 + x2, y2 = p2 + dx = x2 - x1 + dy = y2 - y1 + steps = max(abs(dx), abs(dy)) + x_increment = dx / float(steps) + y_increment = dy / float(steps) + coordinates = [] + x: float = x1 + y: float = y1 + for _ in range(steps): + x += x_increment + y += y_increment + coordinates.append((int(round(x)), int(round(y)))) + return coordinates + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + + x1 = int(input("Enter the x-coordinate of the starting point: ")) + y1 = int(input("Enter the y-coordinate of the starting point: ")) + x2 = int(input("Enter the x-coordinate of the ending point: ")) + y2 = int(input("Enter the y-coordinate of the ending point: ")) + coordinates = digital_differential_analyzer_line((x1, y1), (x2, y2)) + x_points, y_points = zip(*coordinates) + plt.plot(x_points, y_points, marker="o") + plt.title("Digital Differential Analyzer Line Drawing Algorithm") + plt.xlabel("X-axis") + plt.ylabel("Y-axis") + plt.grid() + plt.show() From 12b1023a9d97ca19be761c10129cba5509c9b450 Mon Sep 17 00:00:00 2001 From: Kaustubh Mani Tripathi <129510465+kmtGryffindor20@users.noreply.github.com> Date: Tue, 31 Dec 2024 07:46:32 +0530 Subject: [PATCH 741/757] [ADDED] Implementation of Geometric Mean. (#10421) * [ADDED] Implementation of Geometric Mean. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Rectified type hints * Typo * Apply suggestions from code review --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Tianyi Zheng --- maths/geometric_mean.py | 55 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 maths/geometric_mean.py diff --git a/maths/geometric_mean.py b/maths/geometric_mean.py new file mode 100644 index 000000000000..240d519ad398 --- /dev/null +++ b/maths/geometric_mean.py @@ -0,0 +1,55 @@ +""" +The Geometric Mean of n numbers is defined as the n-th root of the product +of those numbers. It is used to measure the central tendency of the numbers. +https://en.wikipedia.org/wiki/Geometric_mean +""" + + +def compute_geometric_mean(*args: int) -> float: + """ + Return the geometric mean of the argument numbers. + >>> compute_geometric_mean(2,8) + 4.0 + >>> compute_geometric_mean('a', 4) + Traceback (most recent call last): + ... + TypeError: Not a Number + >>> compute_geometric_mean(5, 125) + 25.0 + >>> compute_geometric_mean(1, 0) + 0.0 + >>> compute_geometric_mean(1, 5, 25, 5) + 5.0 + >>> compute_geometric_mean(2, -2) + Traceback (most recent call last): + ... + ArithmeticError: Cannot Compute Geometric Mean for these numbers. + >>> compute_geometric_mean(-5, 25, 1) + -5.0 + """ + product = 1 + for number in args: + if not isinstance(number, int) and not isinstance(number, float): + raise TypeError("Not a Number") + product *= number + # Cannot calculate the even root for negative product. + # Frequently they are restricted to being positive. + if product < 0 and len(args) % 2 == 0: + raise ArithmeticError("Cannot Compute Geometric Mean for these numbers.") + mean = abs(product) ** (1 / len(args)) + # Since python calculates complex roots for negative products with odd roots. + if product < 0: + mean = -mean + # Since it does floating point arithmetic, it gives 64**(1/3) as 3.99999996 + possible_mean = float(round(mean)) + # To check if the rounded number is actually the mean. + if possible_mean ** len(args) == product: + mean = possible_mean + return mean + + +if __name__ == "__main__": + from doctest import testmod + + testmod(name="compute_geometric_mean") + print(compute_geometric_mean(-3, -27)) From bae33acf9008aa6c80351b3c68f492ba0c4a1352 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 6 Jan 2025 21:12:13 +0100 Subject: [PATCH 742/757] [pre-commit.ci] pre-commit autoupdate (#12507) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/astral-sh/ruff-pre-commit: v0.8.4 → v0.8.6](https://github.com/astral-sh/ruff-pre-commit/compare/v0.8.4...v0.8.6) - [github.com/pre-commit/mirrors-mypy: v1.14.0 → v1.14.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.14.0...v1.14.1) * updating DIRECTORY.md --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] --- .pre-commit-config.yaml | 4 ++-- DIRECTORY.md | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 71ac72c29b5f..ec1dbca3a41c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.8.4 + rev: v0.8.6 hooks: - id: ruff - id: ruff-format @@ -47,7 +47,7 @@ repos: - id: validate-pyproject - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.14.0 + rev: v1.14.1 hooks: - id: mypy args: diff --git a/DIRECTORY.md b/DIRECTORY.md index 1248a290d294..3f0a5dbb140f 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -462,6 +462,7 @@ ## Graphics * [Bezier Curve](graphics/bezier_curve.py) + * [Digital Differential Analyzer Line](graphics/digital_differential_analyzer_line.py) * [Vector3 For 2D Rendering](graphics/vector3_for_2d_rendering.py) ## Graphs @@ -663,6 +664,7 @@ * [Gamma](maths/gamma.py) * [Gaussian](maths/gaussian.py) * [Gcd Of N Numbers](maths/gcd_of_n_numbers.py) + * [Geometric Mean](maths/geometric_mean.py) * [Germain Primes](maths/germain_primes.py) * [Greatest Common Divisor](maths/greatest_common_divisor.py) * [Hardy Ramanujanalgo](maths/hardy_ramanujanalgo.py) From b653aee627a95de423f1cad97f283de904271ff7 Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Sun, 12 Jan 2025 19:05:08 +0300 Subject: [PATCH 743/757] Fix ruff (#12515) * Empty commit * Fix * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix * Fix * Fix * updating DIRECTORY.md --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: MaximSmolskiy --- DIRECTORY.md | 4 ++-- ciphers/{base64.py => base64_cipher.py} | 0 project_euler/problem_002/sol4.py | 2 +- strings/{wave.py => wave_string.py} | 0 4 files changed, 3 insertions(+), 3 deletions(-) rename ciphers/{base64.py => base64_cipher.py} (100%) rename strings/{wave.py => wave_string.py} (100%) diff --git a/DIRECTORY.md b/DIRECTORY.md index 3f0a5dbb140f..aad6c72aa8ee 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -86,7 +86,7 @@ * [Baconian Cipher](ciphers/baconian_cipher.py) * [Base16](ciphers/base16.py) * [Base32](ciphers/base32.py) - * [Base64](ciphers/base64.py) + * [Base64 Cipher](ciphers/base64_cipher.py) * [Base85](ciphers/base85.py) * [Beaufort Cipher](ciphers/beaufort_cipher.py) * [Bifid](ciphers/bifid.py) @@ -1331,7 +1331,7 @@ * [Title](strings/title.py) * [Top K Frequent Words](strings/top_k_frequent_words.py) * [Upper](strings/upper.py) - * [Wave](strings/wave.py) + * [Wave String](strings/wave_string.py) * [Wildcard Pattern Matching](strings/wildcard_pattern_matching.py) * [Word Occurrence](strings/word_occurrence.py) * [Word Patterns](strings/word_patterns.py) diff --git a/ciphers/base64.py b/ciphers/base64_cipher.py similarity index 100% rename from ciphers/base64.py rename to ciphers/base64_cipher.py diff --git a/project_euler/problem_002/sol4.py b/project_euler/problem_002/sol4.py index 3a2e4fce341c..a13d34fd760e 100644 --- a/project_euler/problem_002/sol4.py +++ b/project_euler/problem_002/sol4.py @@ -61,7 +61,7 @@ def solution(n: int = 4000000) -> int: if n <= 0: raise ValueError("Parameter n must be greater than or equal to one.") getcontext().prec = 100 - phi = (Decimal(5) ** Decimal(0.5) + 1) / Decimal(2) + phi = (Decimal(5) ** Decimal("0.5") + 1) / Decimal(2) index = (math.floor(math.log(n * (phi + 2), phi) - 1) // 3) * 3 + 2 num = Decimal(round(phi ** Decimal(index + 1))) / (phi + 2) diff --git a/strings/wave.py b/strings/wave_string.py similarity index 100% rename from strings/wave.py rename to strings/wave_string.py From 4c92de5e03310811a376058e110db8d615769087 Mon Sep 17 00:00:00 2001 From: Sanjay Muthu Date: Mon, 13 Jan 2025 05:05:22 +0530 Subject: [PATCH 744/757] Fix dynamic_programming/longest_increasing_subsequence.py (#12517) * Fix #12510 * Added the doctest mentioned in the issue * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fixed Grammer Mistake * Update longest_increasing_subsequence.py * Update longest_increasing_subsequence.py * Update longest_increasing_subsequence.py * Update longest_increasing_subsequence.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Maxim Smolskiy --- dynamic_programming/longest_increasing_subsequence.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/dynamic_programming/longest_increasing_subsequence.py b/dynamic_programming/longest_increasing_subsequence.py index d839757f6da5..1863a882c41e 100644 --- a/dynamic_programming/longest_increasing_subsequence.py +++ b/dynamic_programming/longest_increasing_subsequence.py @@ -24,8 +24,10 @@ def longest_subsequence(array: list[int]) -> list[int]: # This function is recu [10, 22, 33, 41, 60, 80] >>> longest_subsequence([4, 8, 7, 5, 1, 12, 2, 3, 9]) [1, 2, 3, 9] + >>> longest_subsequence([28, 26, 12, 23, 35, 39]) + [12, 23, 35, 39] >>> longest_subsequence([9, 8, 7, 6, 5, 7]) - [8] + [5, 7] >>> longest_subsequence([1, 1, 1]) [1, 1, 1] >>> longest_subsequence([]) @@ -44,7 +46,7 @@ def longest_subsequence(array: list[int]) -> list[int]: # This function is recu while not is_found and i < array_length: if array[i] < pivot: is_found = True - temp_array = [element for element in array[i:] if element >= array[i]] + temp_array = array[i:] temp_array = longest_subsequence(temp_array) if len(temp_array) > len(longest_subseq): longest_subseq = temp_array From 787aa5d3b59640b2d9161b56ca8fde763597efe4 Mon Sep 17 00:00:00 2001 From: Siddhant <87547498+Siddhant231xyz@users.noreply.github.com> Date: Sun, 12 Jan 2025 20:54:23 -0500 Subject: [PATCH 745/757] doctest all_combinations.py (#12506) * doctest in all_combinations.py * added doctest in all_combinations.py * doctests in all_combinations.py * add doctest all_combinations.py * add --------- Co-authored-by: Siddhant Jain --- backtracking/all_combinations.py | 34 ++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/backtracking/all_combinations.py b/backtracking/all_combinations.py index 390decf3a05b..1d15c6263e14 100644 --- a/backtracking/all_combinations.py +++ b/backtracking/all_combinations.py @@ -12,6 +12,8 @@ def combination_lists(n: int, k: int) -> list[list[int]]: """ + Generates all possible combinations of k numbers out of 1 ... n using itertools. + >>> combination_lists(n=4, k=2) [[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]] """ @@ -20,6 +22,8 @@ def combination_lists(n: int, k: int) -> list[list[int]]: def generate_all_combinations(n: int, k: int) -> list[list[int]]: """ + Generates all possible combinations of k numbers out of 1 ... n using backtracking. + >>> generate_all_combinations(n=4, k=2) [[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]] >>> generate_all_combinations(n=0, k=0) @@ -34,6 +38,14 @@ def generate_all_combinations(n: int, k: int) -> list[list[int]]: ValueError: n must not be negative >>> generate_all_combinations(n=5, k=4) [[1, 2, 3, 4], [1, 2, 3, 5], [1, 2, 4, 5], [1, 3, 4, 5], [2, 3, 4, 5]] + >>> generate_all_combinations(n=3, k=3) + [[1, 2, 3]] + >>> generate_all_combinations(n=3, k=1) + [[1], [2], [3]] + >>> generate_all_combinations(n=1, k=0) + [[]] + >>> generate_all_combinations(n=1, k=1) + [[1]] >>> from itertools import combinations >>> all(generate_all_combinations(n, k) == combination_lists(n, k) ... for n in range(1, 6) for k in range(1, 6)) @@ -56,6 +68,28 @@ def create_all_state( current_list: list[int], total_list: list[list[int]], ) -> None: + """ + Helper function to recursively build all combinations. + + >>> create_all_state(1, 4, 2, [], result := []) + >>> result + [[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]] + >>> create_all_state(1, 3, 3, [], result := []) + >>> result + [[1, 2, 3]] + >>> create_all_state(2, 2, 1, [1], result := []) + >>> result + [[1, 2]] + >>> create_all_state(1, 0, 0, [], result := []) + >>> result + [[]] + >>> create_all_state(1, 4, 0, [1, 2], result := []) + >>> result + [[1, 2]] + >>> create_all_state(5, 4, 2, [1, 2], result := []) + >>> result + [] + """ if level == 0: total_list.append(current_list[:]) return From cfcc84edf7d14cb56f52ba6fbd8c8deb2e9a7852 Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Mon, 13 Jan 2025 23:49:07 +0300 Subject: [PATCH 746/757] Fix build (#12516) * Empty commit * Fix * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix * Apply suggestions from code review --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- web_programming/current_stock_price.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/web_programming/current_stock_price.py b/web_programming/current_stock_price.py index d0a65e9aac84..573e1f575c8e 100644 --- a/web_programming/current_stock_price.py +++ b/web_programming/current_stock_price.py @@ -15,7 +15,7 @@ def stock_price(symbol: str = "AAPL") -> str: """ >>> stock_price("EEEE") - '-' + '- ' >>> isinstance(float(stock_price("GOOG")),float) True """ @@ -24,12 +24,10 @@ def stock_price(symbol: str = "AAPL") -> str: url, headers={"USER-AGENT": "Mozilla/5.0"}, timeout=10 ).text soup = BeautifulSoup(yahoo_finance_source, "html.parser") - specific_fin_streamer_tag = soup.find("fin-streamer", {"data-testid": "qsp-price"}) - if specific_fin_streamer_tag: - text = specific_fin_streamer_tag.get_text() - return text - return "No tag with the specified data-test attribute found." + if specific_fin_streamer_tag := soup.find("span", {"data-testid": "qsp-price"}): + return specific_fin_streamer_tag.get_text() + return "No tag with the specified data-testid attribute found." # Search for the symbol at https://finance.yahoo.com/lookup From 4fe50bc1fcf82fceb61839bae314720c092c0692 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 13 Jan 2025 21:52:12 +0100 Subject: [PATCH 747/757] [pre-commit.ci] pre-commit autoupdate -- ruff 2025 stable format (#12521) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/astral-sh/ruff-pre-commit: v0.8.6 → v0.9.1](https://github.com/astral-sh/ruff-pre-commit/compare/v0.8.6...v0.9.1) * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update maths/dual_number_automatic_differentiation.py * Update maths/dual_number_automatic_differentiation.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update dual_number_automatic_differentiation.py * Update dual_number_automatic_differentiation.py * No tag with the specified data-test attribute found. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- .pre-commit-config.yaml | 2 +- ciphers/base64_cipher.py | 12 +++--- ciphers/caesar_cipher.py | 2 +- computer_vision/flip_augmentation.py | 2 +- computer_vision/mosaic_augmentation.py | 2 +- .../hashing/number_theory/prime_numbers.py | 6 +-- data_structures/heap/min_heap.py | 6 +-- data_structures/kd_tree/tests/test_kdtree.py | 12 +++--- .../suffix_tree/tests/test_suffix_tree.py | 24 +++++------ dynamic_programming/climbing_stairs.py | 6 +-- .../iterating_through_submasks.py | 6 +-- .../matrix_chain_multiplication.py | 2 +- .../linear_discriminant_analysis.py | 4 +- .../dual_number_automatic_differentiation.py | 6 +-- maths/max_sum_sliding_window.py | 4 +- .../integration_by_simpson_approx.py | 12 +++--- maths/prime_check.py | 12 +++--- maths/primelib.py | 42 +++++++++---------- matrix/matrix_based_game.py | 2 +- neural_network/input_data.py | 6 +-- scripts/validate_solutions.py | 6 +-- strings/jaro_winkler.py | 4 +- web_programming/fetch_anime_and_play.py | 4 +- 23 files changed, 93 insertions(+), 91 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ec1dbca3a41c..3b1dd9658d7f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.8.6 + rev: v0.9.1 hooks: - id: ruff - id: ruff-format diff --git a/ciphers/base64_cipher.py b/ciphers/base64_cipher.py index 2b950b1be37d..038d13963d95 100644 --- a/ciphers/base64_cipher.py +++ b/ciphers/base64_cipher.py @@ -105,13 +105,13 @@ def base64_decode(encoded_data: str) -> bytes: # Check if the encoded string contains non base64 characters if padding: - assert all( - char in B64_CHARSET for char in encoded_data[:-padding] - ), "Invalid base64 character(s) found." + assert all(char in B64_CHARSET for char in encoded_data[:-padding]), ( + "Invalid base64 character(s) found." + ) else: - assert all( - char in B64_CHARSET for char in encoded_data - ), "Invalid base64 character(s) found." + assert all(char in B64_CHARSET for char in encoded_data), ( + "Invalid base64 character(s) found." + ) # Check the padding assert len(encoded_data) % 4 == 0 and padding < 3, "Incorrect padding" diff --git a/ciphers/caesar_cipher.py b/ciphers/caesar_cipher.py index 9c096fe8a7da..1cf4d67cbaed 100644 --- a/ciphers/caesar_cipher.py +++ b/ciphers/caesar_cipher.py @@ -225,7 +225,7 @@ def brute_force(input_string: str, alphabet: str | None = None) -> dict[int, str if __name__ == "__main__": while True: - print(f'\n{"-" * 10}\n Menu\n{"-" * 10}') + print(f"\n{'-' * 10}\n Menu\n{'-' * 10}") print(*["1.Encrypt", "2.Decrypt", "3.BruteForce", "4.Quit"], sep="\n") # get user input diff --git a/computer_vision/flip_augmentation.py b/computer_vision/flip_augmentation.py index 77a8cbd7b14f..7301424824df 100644 --- a/computer_vision/flip_augmentation.py +++ b/computer_vision/flip_augmentation.py @@ -33,7 +33,7 @@ def main() -> None: file_name = paths[index].split(os.sep)[-1].rsplit(".", 1)[0] file_root = f"{OUTPUT_DIR}/{file_name}_FLIP_{letter_code}" cv2.imwrite(f"{file_root}.jpg", image, [cv2.IMWRITE_JPEG_QUALITY, 85]) - print(f"Success {index+1}/{len(new_images)} with {file_name}") + print(f"Success {index + 1}/{len(new_images)} with {file_name}") annos_list = [] for anno in new_annos[index]: obj = f"{anno[0]} {anno[1]} {anno[2]} {anno[3]} {anno[4]}" diff --git a/computer_vision/mosaic_augmentation.py b/computer_vision/mosaic_augmentation.py index cd923dfe095f..d881347121ea 100644 --- a/computer_vision/mosaic_augmentation.py +++ b/computer_vision/mosaic_augmentation.py @@ -41,7 +41,7 @@ def main() -> None: file_name = path.split(os.sep)[-1].rsplit(".", 1)[0] file_root = f"{OUTPUT_DIR}/{file_name}_MOSAIC_{letter_code}" cv2.imwrite(f"{file_root}.jpg", new_image, [cv2.IMWRITE_JPEG_QUALITY, 85]) - print(f"Succeeded {index+1}/{NUMBER_IMAGES} with {file_name}") + print(f"Succeeded {index + 1}/{NUMBER_IMAGES} with {file_name}") annos_list = [] for anno in new_annos: width = anno[3] - anno[1] diff --git a/data_structures/hashing/number_theory/prime_numbers.py b/data_structures/hashing/number_theory/prime_numbers.py index 2549a1477b2b..82071b5e9f09 100644 --- a/data_structures/hashing/number_theory/prime_numbers.py +++ b/data_structures/hashing/number_theory/prime_numbers.py @@ -32,9 +32,9 @@ def is_prime(number: int) -> bool: """ # precondition - assert isinstance(number, int) and ( - number >= 0 - ), "'number' must been an int and positive" + assert isinstance(number, int) and (number >= 0), ( + "'number' must been an int and positive" + ) if 1 < number < 4: # 2 and 3 are primes diff --git a/data_structures/heap/min_heap.py b/data_structures/heap/min_heap.py index ce7ed570a58d..577b98d788a1 100644 --- a/data_structures/heap/min_heap.py +++ b/data_structures/heap/min_heap.py @@ -124,9 +124,9 @@ def is_empty(self): return len(self.heap) == 0 def decrease_key(self, node, new_value): - assert ( - self.heap[self.idx_of_element[node]].val > new_value - ), "newValue must be less that current value" + assert self.heap[self.idx_of_element[node]].val > new_value, ( + "newValue must be less that current value" + ) node.val = new_value self.heap_dict[node.name] = new_value self.sift_up(self.idx_of_element[node]) diff --git a/data_structures/kd_tree/tests/test_kdtree.py b/data_structures/kd_tree/tests/test_kdtree.py index dce5e4f34ff4..d6a4a66dd24d 100644 --- a/data_structures/kd_tree/tests/test_kdtree.py +++ b/data_structures/kd_tree/tests/test_kdtree.py @@ -48,14 +48,14 @@ def test_build_kdtree(num_points, cube_size, num_dimensions, depth, expected_res assert kdtree is not None, "Expected a KDNode, got None" # Check if root has correct dimensions - assert ( - len(kdtree.point) == num_dimensions - ), f"Expected point dimension {num_dimensions}, got {len(kdtree.point)}" + assert len(kdtree.point) == num_dimensions, ( + f"Expected point dimension {num_dimensions}, got {len(kdtree.point)}" + ) # Check that the tree is balanced to some extent (simplistic check) - assert isinstance( - kdtree, KDNode - ), f"Expected KDNode instance, got {type(kdtree)}" + assert isinstance(kdtree, KDNode), ( + f"Expected KDNode instance, got {type(kdtree)}" + ) def test_nearest_neighbour_search(): diff --git a/data_structures/suffix_tree/tests/test_suffix_tree.py b/data_structures/suffix_tree/tests/test_suffix_tree.py index 45c6790ac48a..c9dbe199d19d 100644 --- a/data_structures/suffix_tree/tests/test_suffix_tree.py +++ b/data_structures/suffix_tree/tests/test_suffix_tree.py @@ -22,18 +22,18 @@ def test_search_existing_patterns(self) -> None: patterns = ["ana", "ban", "na"] for pattern in patterns: with self.subTest(pattern=pattern): - assert self.suffix_tree.search( - pattern - ), f"Pattern '{pattern}' should be found." + assert self.suffix_tree.search(pattern), ( + f"Pattern '{pattern}' should be found." + ) def test_search_non_existing_patterns(self) -> None: """Test searching for patterns that do not exist in the suffix tree.""" patterns = ["xyz", "apple", "cat"] for pattern in patterns: with self.subTest(pattern=pattern): - assert not self.suffix_tree.search( - pattern - ), f"Pattern '{pattern}' should not be found." + assert not self.suffix_tree.search(pattern), ( + f"Pattern '{pattern}' should not be found." + ) def test_search_empty_pattern(self) -> None: """Test searching for an empty pattern.""" @@ -41,18 +41,18 @@ def test_search_empty_pattern(self) -> None: def test_search_full_text(self) -> None: """Test searching for the full text.""" - assert self.suffix_tree.search( - self.text - ), "The full text should be found in the suffix tree." + assert self.suffix_tree.search(self.text), ( + "The full text should be found in the suffix tree." + ) def test_search_substrings(self) -> None: """Test searching for substrings of the full text.""" substrings = ["ban", "ana", "a", "na"] for substring in substrings: with self.subTest(substring=substring): - assert self.suffix_tree.search( - substring - ), f"Substring '{substring}' should be found." + assert self.suffix_tree.search(substring), ( + f"Substring '{substring}' should be found." + ) if __name__ == "__main__": diff --git a/dynamic_programming/climbing_stairs.py b/dynamic_programming/climbing_stairs.py index d6273d025f08..38bdb427eedc 100644 --- a/dynamic_programming/climbing_stairs.py +++ b/dynamic_programming/climbing_stairs.py @@ -25,9 +25,9 @@ def climb_stairs(number_of_steps: int) -> int: ... AssertionError: number_of_steps needs to be positive integer, your input -7 """ - assert ( - isinstance(number_of_steps, int) and number_of_steps > 0 - ), f"number_of_steps needs to be positive integer, your input {number_of_steps}" + assert isinstance(number_of_steps, int) and number_of_steps > 0, ( + f"number_of_steps needs to be positive integer, your input {number_of_steps}" + ) if number_of_steps == 1: return 1 previous, current = 1, 1 diff --git a/dynamic_programming/iterating_through_submasks.py b/dynamic_programming/iterating_through_submasks.py index 372dd2c74a71..efab6dacff3f 100644 --- a/dynamic_programming/iterating_through_submasks.py +++ b/dynamic_programming/iterating_through_submasks.py @@ -37,9 +37,9 @@ def list_of_submasks(mask: int) -> list[int]: """ - assert ( - isinstance(mask, int) and mask > 0 - ), f"mask needs to be positive integer, your input {mask}" + assert isinstance(mask, int) and mask > 0, ( + f"mask needs to be positive integer, your input {mask}" + ) """ first submask iterated will be mask itself then operation will be performed diff --git a/dynamic_programming/matrix_chain_multiplication.py b/dynamic_programming/matrix_chain_multiplication.py index 10e136b9f0db..4c0c771f9092 100644 --- a/dynamic_programming/matrix_chain_multiplication.py +++ b/dynamic_programming/matrix_chain_multiplication.py @@ -134,7 +134,7 @@ def elapsed_time(msg: str) -> Iterator: start = perf_counter_ns() yield - print(f"Finished: {msg} in {(perf_counter_ns() - start) / 10 ** 9} seconds.") + print(f"Finished: {msg} in {(perf_counter_ns() - start) / 10**9} seconds.") if __name__ == "__main__": diff --git a/machine_learning/linear_discriminant_analysis.py b/machine_learning/linear_discriminant_analysis.py index 86f28aef671a..8528ccbbae51 100644 --- a/machine_learning/linear_discriminant_analysis.py +++ b/machine_learning/linear_discriminant_analysis.py @@ -322,7 +322,7 @@ def main(): user_count = valid_input( input_type=int, condition=lambda x: x > 0, - input_msg=(f"Enter The number of instances for class_{i+1}: "), + input_msg=(f"Enter The number of instances for class_{i + 1}: "), err_msg="Number of instances should be positive!", ) counts.append(user_count) @@ -333,7 +333,7 @@ def main(): for a in range(n_classes): user_mean = valid_input( input_type=float, - input_msg=(f"Enter the value of mean for class_{a+1}: "), + input_msg=(f"Enter the value of mean for class_{a + 1}: "), err_msg="This is an invalid value.", ) user_means.append(user_mean) diff --git a/maths/dual_number_automatic_differentiation.py b/maths/dual_number_automatic_differentiation.py index f98997c8be4d..09aeb17a4aea 100644 --- a/maths/dual_number_automatic_differentiation.py +++ b/maths/dual_number_automatic_differentiation.py @@ -17,10 +17,8 @@ def __init__(self, real, rank): self.duals = rank def __repr__(self): - return ( - f"{self.real}+" - f"{'+'.join(str(dual)+'E'+str(n+1)for n,dual in enumerate(self.duals))}" - ) + s = "+".join(f"{dual}E{n}" for n, dual in enumerate(self.duals, 1)) + return f"{self.real}+{s}" def reduce(self): cur = self.duals.copy() diff --git a/maths/max_sum_sliding_window.py b/maths/max_sum_sliding_window.py index 090117429604..c7492978a6c9 100644 --- a/maths/max_sum_sliding_window.py +++ b/maths/max_sum_sliding_window.py @@ -43,4 +43,6 @@ def max_sum_in_array(array: list[int], k: int) -> int: testmod() array = [randint(-1000, 1000) for i in range(100)] k = randint(0, 110) - print(f"The maximum sum of {k} consecutive elements is {max_sum_in_array(array,k)}") + print( + f"The maximum sum of {k} consecutive elements is {max_sum_in_array(array, k)}" + ) diff --git a/maths/numerical_analysis/integration_by_simpson_approx.py b/maths/numerical_analysis/integration_by_simpson_approx.py index 934299997aac..043f3a9a72af 100644 --- a/maths/numerical_analysis/integration_by_simpson_approx.py +++ b/maths/numerical_analysis/integration_by_simpson_approx.py @@ -88,18 +88,18 @@ def simpson_integration(function, a: float, b: float, precision: int = 4) -> flo AssertionError: precision should be positive integer your input : -1 """ - assert callable( - function - ), f"the function(object) passed should be callable your input : {function}" + assert callable(function), ( + f"the function(object) passed should be callable your input : {function}" + ) assert isinstance(a, (float, int)), f"a should be float or integer your input : {a}" assert isinstance(function(a), (float, int)), ( "the function should return integer or float return type of your function, " f"{type(a)}" ) assert isinstance(b, (float, int)), f"b should be float or integer your input : {b}" - assert ( - isinstance(precision, int) and precision > 0 - ), f"precision should be positive integer your input : {precision}" + assert isinstance(precision, int) and precision > 0, ( + f"precision should be positive integer your input : {precision}" + ) # just applying the formula of simpson for approximate integration written in # mentioned article in first comment of this file and above this function diff --git a/maths/prime_check.py b/maths/prime_check.py index f1bc4def2469..a757c4108f24 100644 --- a/maths/prime_check.py +++ b/maths/prime_check.py @@ -73,12 +73,12 @@ def test_primes(self): def test_not_primes(self): with pytest.raises(ValueError): is_prime(-19) - assert not is_prime( - 0 - ), "Zero doesn't have any positive factors, primes must have exactly two." - assert not is_prime( - 1 - ), "One only has 1 positive factor, primes must have exactly two." + assert not is_prime(0), ( + "Zero doesn't have any positive factors, primes must have exactly two." + ) + assert not is_prime(1), ( + "One only has 1 positive factor, primes must have exactly two." + ) assert not is_prime(2 * 2) assert not is_prime(2 * 3) assert not is_prime(3 * 3) diff --git a/maths/primelib.py b/maths/primelib.py index a26b0eaeb328..3a966e5cd936 100644 --- a/maths/primelib.py +++ b/maths/primelib.py @@ -66,9 +66,9 @@ def is_prime(number: int) -> bool: """ # precondition - assert isinstance(number, int) and ( - number >= 0 - ), "'number' must been an int and positive" + assert isinstance(number, int) and (number >= 0), ( + "'number' must been an int and positive" + ) status = True @@ -254,9 +254,9 @@ def greatest_prime_factor(number): """ # precondition - assert isinstance(number, int) and ( - number >= 0 - ), "'number' must been an int and >= 0" + assert isinstance(number, int) and (number >= 0), ( + "'number' must been an int and >= 0" + ) ans = 0 @@ -296,9 +296,9 @@ def smallest_prime_factor(number): """ # precondition - assert isinstance(number, int) and ( - number >= 0 - ), "'number' must been an int and >= 0" + assert isinstance(number, int) and (number >= 0), ( + "'number' must been an int and >= 0" + ) ans = 0 @@ -399,9 +399,9 @@ def goldbach(number): """ # precondition - assert ( - isinstance(number, int) and (number > 2) and is_even(number) - ), "'number' must been an int, even and > 2" + assert isinstance(number, int) and (number > 2) and is_even(number), ( + "'number' must been an int, even and > 2" + ) ans = [] # this list will returned @@ -525,9 +525,9 @@ def kg_v(number1, number2): done.append(n) # precondition - assert isinstance(ans, int) and ( - ans >= 0 - ), "'ans' must been from type int and positive" + assert isinstance(ans, int) and (ans >= 0), ( + "'ans' must been from type int and positive" + ) return ans @@ -574,9 +574,9 @@ def get_prime(n): ans += 1 # precondition - assert isinstance(ans, int) and is_prime( - ans - ), "'ans' must been a prime number and from type int" + assert isinstance(ans, int) and is_prime(ans), ( + "'ans' must been a prime number and from type int" + ) return ans @@ -705,9 +705,9 @@ def is_perfect_number(number): """ # precondition - assert isinstance(number, int) and ( - number > 1 - ), "'number' must been an int and >= 1" + assert isinstance(number, int) and (number > 1), ( + "'number' must been an int and >= 1" + ) divisors = get_divisors(number) diff --git a/matrix/matrix_based_game.py b/matrix/matrix_based_game.py index 1ff0cbe93435..6181086c6704 100644 --- a/matrix/matrix_based_game.py +++ b/matrix/matrix_based_game.py @@ -273,7 +273,7 @@ def process_game(size: int, matrix: list[str], moves: list[tuple[int, int]]) -> size = int(input("Enter the size of the matrix: ")) validate_matrix_size(size) print(f"Enter the {size} rows of the matrix:") - matrix = [input(f"Row {i+1}: ") for i in range(size)] + matrix = [input(f"Row {i + 1}: ") for i in range(size)] validate_matrix_content(matrix, size) moves_input = input("Enter the moves (e.g., '0 0, 1 1'): ") moves = parse_moves(moves_input) diff --git a/neural_network/input_data.py b/neural_network/input_data.py index 72debabb566a..3a8628f939f8 100644 --- a/neural_network/input_data.py +++ b/neural_network/input_data.py @@ -160,9 +160,9 @@ def __init__( self._num_examples = 10000 self.one_hot = one_hot else: - assert ( - images.shape[0] == labels.shape[0] - ), f"images.shape: {images.shape} labels.shape: {labels.shape}" + assert images.shape[0] == labels.shape[0], ( + f"images.shape: {images.shape} labels.shape: {labels.shape}" + ) self._num_examples = images.shape[0] # Convert shape from [num examples, rows, columns, depth] diff --git a/scripts/validate_solutions.py b/scripts/validate_solutions.py index 325c245e0d77..df5d01086bbe 100755 --- a/scripts/validate_solutions.py +++ b/scripts/validate_solutions.py @@ -94,6 +94,6 @@ def test_project_euler(solution_path: pathlib.Path) -> None: solution_module = convert_path_to_module(solution_path) answer = str(solution_module.solution()) answer = hashlib.sha256(answer.encode()).hexdigest() - assert ( - answer == expected - ), f"Expected solution to {problem_number} to have hash {expected}, got {answer}" + assert answer == expected, ( + f"Expected solution to {problem_number} to have hash {expected}, got {answer}" + ) diff --git a/strings/jaro_winkler.py b/strings/jaro_winkler.py index cae2068fabc1..0ce5d83b3c41 100644 --- a/strings/jaro_winkler.py +++ b/strings/jaro_winkler.py @@ -33,7 +33,9 @@ def get_matched_characters(_str1: str, _str2: str) -> str: right = int(min(i + limit + 1, len(_str2))) if char in _str2[left:right]: matched.append(char) - _str2 = f"{_str2[0:_str2.index(char)]} {_str2[_str2.index(char) + 1:]}" + _str2 = ( + f"{_str2[0 : _str2.index(char)]} {_str2[_str2.index(char) + 1 :]}" + ) return "".join(matched) diff --git a/web_programming/fetch_anime_and_play.py b/web_programming/fetch_anime_and_play.py index fd7c3a3a7381..e56b7124eeb5 100644 --- a/web_programming/fetch_anime_and_play.py +++ b/web_programming/fetch_anime_and_play.py @@ -165,7 +165,7 @@ def get_anime_episode(episode_endpoint: str) -> list: print(f"Found {len(anime_list)} results: ") for i, anime in enumerate(anime_list): anime_title = anime["title"] - print(f"{i+1}. {anime_title}") + print(f"{i + 1}. {anime_title}") anime_choice = int(input("\nPlease choose from the following list: ").strip()) chosen_anime = anime_list[anime_choice - 1] @@ -177,7 +177,7 @@ def get_anime_episode(episode_endpoint: str) -> list: else: print(f"Found {len(episode_list)} results: ") for i, episode in enumerate(episode_list): - print(f"{i+1}. {episode['title']}") + print(f"{i + 1}. {episode['title']}") episode_choice = int(input("\nChoose an episode by serial no: ").strip()) chosen_episode = episode_list[episode_choice - 1] From f04d308431266759dce36265d8701dfb106932af Mon Sep 17 00:00:00 2001 From: Sanjay Muthu Date: Wed, 15 Jan 2025 02:19:04 +0530 Subject: [PATCH 748/757] Create longest_increasing_subsequence_iterative.py (#12524) * Create longest_increasing_subsequence_iterative.py * Update longest_increasing_subsequence_iterative.py * Update longest_increasing_subsequence_iterative.py --------- Co-authored-by: Maxim Smolskiy --- ...ongest_increasing_subsequence_iterative.py | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 dynamic_programming/longest_increasing_subsequence_iterative.py diff --git a/dynamic_programming/longest_increasing_subsequence_iterative.py b/dynamic_programming/longest_increasing_subsequence_iterative.py new file mode 100644 index 000000000000..665c86a35d2e --- /dev/null +++ b/dynamic_programming/longest_increasing_subsequence_iterative.py @@ -0,0 +1,72 @@ +""" +Author : Sanjay Muthu + +This is a pure Python implementation of Dynamic Programming solution to the longest +increasing subsequence of a given sequence. + +The problem is: + Given an array, to find the longest and increasing sub-array in that given array and + return it. + +Example: + ``[10, 22, 9, 33, 21, 50, 41, 60, 80]`` as input will return + ``[10, 22, 33, 50, 60, 80]`` as output +""" + +from __future__ import annotations + +import copy + + +def longest_subsequence(array: list[int]) -> list[int]: + """ + Some examples + + >>> longest_subsequence([10, 22, 9, 33, 21, 50, 41, 60, 80]) + [10, 22, 33, 50, 60, 80] + >>> longest_subsequence([4, 8, 7, 5, 1, 12, 2, 3, 9]) + [1, 2, 3, 9] + >>> longest_subsequence([9, 8, 7, 6, 5, 7]) + [7, 7] + >>> longest_subsequence([28, 26, 12, 23, 35, 39]) + [12, 23, 35, 39] + >>> longest_subsequence([1, 1, 1]) + [1, 1, 1] + >>> longest_subsequence([]) + [] + """ + n = len(array) + # The longest increasing subsequence ending at array[i] + longest_increasing_subsequence = [] + for i in range(n): + longest_increasing_subsequence.append([array[i]]) + + for i in range(1, n): + for prev in range(i): + # If array[prev] is less than or equal to array[i], then + # longest_increasing_subsequence[prev] + array[i] + # is a valid increasing subsequence + + # longest_increasing_subsequence[i] is only set to + # longest_increasing_subsequence[prev] + array[i] if the length is longer. + + if array[prev] <= array[i] and len( + longest_increasing_subsequence[prev] + ) + 1 > len(longest_increasing_subsequence[i]): + longest_increasing_subsequence[i] = copy.copy( + longest_increasing_subsequence[prev] + ) + longest_increasing_subsequence[i].append(array[i]) + + result: list[int] = [] + for i in range(n): + if len(longest_increasing_subsequence[i]) > len(result): + result = longest_increasing_subsequence[i] + + return result + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 0040ad47f928f299dadbb97c5cea00bc1daf8c75 Mon Sep 17 00:00:00 2001 From: aydinomer00 <109145643+aydinomer00@users.noreply.github.com> Date: Wed, 15 Jan 2025 00:24:36 +0300 Subject: [PATCH 749/757] Add butterfly pattern implementation (#12493) * Add butterfly pattern implementation * Add butterfly pattern implementation * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Add finalized butterfly pattern implementation and test * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Delete graphics/test_butterfly_pattern.py * Update butterfly_pattern.py * Update butterfly_pattern.py * Update butterfly_pattern.py * Update butterfly_pattern.py * Update butterfly_pattern.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Maxim Smolskiy --- graphics/butterfly_pattern.py | 46 +++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 graphics/butterfly_pattern.py diff --git a/graphics/butterfly_pattern.py b/graphics/butterfly_pattern.py new file mode 100644 index 000000000000..7913b03a7e95 --- /dev/null +++ b/graphics/butterfly_pattern.py @@ -0,0 +1,46 @@ +def butterfly_pattern(n: int) -> str: + """ + Creates a butterfly pattern of size n and returns it as a string. + + >>> print(butterfly_pattern(3)) + * * + ** ** + ***** + ** ** + * * + >>> print(butterfly_pattern(5)) + * * + ** ** + *** *** + **** **** + ********* + **** **** + *** *** + ** ** + * * + """ + result = [] + + # Upper part + for i in range(1, n): + left_stars = "*" * i + spaces = " " * (2 * (n - i) - 1) + right_stars = "*" * i + result.append(left_stars + spaces + right_stars) + + # Middle part + result.append("*" * (2 * n - 1)) + + # Lower part + for i in range(n - 1, 0, -1): + left_stars = "*" * i + spaces = " " * (2 * (n - i) - 1) + right_stars = "*" * i + result.append(left_stars + spaces + right_stars) + + return "\n".join(result) + + +if __name__ == "__main__": + n = int(input("Enter the size of the butterfly pattern: ")) + print(butterfly_pattern(n)) From 533767ff46bbcf5c594ff8196894ae2e8130bc3e Mon Sep 17 00:00:00 2001 From: Nguyen Thi Thanh Minh <140883075+minh-swinburne@users.noreply.github.com> Date: Sat, 18 Jan 2025 10:07:44 +0700 Subject: [PATCH 750/757] Doomsday Algorithm: Fix leap year check (#12396) * Fix leap year check Replace `!=` in `(year % 400) != 0` (line 49) with `==` Justification: Years that are divisible by 100 (centurian == 100) but not by 400 (year % 400 != 0) are skipped and NOT leap year. * Update parentheses Correct the parentheses to make clear the precedence of the conditional check * Update other/doomsday.py Co-authored-by: Tianyi Zheng --------- Co-authored-by: Tianyi Zheng --- other/doomsday.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/other/doomsday.py b/other/doomsday.py index d8fe261156a1..be3b18eeecaa 100644 --- a/other/doomsday.py +++ b/other/doomsday.py @@ -46,7 +46,7 @@ def get_week_day(year: int, month: int, day: int) -> str: ) % 7 day_anchor = ( DOOMSDAY_NOT_LEAP[month - 1] - if (year % 4 != 0) or (centurian == 0 and (year % 400) == 0) + if year % 4 != 0 or (centurian == 0 and year % 400 != 0) else DOOMSDAY_LEAP[month - 1] ) week_day = (dooms_day + day - day_anchor) % 7 From 91ebea1d99735ee2798b01ebcea0fc06e9a6af49 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Sun, 19 Jan 2025 08:33:35 +0100 Subject: [PATCH 751/757] Sphinx runs on ubuntu 24.04 arm (#12530) * Speed up our Sphinx GitHub Action with ARM # `runs-on: ubuntu-24.04-arm` https://docs.github.com/en/actions/using-github-hosted-runners/using-github-hosted-runners/about-github-hosted-runners#supported-runners-and-hardware-resources * updating DIRECTORY.md --------- Co-authored-by: cclauss --- .github/workflows/sphinx.yml | 2 +- DIRECTORY.md | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/sphinx.yml b/.github/workflows/sphinx.yml index d02435d98028..16ff284a74f2 100644 --- a/.github/workflows/sphinx.yml +++ b/.github/workflows/sphinx.yml @@ -23,7 +23,7 @@ concurrency: jobs: build_docs: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04-arm steps: - uses: actions/checkout@v4 - uses: astral-sh/setup-uv@v5 diff --git a/DIRECTORY.md b/DIRECTORY.md index aad6c72aa8ee..941e30dfe721 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -377,6 +377,7 @@ * [Longest Common Subsequence](dynamic_programming/longest_common_subsequence.py) * [Longest Common Substring](dynamic_programming/longest_common_substring.py) * [Longest Increasing Subsequence](dynamic_programming/longest_increasing_subsequence.py) + * [Longest Increasing Subsequence Iterative](dynamic_programming/longest_increasing_subsequence_iterative.py) * [Longest Increasing Subsequence O Nlogn](dynamic_programming/longest_increasing_subsequence_o_nlogn.py) * [Longest Palindromic Subsequence](dynamic_programming/longest_palindromic_subsequence.py) * [Matrix Chain Multiplication](dynamic_programming/matrix_chain_multiplication.py) @@ -462,6 +463,7 @@ ## Graphics * [Bezier Curve](graphics/bezier_curve.py) + * [Butterfly Pattern](graphics/butterfly_pattern.py) * [Digital Differential Analyzer Line](graphics/digital_differential_analyzer_line.py) * [Vector3 For 2D Rendering](graphics/vector3_for_2d_rendering.py) From 1f74db0c06df7557e7ae3a17ebcc303f753f824e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 20 Jan 2025 21:22:02 +0100 Subject: [PATCH 752/757] [pre-commit.ci] pre-commit autoupdate (#12536) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.9.1 → v0.9.2](https://github.com/astral-sh/ruff-pre-commit/compare/v0.9.1...v0.9.2) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3b1dd9658d7f..c4480f47faa1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.9.1 + rev: v0.9.2 hooks: - id: ruff - id: ruff-format From 9fb51b4169e0f7a4952e9eb460b91f4d7ffb819f Mon Sep 17 00:00:00 2001 From: Ronald Ngounou <74538524+ronaldngounou@users.noreply.github.com> Date: Thu, 23 Jan 2025 09:02:46 +0100 Subject: [PATCH 753/757] Update docstrings in the functions definitions. (#11797) --- data_structures/arrays/sudoku_solver.py | 61 +++++++++++++++++-------- 1 file changed, 43 insertions(+), 18 deletions(-) diff --git a/data_structures/arrays/sudoku_solver.py b/data_structures/arrays/sudoku_solver.py index fd1a4f3e37b8..e1714e57ece8 100644 --- a/data_structures/arrays/sudoku_solver.py +++ b/data_structures/arrays/sudoku_solver.py @@ -9,7 +9,9 @@ def cross(items_a, items_b): - "Cross product of elements in A and elements in B." + """ + Cross product of elements in A and elements in B. + """ return [a + b for a in items_a for b in items_b] @@ -27,7 +29,7 @@ def cross(items_a, items_b): def test(): - "A set of unit tests." + """A set of unit tests.""" assert len(squares) == 81 assert len(unitlist) == 27 assert all(len(units[s]) == 3 for s in squares) @@ -47,8 +49,10 @@ def test(): def parse_grid(grid): - """Convert grid to a dict of possible values, {square: digits}, or - return False if a contradiction is detected.""" + """ + Convert grid to a dict of possible values, {square: digits}, or + return False if a contradiction is detected. + """ ## To start, every square can be any digit; then assign values from the grid. values = {s: digits for s in squares} for s, d in grid_values(grid).items(): @@ -58,15 +62,19 @@ def parse_grid(grid): def grid_values(grid): - "Convert grid into a dict of {square: char} with '0' or '.' for empties." + """ + Convert grid into a dict of {square: char} with '0' or '.' for empties. + """ chars = [c for c in grid if c in digits or c in "0."] assert len(chars) == 81 return dict(zip(squares, chars)) def assign(values, s, d): - """Eliminate all the other values (except d) from values[s] and propagate. - Return values, except return False if a contradiction is detected.""" + """ + Eliminate all the other values (except d) from values[s] and propagate. + Return values, except return False if a contradiction is detected. + """ other_values = values[s].replace(d, "") if all(eliminate(values, s, d2) for d2 in other_values): return values @@ -75,8 +83,10 @@ def assign(values, s, d): def eliminate(values, s, d): - """Eliminate d from values[s]; propagate when values or places <= 2. - Return values, except return False if a contradiction is detected.""" + """ + Eliminate d from values[s]; propagate when values or places <= 2. + Return values, except return False if a contradiction is detected. + """ if d not in values[s]: return values ## Already eliminated values[s] = values[s].replace(d, "") @@ -99,7 +109,9 @@ def eliminate(values, s, d): def display(values): - "Display these values as a 2-D grid." + """ + Display these values as a 2-D grid. + """ width = 1 + max(len(values[s]) for s in squares) line = "+".join(["-" * (width * 3)] * 3) for r in rows: @@ -114,11 +126,14 @@ def display(values): def solve(grid): + """ + Solve the grid. + """ return search(parse_grid(grid)) def some(seq): - "Return some element of seq that is true." + """Return some element of seq that is true.""" for e in seq: if e: return e @@ -126,7 +141,9 @@ def some(seq): def search(values): - "Using depth-first search and propagation, try all possible values." + """ + Using depth-first search and propagation, try all possible values. + """ if values is False: return False ## Failed earlier if all(len(values[s]) == 1 for s in squares): @@ -137,9 +154,11 @@ def search(values): def solve_all(grids, name="", showif=0.0): - """Attempt to solve a sequence of grids. Report results. + """ + Attempt to solve a sequence of grids. Report results. When showif is a number of seconds, display puzzles that take longer. - When showif is None, don't display any puzzles.""" + When showif is None, don't display any puzzles. + """ def time_solve(grid): start = time.monotonic() @@ -162,7 +181,9 @@ def time_solve(grid): def solved(values): - "A puzzle is solved if each unit is a permutation of the digits 1 to 9." + """ + A puzzle is solved if each unit is a permutation of the digits 1 to 9. + """ def unitsolved(unit): return {values[s] for s in unit} == set(digits) @@ -177,9 +198,11 @@ def from_file(filename, sep="\n"): def random_puzzle(assignments=17): - """Make a random puzzle with N or more assignments. Restart on contradictions. + """ + Make a random puzzle with N or more assignments. Restart on contradictions. Note the resulting puzzle is not guaranteed to be solvable, but empirically - about 99.8% of them are solvable. Some have multiple solutions.""" + about 99.8% of them are solvable. Some have multiple solutions. + """ values = {s: digits for s in squares} for s in shuffled(squares): if not assign(values, s, random.choice(values[s])): @@ -191,7 +214,9 @@ def random_puzzle(assignments=17): def shuffled(seq): - "Return a randomly shuffled copy of the input sequence." + """ + Return a randomly shuffled copy of the input sequence. + """ seq = list(seq) random.shuffle(seq) return seq From c666db3729b6d9f73e2f7756a3974f53279caa50 Mon Sep 17 00:00:00 2001 From: Vijayalaxmi Wakode Date: Fri, 24 Jan 2025 03:31:47 +0530 Subject: [PATCH 754/757] Add Doc test bubble sort (#12070) * The string manipulation - replace() * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update replace.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * updating DIRECTORY.md * Add doc test to bubble_sort * Update DIRECTORY.md * Delete strings/replace.py * Update bubble_sort.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: vijayalaxmi777 Co-authored-by: Maxim Smolskiy --- sorts/bubble_sort.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sorts/bubble_sort.py b/sorts/bubble_sort.py index bdf85c70dd35..9ec3d5384f38 100644 --- a/sorts/bubble_sort.py +++ b/sorts/bubble_sort.py @@ -85,6 +85,8 @@ def bubble_sort_recursive(collection: list[Any]) -> list[Any]: [1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7] >>> bubble_sort_recursive([1, 3.3, 5, 7.7, 2, 4.4, 6]) [1, 2, 3.3, 4.4, 5, 6, 7.7] + >>> bubble_sort_recursive(['a', 'Z', 'B', 'C', 'A', 'c']) + ['A', 'B', 'C', 'Z', 'a', 'c'] >>> import random >>> collection_arg = random.sample(range(-50, 50), 100) >>> bubble_sort_recursive(collection_arg) == sorted(collection_arg) From 13e4d3e76cfaa74d8b14314d319fb6c089aa051e Mon Sep 17 00:00:00 2001 From: Rachel Spears <103690982+Rosepetal2022@users.noreply.github.com> Date: Thu, 23 Jan 2025 21:59:36 -0800 Subject: [PATCH 755/757] Fix error in avl_tree del_node function (#11510) * fixed error in del_node function * Update avl_tree.py --------- Co-authored-by: Maxim Smolskiy --- data_structures/binary_tree/avl_tree.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/data_structures/binary_tree/avl_tree.py b/data_structures/binary_tree/avl_tree.py index 9fca7237404c..8558305eefe4 100644 --- a/data_structures/binary_tree/avl_tree.py +++ b/data_structures/binary_tree/avl_tree.py @@ -221,6 +221,10 @@ def del_node(root: MyNode, data: Any) -> MyNode | None: else: root.set_right(del_node(right_child, data)) + # Re-fetch left_child and right_child references + left_child = root.get_left() + right_child = root.get_right() + if get_height(right_child) - get_height(left_child) == 2: assert right_child is not None if get_height(right_child.get_right()) > get_height(right_child.get_left()): From a65b346ad42223fb644cfeb6a90291e6c859aa00 Mon Sep 17 00:00:00 2001 From: Isidro Arias Date: Sat, 25 Jan 2025 11:38:51 +0100 Subject: [PATCH 756/757] simplified and refactor diffie_hellman.py Everything is a refactor except the method generate_shared_key(). Its hashing final step has been removed for simplicity --- ciphers/diffie_hellman.py | 69 ++++++++------------------------------- 1 file changed, 14 insertions(+), 55 deletions(-) diff --git a/ciphers/diffie_hellman.py b/ciphers/diffie_hellman.py index aec7fb3eaf17..bbffbe0980d8 100644 --- a/ciphers/diffie_hellman.py +++ b/ciphers/diffie_hellman.py @@ -1,11 +1,9 @@ -from binascii import hexlify -from hashlib import sha256 -from os import urandom +import random # RFC 3526 - More Modular Exponential (MODP) Diffie-Hellman groups for # Internet Key Exchange (IKE) https://tools.ietf.org/html/rfc3526 -primes = { +PRIMES = { # 1536-bit 5: { "prime": int( @@ -187,44 +185,27 @@ class DiffieHellman: >>> alice = DiffieHellman() >>> bob = DiffieHellman() - >>> alice_private = alice.get_private_key() - >>> alice_public = alice.generate_public_key() + >>> alice_public = alice.public_key + >>> bob_public = bob.public_key - >>> bob_private = bob.get_private_key() - >>> bob_public = bob.generate_public_key() - - >>> # generating shared key using the DH object + Generating shared key using the DH object >>> alice_shared = alice.generate_shared_key(bob_public) >>> bob_shared = bob.generate_shared_key(alice_public) - - >>> assert alice_shared == bob_shared - - >>> # generating shared key using static methods - >>> alice_shared = DiffieHellman.generate_shared_key_static( - ... alice_private, bob_public - ... ) - >>> bob_shared = DiffieHellman.generate_shared_key_static( - ... bob_private, alice_public - ... ) - >>> assert alice_shared == bob_shared """ # Current minimum recommendation is 2048 bit (group 14) def __init__(self, group: int = 14) -> None: - if group not in primes: + if group not in PRIMES: raise ValueError("Unsupported Group") - self.prime = primes[group]["prime"] - self.generator = primes[group]["generator"] - - self.__private_key = int(hexlify(urandom(32)), base=16) + self.prime = PRIMES[group]["prime"] + self.generator = PRIMES[group]["generator"] - def get_private_key(self) -> str: - return hex(self.__private_key)[2:] + self.__private_key = random.getrandbits(256) - def generate_public_key(self) -> str: - public_key = pow(self.generator, self.__private_key, self.prime) - return hex(public_key)[2:] + @property + def public_key(self) -> int: + return pow(self.generator, self.__private_key, self.prime) def is_valid_public_key(self, key: int) -> bool: # check if the other public key is valid based on NIST SP800-56 @@ -233,32 +214,10 @@ def is_valid_public_key(self, key: int) -> bool: and pow(key, (self.prime - 1) // 2, self.prime) == 1 ) - def generate_shared_key(self, other_key_str: str) -> str: - other_key = int(other_key_str, base=16) + def generate_shared_key(self, other_key: int) -> int: if not self.is_valid_public_key(other_key): raise ValueError("Invalid public key") - shared_key = pow(other_key, self.__private_key, self.prime) - return sha256(str(shared_key).encode()).hexdigest() - - @staticmethod - def is_valid_public_key_static(remote_public_key_str: int, prime: int) -> bool: - # check if the other public key is valid based on NIST SP800-56 - return ( - 2 <= remote_public_key_str <= prime - 2 - and pow(remote_public_key_str, (prime - 1) // 2, prime) == 1 - ) - - @staticmethod - def generate_shared_key_static( - local_private_key_str: str, remote_public_key_str: str, group: int = 14 - ) -> str: - local_private_key = int(local_private_key_str, base=16) - remote_public_key = int(remote_public_key_str, base=16) - prime = primes[group]["prime"] - if not DiffieHellman.is_valid_public_key_static(remote_public_key, prime): - raise ValueError("Invalid public key") - shared_key = pow(remote_public_key, local_private_key, prime) - return sha256(str(shared_key).encode()).hexdigest() + return pow(other_key, self.__private_key, self.prime) if __name__ == "__main__": From bb987c43167a6d01783760222da962dcf8a13e23 Mon Sep 17 00:00:00 2001 From: Isidro Arias Date: Sat, 25 Jan 2025 12:05:36 +0100 Subject: [PATCH 757/757] Refactor doctest; change initializer arguments group: int -> prime: int, generator: int --- ciphers/diffie_hellman.py | 46 ++++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/ciphers/diffie_hellman.py b/ciphers/diffie_hellman.py index bbffbe0980d8..395d98ec6e60 100644 --- a/ciphers/diffie_hellman.py +++ b/ciphers/diffie_hellman.py @@ -1,9 +1,14 @@ -import random +""" +https://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange +""" -# RFC 3526 - More Modular Exponential (MODP) Diffie-Hellman groups for -# Internet Key Exchange (IKE) https://tools.ietf.org/html/rfc3526 +import random -PRIMES = { +""" +RFC 3526 - More Modular Exponential (MODP) Diffie-Hellman groups for +Internet Key Exchange (IKE) https://tools.ietf.org/html/rfc3526 +""" +GROUPS = { # 1536-bit 5: { "prime": int( @@ -179,28 +184,29 @@ class DiffieHellman: """ - Class to represent the Diffie-Hellman key exchange protocol + Class to represent one party in the Diffie-Hellman key exchange protocol + Current minimum recommendation is 2048 bit + >>> group = GROUPS[14] + >>> prime, generator = group['prime'], group['generator'] + >>> alice = DiffieHellman(prime, generator) - >>> alice = DiffieHellman() - >>> bob = DiffieHellman() + Both parties should agree on the same public parameters + >>> bob = DiffieHellman(alice.prime, alice.generator) - >>> alice_public = alice.public_key - >>> bob_public = bob.public_key + Alice sends Bob its public key, + >>> bob_shared = bob.generate_shared_key(alice.public_key) - Generating shared key using the DH object - >>> alice_shared = alice.generate_shared_key(bob_public) - >>> bob_shared = bob.generate_shared_key(alice_public) - >>> assert alice_shared == bob_shared - """ + and the same vice versa: + >>> alice_shared = alice.generate_shared_key(bob.public_key) - # Current minimum recommendation is 2048 bit (group 14) - def __init__(self, group: int = 14) -> None: - if group not in PRIMES: - raise ValueError("Unsupported Group") - self.prime = PRIMES[group]["prime"] - self.generator = PRIMES[group]["generator"] + >>> alice_shared == bob_shared + True + """ + def __init__(self, prime: int, generator: int) -> None: + self.prime = prime + self.generator = generator self.__private_key = random.getrandbits(256) @property