From dc1b2003b44b9fd831b5870d988e4f0af93b3389 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 26 Aug 2025 15:32:39 +0300 Subject: [PATCH 01/32] [pre-commit.ci] pre-commit autoupdate (#12930) 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.12.9 → v0.12.10](https://github.com/astral-sh/ruff-pre-commit/compare/v0.12.9...v0.12.10) - [github.com/pre-commit/mirrors-mypy: v1.15.0 → v1.17.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.15.0...v1.17.1) * Update word_break.py * Update word_break.py * Update word_break.py * Update word_break.py * Update word_break.py * Update covid_stats_via_xpath.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update pyproject.toml * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update pyproject.toml * Update pyproject.toml * Update covid_stats_via_xpath.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: Maxim Smolskiy --- .pre-commit-config.yaml | 4 ++-- dynamic_programming/word_break.py | 2 +- web_programming/covid_stats_via_xpath.py | 12 +++++++----- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 252cfebc53a8..6eb0906fb23a 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.12.9 + rev: v0.12.10 hooks: - id: ruff-check - id: ruff-format @@ -47,7 +47,7 @@ repos: - id: validate-pyproject - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.15.0 + rev: v1.17.1 hooks: - id: mypy args: diff --git a/dynamic_programming/word_break.py b/dynamic_programming/word_break.py index 4d7ac869080c..c4ba2d7aa976 100644 --- a/dynamic_programming/word_break.py +++ b/dynamic_programming/word_break.py @@ -90,7 +90,7 @@ def is_breakable(index: int) -> bool: if index == len_string: return True - trie_node = trie + trie_node: Any = trie for i in range(index, len_string): trie_node = trie_node.get(string[i], None) diff --git a/web_programming/covid_stats_via_xpath.py b/web_programming/covid_stats_via_xpath.py index f7db51b63169..9c016ba414ea 100644 --- a/web_programming/covid_stats_via_xpath.py +++ b/web_programming/covid_stats_via_xpath.py @@ -1,5 +1,5 @@ """ -This is to show simple COVID19 info fetching from worldometers site using lxml +This is to show simple COVID19 info fetching from worldometers archive site using lxml * The main motivation to use lxml in place of bs4 is that it is faster and therefore more convenient to use in Python web projects (e.g. Django or Flask-based) """ @@ -19,12 +19,14 @@ class CovidData(NamedTuple): - cases: int - deaths: int - recovered: int + cases: str + deaths: str + recovered: str -def covid_stats(url: str = "https://www.worldometers.info/coronavirus/") -> CovidData: +def covid_stats( + url: str = "https://web.archive.org/web/20250825095350/https://www.worldometers.info/coronavirus/", +) -> CovidData: xpath_str = '//div[@class = "maincounter-number"]/span/text()' return CovidData( *html.fromstring(httpx.get(url, timeout=10).content).xpath(xpath_str) From 55db5a1b8d463a514522dd759053d30357538b62 Mon Sep 17 00:00:00 2001 From: Mindaugas <76015221+mindaugl@users.noreply.github.com> Date: Tue, 26 Aug 2025 21:17:18 +0300 Subject: [PATCH 02/32] Add new solution for the euler project problem 9 (#12771) * Add new solution for the euler project problem 9 - precompute the squares. * Update sol4.py * updating DIRECTORY.md * Update sol4.py * Update sol4.py * Update sol4.py --------- Co-authored-by: Maxim Smolskiy Co-authored-by: MaximSmolskiy --- DIRECTORY.md | 1 + project_euler/problem_009/sol4.py | 60 +++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 project_euler/problem_009/sol4.py diff --git a/DIRECTORY.md b/DIRECTORY.md index 81d6f4c70864..2e3b4398f26e 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -956,6 +956,7 @@ * [Sol1](project_euler/problem_009/sol1.py) * [Sol2](project_euler/problem_009/sol2.py) * [Sol3](project_euler/problem_009/sol3.py) + * [Sol4](project_euler/problem_009/sol4.py) * Problem 010 * [Sol1](project_euler/problem_010/sol1.py) * [Sol2](project_euler/problem_010/sol2.py) diff --git a/project_euler/problem_009/sol4.py b/project_euler/problem_009/sol4.py new file mode 100644 index 000000000000..a07d40ccb54d --- /dev/null +++ b/project_euler/problem_009/sol4.py @@ -0,0 +1,60 @@ +""" +Project Euler Problem 9: https://projecteuler.net/problem=9 + +Special Pythagorean triplet + +A Pythagorean triplet is a set of three natural numbers, a < b < c, for which, + + a^2 + b^2 = c^2. + +For example, 3^2 + 4^2 = 9 + 16 = 25 = 5^2. + +There exists exactly one Pythagorean triplet for which a + b + c = 1000. +Find the product abc. + +References: + - https://en.wikipedia.org/wiki/Pythagorean_triple +""" + + +def get_squares(n: int) -> list[int]: + """ + >>> get_squares(0) + [] + >>> get_squares(1) + [0] + >>> get_squares(2) + [0, 1] + >>> get_squares(3) + [0, 1, 4] + >>> get_squares(4) + [0, 1, 4, 9] + """ + return [number * number for number in range(n)] + + +def solution(n: int = 1000) -> int: + """ + Precomputing squares and checking if a^2 + b^2 is the square by set look-up. + + >>> solution(12) + 60 + >>> solution(36) + 1620 + """ + + squares = get_squares(n) + squares_set = set(squares) + for a in range(1, n // 3): + for b in range(a + 1, (n - a) // 2 + 1): + if ( + squares[a] + squares[b] in squares_set + and squares[n - a - b] == squares[a] + squares[b] + ): + return a * b * (n - a - b) + + return -1 + + +if __name__ == "__main__": + print(f"{solution() = }") From 44cf167175cf2a53080006dbedd40b5f5343ef18 Mon Sep 17 00:00:00 2001 From: Sharan Sukesh <77352136+sharansukesh1003@users.noreply.github.com> Date: Tue, 26 Aug 2025 14:42:14 -0400 Subject: [PATCH 03/32] Create cyclic_sort.py (#9256) * Create cyclic_sort.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update cyclic_sort.py * updating DIRECTORY.md * Update cyclic_sort.py * Update cyclic_sort.py * Update cyclic_sort.py * Update cyclic_sort.py * Update cyclic_sort.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Maxim Smolskiy Co-authored-by: MaximSmolskiy --- DIRECTORY.md | 1 + sorts/cyclic_sort.py | 55 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 sorts/cyclic_sort.py diff --git a/DIRECTORY.md b/DIRECTORY.md index 2e3b4398f26e..53c53d208656 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -1267,6 +1267,7 @@ * [Comb Sort](sorts/comb_sort.py) * [Counting Sort](sorts/counting_sort.py) * [Cycle Sort](sorts/cycle_sort.py) + * [Cyclic Sort](sorts/cyclic_sort.py) * [Double Sort](sorts/double_sort.py) * [Dutch National Flag Sort](sorts/dutch_national_flag_sort.py) * [Exchange Sort](sorts/exchange_sort.py) diff --git a/sorts/cyclic_sort.py b/sorts/cyclic_sort.py new file mode 100644 index 000000000000..9e81291548d4 --- /dev/null +++ b/sorts/cyclic_sort.py @@ -0,0 +1,55 @@ +""" +This is a pure Python implementation of the Cyclic Sort algorithm. + +For doctests run following command: +python -m doctest -v cyclic_sort.py +or +python3 -m doctest -v cyclic_sort.py +For manual testing run: +python cyclic_sort.py +or +python3 cyclic_sort.py +""" + + +def cyclic_sort(nums: list[int]) -> list[int]: + """ + Sorts the input list of n integers from 1 to n in-place + using the Cyclic Sort algorithm. + + :param nums: List of n integers from 1 to n to be sorted. + :return: The same list sorted in ascending order. + + Time complexity: O(n), where n is the number of integers in the list. + + Examples: + >>> cyclic_sort([]) + [] + >>> cyclic_sort([3, 5, 2, 1, 4]) + [1, 2, 3, 4, 5] + """ + + # Perform cyclic sort + index = 0 + while index < len(nums): + # Calculate the correct index for the current element + correct_index = nums[index] - 1 + # If the current element is not at its correct position, + # swap it with the element at its correct index + if index != correct_index: + nums[index], nums[correct_index] = nums[correct_index], nums[index] + else: + # If the current element is already in its correct position, + # move to the next element + index += 1 + + return nums + + +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(*cyclic_sort(unsorted), sep=",") From 4961b3aa899f79b02539ec031d8b6e5adafc02d1 Mon Sep 17 00:00:00 2001 From: Wei Jiang <42140605+Jiang15@users.noreply.github.com> Date: Wed, 27 Aug 2025 00:10:22 +0200 Subject: [PATCH 04/32] Enhancement of the knapsack algorithm with memorization and generalisation (#9295) * enhance knapsack problem * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * wei/refactor code * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update test_knapsack.py * Update knapsack.py * Update test_knapsack.py * Update knapsack.py * Update knapsack.py * Update knapsack.py * Update knapsack.py * Update knapsack.py * Update test_knapsack.py --------- Co-authored-by: weijiang Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Maxim Smolskiy --- knapsack/README.md | 2 +- knapsack/knapsack.py | 65 ++++++++++++++++++++++----------- knapsack/tests/test_knapsack.py | 12 +++++- 3 files changed, 55 insertions(+), 24 deletions(-) diff --git a/knapsack/README.md b/knapsack/README.md index f31e5f591412..686ea929255a 100644 --- a/knapsack/README.md +++ b/knapsack/README.md @@ -1,4 +1,4 @@ -# A naive recursive implementation of 0-1 Knapsack Problem +# A recursive implementation of 0-N Knapsack Problem This overview is taken from: diff --git a/knapsack/knapsack.py b/knapsack/knapsack.py index bb507be1ba3c..0648773c919f 100644 --- a/knapsack/knapsack.py +++ b/knapsack/knapsack.py @@ -1,14 +1,23 @@ -"""A naive recursive implementation of 0-1 Knapsack Problem +"""A recursive implementation of 0-N Knapsack Problem https://en.wikipedia.org/wiki/Knapsack_problem """ from __future__ import annotations +from functools import lru_cache -def knapsack(capacity: int, weights: list[int], values: list[int], counter: int) -> int: + +def knapsack( + capacity: int, + weights: list[int], + values: list[int], + counter: int, + allow_repetition=False, +) -> int: """ Returns the maximum value that can be put in a knapsack of a capacity cap, - whereby each weight w has a specific value val. + whereby each weight w has a specific value val + with option to allow repetitive selection of items >>> cap = 50 >>> val = [60, 100, 120] @@ -17,28 +26,40 @@ def knapsack(capacity: int, weights: list[int], values: list[int], counter: int) >>> knapsack(cap, w, val, c) 220 - The result is 220 cause the values of 100 and 120 got the weight of 50 + Given the repetition is NOT allowed, + the result is 220 cause the values of 100 and 120 got the weight of 50 which is the limit of the capacity. + >>> knapsack(cap, w, val, c, True) + 300 + + Given the repetition is allowed, + the result is 300 cause the values of 60*5 (pick 5 times) + got the weight of 10*5 which is the limit of the capacity. """ - # Base Case - if counter == 0 or capacity == 0: - return 0 - - # If weight of the nth item is more than Knapsack of capacity, - # then this item cannot be included in the optimal solution, - # else return the maximum of two cases: - # (1) nth item included - # (2) not included - if weights[counter - 1] > capacity: - return knapsack(capacity, weights, values, counter - 1) - else: - left_capacity = capacity - weights[counter - 1] - new_value_included = values[counter - 1] + knapsack( - left_capacity, weights, values, counter - 1 - ) - without_new_value = knapsack(capacity, weights, values, counter - 1) - return max(new_value_included, without_new_value) + @lru_cache + def knapsack_recur(capacity: int, counter: int) -> int: + # Base Case + if counter == 0 or capacity == 0: + return 0 + + # If weight of the nth item is more than Knapsack of capacity, + # then this item cannot be included in the optimal solution, + # else return the maximum of two cases: + # (1) nth item included only once (0-1), if allow_repetition is False + # nth item included one or more times (0-N), if allow_repetition is True + # (2) not included + if weights[counter - 1] > capacity: + return knapsack_recur(capacity, counter - 1) + else: + left_capacity = capacity - weights[counter - 1] + new_value_included = values[counter - 1] + knapsack_recur( + left_capacity, counter - 1 if not allow_repetition else counter + ) + without_new_value = knapsack_recur(capacity, counter - 1) + return max(new_value_included, without_new_value) + + return knapsack_recur(capacity, counter) if __name__ == "__main__": diff --git a/knapsack/tests/test_knapsack.py b/knapsack/tests/test_knapsack.py index 7bfb8780627b..80378aae4579 100644 --- a/knapsack/tests/test_knapsack.py +++ b/knapsack/tests/test_knapsack.py @@ -30,7 +30,7 @@ def test_base_case(self): def test_easy_case(self): """ - test for the base case + test for the easy case """ cap = 3 val = [1, 2, 3] @@ -48,6 +48,16 @@ def test_knapsack(self): c = len(val) assert k.knapsack(cap, w, val, c) == 220 + def test_knapsack_repetition(self): + """ + test for the knapsack repetition + """ + cap = 50 + val = [60, 100, 120] + w = [10, 20, 30] + c = len(val) + assert k.knapsack(cap, w, val, c, True) == 300 + if __name__ == "__main__": unittest.main() From b486ac60e6e047f94615f0489ae8aaa48d33ff41 Mon Sep 17 00:00:00 2001 From: Om Ashishkumar Soni Date: Thu, 28 Aug 2025 00:32:14 +0530 Subject: [PATCH 05/32] added coordinate_compression (#9317) * added coordinate_compression algorithm * added coordinate_compression & doctest * adding return type hints and utilized enumerate * adding exmaple usage in main function * added type hints, for list and dict * updating DIRECTORY.md * Update other/coordinate_compression.py Co-authored-by: Christian Clauss * Update other/coordinate_compression.py Co-authored-by: Christian Clauss * Update other/coordinate_compression.py Co-authored-by: Christian Clauss * Update other/coordinate_compression.py Co-authored-by: Christian Clauss * Update other/coordinate_compression.py Co-authored-by: Christian Clauss * Update other/coordinate_compression.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/coordinate_compression.py Co-authored-by: Christian Clauss * Update other/coordinate_compression.py Co-authored-by: Christian Clauss * Update coordinate_compression.py * Create coordinate_compression.py * updating DIRECTORY.md * Delete other/coordinate_compression.py * updating DIRECTORY.md * Update coordinate_compression.py * Update coordinate_compression.py * Update coordinate_compression.py * Update coordinate_compression.py * Update coordinate_compression.py * Update coordinate_compression.py * Update coordinate_compression.py * Update coordinate_compression.py * Update coordinate_compression.py --------- Co-authored-by: Maxim Smolskiy Co-authored-by: MaximSmolskiy Co-authored-by: Christian Clauss Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- DIRECTORY.md | 1 + data_compression/coordinate_compression.py | 132 +++++++++++++++++++++ 2 files changed, 133 insertions(+) create mode 100644 data_compression/coordinate_compression.py diff --git a/DIRECTORY.md b/DIRECTORY.md index 53c53d208656..0636ba0a7ecc 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -174,6 +174,7 @@ ## Data Compression * [Burrows Wheeler](data_compression/burrows_wheeler.py) + * [Coordinate Compression](data_compression/coordinate_compression.py) * [Huffman](data_compression/huffman.py) * [Lempel Ziv](data_compression/lempel_ziv.py) * [Lempel Ziv Decompress](data_compression/lempel_ziv_decompress.py) diff --git a/data_compression/coordinate_compression.py b/data_compression/coordinate_compression.py new file mode 100644 index 000000000000..9c4ad9a99ac3 --- /dev/null +++ b/data_compression/coordinate_compression.py @@ -0,0 +1,132 @@ +""" +Assumption: + - The values to compress are assumed to be comparable, + values can be sorted and compared with '<' and '>' operators. +""" + + +class CoordinateCompressor: + """ + A class for coordinate compression. + + This class allows you to compress and decompress a list of values. + + Mapping: + In addition to compression and decompression, this class maintains a mapping + between original values and their compressed counterparts using two data + structures: a dictionary `coordinate_map` and a list `reverse_map`: + - `coordinate_map`: A dictionary that maps original values to their compressed + coordinates. Keys are original values, and values are compressed coordinates. + - `reverse_map`: A list used for reverse mapping, where each index corresponds + to a compressed coordinate, and the value at that index is the original value. + + Example of mapping: + Original: 10, Compressed: 0 + Original: 52, Compressed: 1 + Original: 83, Compressed: 2 + Original: 100, Compressed: 3 + + This mapping allows for efficient compression and decompression of values within + the list. + """ + + def __init__(self, arr: list[int | float | str]) -> None: + """ + Initialize the CoordinateCompressor with a list. + + Args: + arr: The list of values to be compressed. + + >>> arr = [100, 10, 52, 83] + >>> cc = CoordinateCompressor(arr) + >>> cc.compress(100) + 3 + >>> cc.compress(52) + 1 + >>> cc.decompress(1) + 52 + """ + + # A dictionary to store compressed coordinates + self.coordinate_map: dict[int | float | str, int] = {} + + # A list to store reverse mapping + self.reverse_map: list[int | float | str] = [-1] * len(arr) + + self.arr = sorted(arr) # The input list + self.n = len(arr) # The length of the input list + self.compress_coordinates() + + def compress_coordinates(self) -> None: + """ + Compress the coordinates in the input list. + + >>> arr = [100, 10, 52, 83] + >>> cc = CoordinateCompressor(arr) + >>> cc.coordinate_map[83] + 2 + >>> cc.coordinate_map[80] # Value not in the original list + Traceback (most recent call last): + ... + KeyError: 80 + >>> cc.reverse_map[2] + 83 + """ + key = 0 + for val in self.arr: + if val not in self.coordinate_map: + self.coordinate_map[val] = key + self.reverse_map[key] = val + key += 1 + + def compress(self, original: float | str) -> int: + """ + Compress a single value. + + Args: + original: The value to compress. + + Returns: + The compressed integer, or -1 if not found in the original list. + + >>> arr = [100, 10, 52, 83] + >>> cc = CoordinateCompressor(arr) + >>> cc.compress(100) + 3 + >>> cc.compress(7) # Value not in the original list + -1 + """ + return self.coordinate_map.get(original, -1) + + def decompress(self, num: int) -> int | float | str: + """ + Decompress a single integer. + + Args: + num: The compressed integer to decompress. + + Returns: + The original value. + + >>> arr = [100, 10, 52, 83] + >>> cc = CoordinateCompressor(arr) + >>> cc.decompress(0) + 10 + >>> cc.decompress(5) # Compressed coordinate out of range + -1 + """ + return self.reverse_map[num] if 0 <= num < len(self.reverse_map) else -1 + + +if __name__ == "__main__": + from doctest import testmod + + testmod() + + arr: list[int | float | str] = [100, 10, 52, 83] + cc = CoordinateCompressor(arr) + + for original in arr: + compressed = cc.compress(original) + decompressed = cc.decompress(compressed) + print(f"Original: {decompressed}, Compressed: {compressed}") From 2fa65c7d9212514a5c8241c7636166f5758c5ff0 Mon Sep 17 00:00:00 2001 From: Shreyash Kashyap <76607993+SYK-08@users.noreply.github.com> Date: Thu, 28 Aug 2025 02:11:04 +0530 Subject: [PATCH 06/32] Improve blockchain/README.md (#9630) * Update README.md * Update blockchain/README.md Co-authored-by: Tianyi Zheng * Update blockchain/README.md Co-authored-by: Tianyi Zheng * Update blockchain/README.md Co-authored-by: Tianyi Zheng * Update blockchain/README.md Co-authored-by: Tianyi Zheng * Update blockchain/README.md Co-authored-by: Tianyi Zheng * Update blockchain/README.md Co-authored-by: Tianyi Zheng * Update README.md --------- Co-authored-by: Maxim Smolskiy Co-authored-by: Tianyi Zheng --- blockchain/README.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/blockchain/README.md b/blockchain/README.md index b5fab7b36eaa..ecd784fc2c7d 100644 --- a/blockchain/README.md +++ b/blockchain/README.md @@ -1,8 +1,8 @@ # Blockchain -A Blockchain is a type of **distributed ledger** technology (DLT) that consists of growing list of records, called **blocks**, that are securely linked together using **cryptography**. +A Blockchain is a type of **distributed ledger** technology (DLT) that consists of a growing list of records, called **blocks**, that are securely linked together using **cryptography**. -Let's breakdown the terminologies in the above definition. We find below terminologies, +Let's break down the terminologies in the above definition. We find below terminologies, - Digital Ledger Technology (DLT) - Blocks @@ -10,35 +10,35 @@ Let's breakdown the terminologies in the above definition. We find below termino ## Digital Ledger Technology - It is otherwise called as distributed ledger technology. It is simply the opposite of centralized database. Firstly, what is a **ledger**? A ledger is a book or collection of accounts that records account transactions. +Blockchain is also called distributed ledger technology. It is simply the opposite of a centralized database. Firstly, what is a **ledger**? A ledger is a book or collection of accounts that records account transactions. - *Why is Blockchain addressed as digital ledger if it can record more than account transactions? What other transaction details and information can it hold?* +*Why is Blockchain addressed as a digital ledger if it can record more than account transactions? What other transaction details and information can it hold?* -Digital Ledger Technology is just a ledger which is shared among multiple nodes. This way there exist no need for central authority to hold the info. Okay, how is it differentiated from central database and what are their benefits? +Digital Ledger Technology is just a ledger that is shared among multiple nodes. This way there exists no need for a central authority to hold the info. Okay, how is it differentiated from a central database and what are their benefits? -There is an organization which has 4 branches whose data are stored in a centralized database. So even if one branch needs any data from ledger they need an approval from database in charge. And if one hacks the central database he gets to tamper and control all the data. +Suppose that there is an organization that has 4 branches whose data are stored in a centralized database. So even if one branch needs any data from the ledger it needs approval from the database in charge. And if one hacks the central database he gets to tamper and control all the data. -Now lets assume every branch has a copy of the ledger and then once anything is added to the ledger by anyone branch it is gonna automatically reflect in all other ledgers available in other branch. This is done using Peer-to-peer network. +Now let's assume every branch has a copy of the ledger and then once anything is added to the ledger by any branch it is gonna automatically reflect in all other ledgers available in other branches. This is done using a peer-to-peer network. -So this means even if information is tampered in one branch we can find out. If one branch is hacked we can be alerted ,so we can safeguard other branches. Now, assume these branches as computers or nodes and the ledger is a transaction record or digital receipt. If one ledger is hacked in a node we can detect since there will be a mismatch in comparison with other node information. So this is the concept of Digital Ledger Technology. +This means that even if information is tampered with in one branch we can find out. If one branch is hacked we can be alerted, so we can safeguard other branches. Now, assume these branches as computers or nodes and the ledger is a transaction record or digital receipt. If one ledger is hacked in a node we can detect since there will be a mismatch in comparison with other node information. So this is the concept of Digital Ledger Technology. *Is it required for all nodes to have access to all information in other nodes? Wouldn't this require enormous storage space in each node?* ## Blocks -In short a block is nothing but collections of records with a labelled header. These are connected cryptographically. Once a new block is added to a chain, the previous block is connected, more precisely said as locked and hence, will remain unaltered. We can understand this concept once we get a clear understanding of working mechanism of blockchain. +In short, a block is nothing but a collection of records with a labelled header. These are connected cryptographically. Once a new block is added to a chain, the previous block is connected, more precisely said as locked, and hence will remain unaltered. We can understand this concept once we get a clear understanding of the working mechanism of blockchain. ## Cryptography -It is the practice and study of secure communication techniques in the midst of adversarial behavior. More broadly, cryptography is the creation and analysis of protocols that prevent third parties or the general public from accessing private messages. +Cryptography is the practice and study of secure communication techniques amid adversarial behavior. More broadly, cryptography is the creation and analysis of protocols that prevent third parties or the general public from accessing private messages. *Which cryptography technology is most widely used in blockchain and why?* -So, in general, blockchain technology is a distributed record holder which records the information about ownership of an asset. To define precisely, +So, in general, blockchain technology is a distributed record holder that records the information about ownership of an asset. To define precisely, > Blockchain is a distributed, immutable ledger that makes it easier to record transactions and track assets in a corporate network. An asset could be tangible (such as a house, car, cash, or land) or intangible (such as a business) (intellectual property, patents, copyrights, branding). A blockchain network can track and sell almost anything of value, lowering risk and costs for everyone involved. -So this is all about introduction to blockchain technology. To learn more about the topic refer below links.... +So this is all about the introduction to blockchain technology. To learn more about the topic refer below links.... * * * From 0c39e43af70274944e62e84708585cfd7711d2d3 Mon Sep 17 00:00:00 2001 From: Mary-0165 <146911989+Mary-0165@users.noreply.github.com> Date: Thu, 28 Aug 2025 02:25:27 +0530 Subject: [PATCH 07/32] Algorithm to find unique prime factors (#9935) * algorithm to find unique prime factors * Update prime_factors.py * Update prime_factors.py * Update prime_factors.py --------- Co-authored-by: Maxim Smolskiy --- maths/prime_factors.py | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/maths/prime_factors.py b/maths/prime_factors.py index 47abcf10e618..6eff57d12d17 100644 --- a/maths/prime_factors.py +++ b/maths/prime_factors.py @@ -47,6 +47,46 @@ def prime_factors(n: int) -> list[int]: return factors +def unique_prime_factors(n: int) -> list[int]: + """ + Returns unique prime factors of n as a list. + + >>> unique_prime_factors(0) + [] + >>> unique_prime_factors(100) + [2, 5] + >>> unique_prime_factors(2560) + [2, 5] + >>> unique_prime_factors(10**-2) + [] + >>> unique_prime_factors(0.02) + [] + >>> unique_prime_factors(10**241) + [2, 5] + >>> unique_prime_factors(10**-354) + [] + >>> unique_prime_factors('hello') + Traceback (most recent call last): + ... + TypeError: '<=' not supported between instances of 'int' and 'str' + >>> unique_prime_factors([1,2,'hello']) + Traceback (most recent call last): + ... + TypeError: '<=' not supported between instances of 'int' and 'list' + """ + i = 2 + factors = [] + while i * i <= n: + if not n % i: + while not n % i: + n //= i + factors.append(i) + i += 1 + if n > 1: + factors.append(n) + return factors + + if __name__ == "__main__": import doctest From e6b5d26d5c9cd23942a52c0210cee244f0aba85e Mon Sep 17 00:00:00 2001 From: architmaheshwari99 <99663695+architmaheshwari99@users.noreply.github.com> Date: Thu, 28 Aug 2025 02:37:46 +0530 Subject: [PATCH 08/32] Combination sum fix and test cases (#10193) * fixed_incorrect_test_combination_sum * reverting fn arg * ruff * Update combination_sum.py * Update combination_sum.py --------- Co-authored-by: Maxim Smolskiy --- backtracking/combination_sum.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/backtracking/combination_sum.py b/backtracking/combination_sum.py index 3c6ed81f44f0..3d954f11d2c5 100644 --- a/backtracking/combination_sum.py +++ b/backtracking/combination_sum.py @@ -47,8 +47,18 @@ 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 + ValueError: All elements in candidates must be non-negative + >>> combination_sum([], 1) + Traceback (most recent call last): + ... + ValueError: Candidates list should not be empty """ + if not candidates: + raise ValueError("Candidates list should not be empty") + + if any(x < 0 for x in candidates): + raise ValueError("All elements in candidates must be non-negative") + path = [] # type: list[int] answer = [] # type: list[int] backtrack(candidates, path, answer, target, 0) From 84f101ca6eba4255b7780120ef772f64be437a67 Mon Sep 17 00:00:00 2001 From: Prathamesh Gadekar <93116210+Pr0-C0der@users.noreply.github.com> Date: Thu, 28 Aug 2025 03:52:12 +0530 Subject: [PATCH 09/32] Add/generate parentheses iterative approach (#10024) * Generate parantheses iterative * Generate parantheses iterative * Generating parantheses code using iterative approach * Update generate_parentheses_iterative.py * updating DIRECTORY.md * Update generate_parentheses_iterative.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update generate_parentheses_iterative.py * Update generate_parentheses_iterative.py * Update generate_parentheses_iterative.py --------- Co-authored-by: nightmare10123 Co-authored-by: Maxim Smolskiy Co-authored-by: MaximSmolskiy Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- DIRECTORY.md | 1 + .../generate_parentheses_iterative.py | 62 +++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 backtracking/generate_parentheses_iterative.py diff --git a/DIRECTORY.md b/DIRECTORY.md index 0636ba0a7ecc..41a2d2e9af03 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -12,6 +12,7 @@ * [Combination Sum](backtracking/combination_sum.py) * [Crossword Puzzle Solver](backtracking/crossword_puzzle_solver.py) * [Generate Parentheses](backtracking/generate_parentheses.py) + * [Generate Parentheses Iterative](backtracking/generate_parentheses_iterative.py) * [Hamiltonian Cycle](backtracking/hamiltonian_cycle.py) * [Knight Tour](backtracking/knight_tour.py) * [Match Word Pattern](backtracking/match_word_pattern.py) diff --git a/backtracking/generate_parentheses_iterative.py b/backtracking/generate_parentheses_iterative.py new file mode 100644 index 000000000000..175941c7ae95 --- /dev/null +++ b/backtracking/generate_parentheses_iterative.py @@ -0,0 +1,62 @@ +def generate_parentheses_iterative(length: int) -> list: + """ + Generate all valid combinations of parentheses (Iterative Approach). + + The algorithm works as follows: + 1. Initialize an empty list to store the combinations. + 2. Initialize a stack to keep track of partial combinations. + 3. Start with empty string and push it onstack along with the counts of '(' and ')'. + 4. While the stack is not empty: + a. Pop a partial combination and its open and close counts from the stack. + b. If the combination length is equal to 2*length, add it to the result. + c. If open count < length, push new combination with added '(' on stack. + d. If close count < open count, push new combination with added ')' on stack. + 5. Return the result containing all valid combinations. + + Args: + length: The desired length of the parentheses combinations + + Returns: + A list of strings representing valid combinations of parentheses + + Time Complexity: + O(2^(2*length)) + + Space Complexity: + O(2^(2*length)) + + >>> generate_parentheses_iterative(3) + ['()()()', '()(())', '(())()', '(()())', '((()))'] + >>> generate_parentheses_iterative(2) + ['()()', '(())'] + >>> generate_parentheses_iterative(1) + ['()'] + >>> generate_parentheses_iterative(0) + [''] + """ + result = [] + stack = [] + + # Each element in stack is a tuple (current_combination, open_count, close_count) + stack.append(("", 0, 0)) + + while stack: + current_combination, open_count, close_count = stack.pop() + + if len(current_combination) == 2 * length: + result.append(current_combination) + + if open_count < length: + stack.append((current_combination + "(", open_count + 1, close_count)) + + if close_count < open_count: + stack.append((current_combination + ")", open_count, close_count + 1)) + + return result + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + print(generate_parentheses_iterative(3)) From 54aa73fdf091dfaf25f1971db443b33169d799f4 Mon Sep 17 00:00:00 2001 From: Praful Katare <47990928+Kpraful@users.noreply.github.com> Date: Thu, 28 Aug 2025 04:15:05 +0530 Subject: [PATCH 10/32] Fixes bugs in graphs/dijkstra_algorithm.py (#10092) * Fixes bug in PriorityQueue Algorithm; Fixes syntax in code for array. * Update dijkstra_algorithm.py * Update dijkstra_algorithm.py * Update dijkstra_algorithm.py * Update dijkstra_algorithm.py --------- Co-authored-by: Maxim Smolskiy --- graphs/dijkstra_algorithm.py | 26 +++++++------------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/graphs/dijkstra_algorithm.py b/graphs/dijkstra_algorithm.py index 51412b790bac..60646862fca8 100644 --- a/graphs/dijkstra_algorithm.py +++ b/graphs/dijkstra_algorithm.py @@ -52,45 +52,33 @@ def min_heapify(self, idx): >>> 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')] + [(5, 'B'), (10, 'A'), (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')] + [(5, 'C'), (15, 'B'), (10, 'A')] >>> 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')] + [(5, 'B'), (10, 'A')] """ 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) @@ -130,12 +118,12 @@ def extract_min(self): >>> priority_queue_test.extract_min() 'C' >>> priority_queue_test.array[0] - (15, 'B') + (10, 'A') """ min_node = self.array[0][1] self.array[0] = self.array[self.cur_size - 1] self.cur_size -= 1 - self.min_heapify(1) + self.min_heapify(0) del self.pos[min_node] return min_node From 4394fd93d384cea5cc46ef61d31995ee28872cc9 Mon Sep 17 00:00:00 2001 From: __Aditya <149356523+aditya7balotra@users.noreply.github.com> Date: Fri, 29 Aug 2025 22:03:43 +0530 Subject: [PATCH 11/32] Weierstrass Method (#12877) * Add weierstrass_method for approximating complex roots - Implements Durand-Kerner (Weierstrass) method for polynomial root finding - Accepts user-defined polynomial function and degree - Uses random perturbation of complex roots of unity for initial guesses - Handles validation, overflow clipping, and includes doctest * Update weierstrass_method.py * add more tests * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update weierstrass_method.py * Update weierstrass_method.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .../numerical_analysis/weierstrass_method.py | 97 +++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 maths/numerical_analysis/weierstrass_method.py diff --git a/maths/numerical_analysis/weierstrass_method.py b/maths/numerical_analysis/weierstrass_method.py new file mode 100644 index 000000000000..b5a767af3a86 --- /dev/null +++ b/maths/numerical_analysis/weierstrass_method.py @@ -0,0 +1,97 @@ +from collections.abc import Callable + +import numpy as np + + +def weierstrass_method( + polynomial: Callable[[np.ndarray], np.ndarray], + degree: int, + roots: np.ndarray | None = None, + max_iter: int = 100, +) -> np.ndarray: + """ + Approximates all complex roots of a polynomial using the + Weierstrass (Durand-Kerner) method. + Args: + polynomial: A function that takes a NumPy array of complex numbers and returns + the polynomial values at those points. + degree: Degree of the polynomial (number of roots to find). Must be ≥ 1. + roots: Optional initial guess as a NumPy array of complex numbers. + Must have length equal to 'degree'. + If None, perturbed complex roots of unity are used. + max_iter: Number of iterations to perform (default: 100). + + Returns: + np.ndarray: Array of approximated complex roots. + + Raises: + ValueError: If degree < 1, or if initial roots length doesn't match the degree. + + Note: + - Root updates are clipped to prevent numerical overflow. + + Example: + >>> import numpy as np + >>> def check(poly, degree, expected): + ... roots = weierstrass_method(poly, degree) + ... return np.allclose(np.sort(roots), np.sort(expected)) + + >>> check( + ... lambda x: x**2 - 1, + ... 2, + ... np.array([-1, 1])) + True + + >>> check( + ... lambda x: x**3 - 4.5*x**2 + 5.75*x - 1.875, + ... 3, + ... np.array([1.5, 0.5, 2.5]) + ... ) + True + + See Also: + https://en.wikipedia.org/wiki/Durand%E2%80%93Kerner_method + """ + + if degree < 1: + raise ValueError("Degree of the polynomial must be at least 1.") + + if roots is None: + # Use perturbed complex roots of unity as initial guesses + rng = np.random.default_rng() + roots = np.array( + [ + np.exp(2j * np.pi * i / degree) * (1 + 1e-3 * rng.random()) + for i in range(degree) + ], + dtype=np.complex128, + ) + + else: + roots = np.asarray(roots, dtype=np.complex128) + if roots.shape[0] != degree: + raise ValueError( + "Length of initial roots must match the degree of the polynomial." + ) + + for _ in range(max_iter): + # Construct the product denominator for each root + denominator = np.array([root - roots for root in roots], dtype=np.complex128) + np.fill_diagonal(denominator, 1.0) # Avoid zero in diagonal + denominator = np.prod(denominator, axis=1) + + # Evaluate polynomial at each root + numerator = polynomial(roots).astype(np.complex128) + + # Compute update and clip to prevent overflow + delta = numerator / denominator + delta = np.clip(delta, -1e10, 1e10) + roots -= delta + + return roots + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 5c5057247683d5851f4d34982ea69de0d648b665 Mon Sep 17 00:00:00 2001 From: Aasheesh <126905285+AasheeshLikePanner@users.noreply.github.com> Date: Sat, 30 Aug 2025 04:47:31 +0530 Subject: [PATCH 12/32] Fixing stock_span_problem.py (#10540) * 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 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 * Update subset_generation.py * Update data_structures/stacks/stock_span_problem.py Co-authored-by: Christian Clauss * Update stock_span_problem.py * Update data_structures/stacks/stock_span_problem.py Co-authored-by: Christian Clauss * Update stock_span_problem.py * Update stock_span_problem.py * updating DIRECTORY.md * Update stock_span_problem.py * Update stock_span_problem.py * Update stock_span_problem.py * Update stock_span_problem.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: Maxim Smolskiy Co-authored-by: MaximSmolskiy --- DIRECTORY.md | 1 + data_structures/stacks/stock_span_problem.py | 34 ++++++++++++++++---- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index 41a2d2e9af03..2df9d56e99b7 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -725,6 +725,7 @@ * [Secant Method](maths/numerical_analysis/secant_method.py) * [Simpson Rule](maths/numerical_analysis/simpson_rule.py) * [Square Root](maths/numerical_analysis/square_root.py) + * [Weierstrass Method](maths/numerical_analysis/weierstrass_method.py) * [Odd Sieve](maths/odd_sieve.py) * [Perfect Cube](maths/perfect_cube.py) * [Perfect Number](maths/perfect_number.py) diff --git a/data_structures/stacks/stock_span_problem.py b/data_structures/stacks/stock_span_problem.py index 5efe58d25798..74c2636784e2 100644 --- a/data_structures/stacks/stock_span_problem.py +++ b/data_structures/stacks/stock_span_problem.py @@ -8,8 +8,29 @@ """ -def calculation_span(price, s): +def calculate_span(price: list[int]) -> list[int]: + """ + Calculate the span values for a given list of stock prices. + Args: + price: List of stock prices. + Returns: + List of span values. + + >>> calculate_span([10, 4, 5, 90, 120, 80]) + [1, 1, 2, 4, 5, 1] + >>> calculate_span([100, 50, 60, 70, 80, 90]) + [1, 1, 2, 3, 4, 5] + >>> calculate_span([5, 4, 3, 2, 1]) + [1, 1, 1, 1, 1] + >>> calculate_span([1, 2, 3, 4, 5]) + [1, 2, 3, 4, 5] + >>> calculate_span([10, 20, 30, 40, 50]) + [1, 2, 3, 4, 5] + >>> calculate_span([100, 80, 60, 70, 60, 75, 85]) + [1, 1, 1, 2, 1, 4, 6] + """ n = len(price) + s = [0] * n # Create a stack and push index of fist element to it st = [] st.append(0) @@ -21,18 +42,20 @@ def calculation_span(price, s): for i in range(1, n): # Pop elements from stack while stack is not # empty and top of stack is smaller than price[i] - while len(st) > 0 and price[st[0]] <= price[i]: + while len(st) > 0 and price[st[-1]] <= price[i]: st.pop() # If stack becomes empty, then price[i] is greater # than all elements on left of it, i.e. price[0], # price[1], ..price[i-1]. Else the price[i] is # greater than elements after top of stack - s[i] = i + 1 if len(st) <= 0 else (i - st[0]) + s[i] = i + 1 if len(st) <= 0 else (i - st[-1]) # Push this element to stack st.append(i) + return s + # A utility function to print elements of array def print_array(arr, n): @@ -42,10 +65,9 @@ def print_array(arr, n): # Driver program to test above function price = [10, 4, 5, 90, 120, 80] -S = [0 for i in range(len(price) + 1)] -# Fill the span values in array S[] -calculation_span(price, S) +# Calculate the span values +S = calculate_span(price) # Print the calculated span values print_array(S, len(price)) From 488f143b8c8b9d1528c2decdc9135d2c749a7a69 Mon Sep 17 00:00:00 2001 From: Venkata Ramana Menda <115451367+RamanaMenda@users.noreply.github.com> Date: Sat, 30 Aug 2025 05:46:21 +0530 Subject: [PATCH 13/32] Performance: 25% faster Project Euler 73 #10503 (#11553) * Seperate slow_solution and solution * Add performance benchmark * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix issues * Update sol1.py * Update sol1.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Maxim Smolskiy --- project_euler/problem_073/sol1.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/project_euler/problem_073/sol1.py b/project_euler/problem_073/sol1.py index 2b66b7d8769b..c39110252ccd 100644 --- a/project_euler/problem_073/sol1.py +++ b/project_euler/problem_073/sol1.py @@ -36,7 +36,12 @@ def solution(max_d: int = 12_000) -> int: fractions_number = 0 for d in range(max_d + 1): - for n in range(d // 3 + 1, (d + 1) // 2): + n_start = d // 3 + 1 + n_step = 1 + if d % 2 == 0: + n_start += 1 - n_start % 2 + n_step = 2 + for n in range(n_start, (d + 1) // 2, n_step): if gcd(n, d) == 1: fractions_number += 1 return fractions_number From c5de3954ccefaeae76b9ebaca045a396619a6761 Mon Sep 17 00:00:00 2001 From: Sohail khan <62786136+byteninjaa0@users.noreply.github.com> Date: Sat, 30 Aug 2025 05:58:38 +0530 Subject: [PATCH 14/32] Add new test for bucket sort algorithm in sorts/bucket_sort.py (#11605) * Add new test for bucket sort algorithm in sorts/bucket_sort.py * Update fractional_knapsack.py * Update matrix_class.py * Update bucket_sort.py * Update bucket_sort.py --------- Co-authored-by: Maxim Smolskiy --- sorts/bucket_sort.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/sorts/bucket_sort.py b/sorts/bucket_sort.py index 1c1320a58a7d..893c7ff3a23a 100644 --- a/sorts/bucket_sort.py +++ b/sorts/bucket_sort.py @@ -51,12 +51,35 @@ def bucket_sort(my_list: list, bucket_count: int = 10) -> list: >>> collection = random.sample(range(-50, 50), 50) >>> bucket_sort(collection) == sorted(collection) True + >>> data = [1, 2, 2, 1, 1, 3] + >>> bucket_sort(data) == sorted(data) + True + >>> data = [5, 5, 5, 5, 5] + >>> bucket_sort(data) == sorted(data) + True + >>> data = [1000, -1000, 500, -500, 0] + >>> bucket_sort(data) == sorted(data) + True + >>> data = [5.5, 2.2, -1.1, 3.3, 0.0] + >>> bucket_sort(data) == sorted(data) + True + >>> bucket_sort([1]) == [1] + True + >>> data = [-1.1, -1.5, -3.4, 2.5, 3.6, -3.3] + >>> bucket_sort(data) == sorted(data) + True + >>> data = [9, 2, 7, 1, 5] + >>> bucket_sort(data) == sorted(data) + True """ if len(my_list) == 0 or bucket_count <= 0: return [] min_value, max_value = min(my_list), max(my_list) + if min_value == max_value: + return my_list + bucket_size = (max_value - min_value) / bucket_count buckets: list[list] = [[] for _ in range(bucket_count)] @@ -73,3 +96,6 @@ def bucket_sort(my_list: list, bucket_count: int = 10) -> list: testmod() assert bucket_sort([4, 5, 3, 2, 1]) == [1, 2, 3, 4, 5] assert bucket_sort([0, 1, -10, 15, 2, -2]) == [-10, -2, 0, 1, 2, 15] + assert bucket_sort([1.1, 1.2, -1.2, 0, 2.4]) == [-1.2, 0, 1.1, 1.2, 2.4] + assert bucket_sort([5, 5, 5, 5, 5]) == [5, 5, 5, 5, 5] + assert bucket_sort([-5, -1, -6, -2]) == [-6, -5, -2, -1] From 1e0d3173fa4566bc03fbc207168225d948153bb6 Mon Sep 17 00:00:00 2001 From: Abhijit Kumar Singh Date: Sat, 30 Aug 2025 06:07:13 +0530 Subject: [PATCH 15/32] Specify space complexity for merge sort (#11749) * Added space complexity Space complexity of merge sort is a key factor, when compared to quick sort * Update merge_sort.py --------- Co-authored-by: Maxim Smolskiy --- sorts/merge_sort.py | 1 + 1 file changed, 1 insertion(+) diff --git a/sorts/merge_sort.py b/sorts/merge_sort.py index 0628b848b794..11c202788035 100644 --- a/sorts/merge_sort.py +++ b/sorts/merge_sort.py @@ -18,6 +18,7 @@ def merge_sort(collection: list) -> list: :return: The same collection ordered in ascending order. Time Complexity: O(n log n) + Space Complexity: O(n) Examples: >>> merge_sort([0, 5, 3, 2, 2]) From b0920454ccae10414aed0cb7ed4ccc60930f341b Mon Sep 17 00:00:00 2001 From: Ronald Ngounou <74538524+ronaldngounou@users.noreply.github.com> Date: Sat, 30 Aug 2025 01:49:44 -0700 Subject: [PATCH 16/32] Add unit tests to binary_tree_path_sum.py (#11833) * test: Add unit tests * test: Add successful tests in binaree_tree_path_sum * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update binary_tree_path_sum.py * Update binary_tree_path_sum.py * Update binary_tree_path_sum.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Maxim Smolskiy --- .../binary_tree/binary_tree_path_sum.py | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/data_structures/binary_tree/binary_tree_path_sum.py b/data_structures/binary_tree/binary_tree_path_sum.py index a3fe9ca7a7e2..8477690c777a 100644 --- a/data_structures/binary_tree/binary_tree_path_sum.py +++ b/data_structures/binary_tree/binary_tree_path_sum.py @@ -50,6 +50,26 @@ class BinaryTreePathSum: >>> tree.right.right = Node(10) >>> BinaryTreePathSum().path_sum(tree, 8) 2 + >>> BinaryTreePathSum().path_sum(None, 0) + 0 + >>> BinaryTreePathSum().path_sum(tree, 0) + 0 + + The second tree looks like this + 0 + / \ + 5 5 + + >>> tree2 = Node(0) + >>> tree2.left = Node(5) + >>> tree2.right = Node(5) + + >>> BinaryTreePathSum().path_sum(tree2, 5) + 4 + >>> BinaryTreePathSum().path_sum(tree2, -1) + 0 + >>> BinaryTreePathSum().path_sum(tree2, 0) + 1 """ target: int From 501576f90e97de5eedffc7d7196a5fb1fa751028 Mon Sep 17 00:00:00 2001 From: ANIRUDDHA ADAK Date: Sat, 30 Aug 2025 15:41:57 +0530 Subject: [PATCH 17/32] Update README.md (#12345) * Update README.md Add emojis for enhanced visual appeal and readability in README . * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update README.md * Update README.md * Update README.md --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Maxim Smolskiy --- README.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index fe65bb253360..182d36a8d905 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@

The Algorithms - Python

+ @@ -19,6 +20,7 @@ Gitter chat +
@@ -30,20 +32,21 @@ code style: black + -

All algorithms implemented in Python - for education

+

All algorithms implemented in Python - for education 📚

Implementations are for learning purposes only. They may be less efficient than the implementations in the Python standard library. Use them at your discretion. -## Getting Started +## 🚀 Getting Started -Read through our [Contribution Guidelines](CONTRIBUTING.md) before you contribute. +📋 Read through our [Contribution Guidelines](CONTRIBUTING.md) before you contribute. -## Community Channels +## 🌐 Community Channels 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 +## 📜 List of Algorithms See our [directory](DIRECTORY.md) for easier navigation and a better overview of the project. From e3a263c1ed4ed1efdf5682714b1493f28d926760 Mon Sep 17 00:00:00 2001 From: lorenzo30salgado <79310171+lorenzo30salgado@users.noreply.github.com> Date: Sat, 30 Aug 2025 22:58:54 +0200 Subject: [PATCH 18/32] Adding a 3D plot to the k-means clustering algorithm (#12372) * Adding a 3D plot to the k-means clustering algorithm * Update k_means_clust.py * Update k_means_clust.py --------- Co-authored-by: Maxim Smolskiy --- machine_learning/k_means_clust.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/machine_learning/k_means_clust.py b/machine_learning/k_means_clust.py index a926362fc18b..a55153628f9c 100644 --- a/machine_learning/k_means_clust.py +++ b/machine_learning/k_means_clust.py @@ -37,7 +37,13 @@ heterogeneity, k ) - 5. Transfers Dataframe into excel format it must have feature called + 5. Plot the labeled 3D data points with centroids. + plot_kmeans( + X, + centroids, + cluster_assignment + ) + 6. Transfers Dataframe into excel format it must have feature called 'Clust' with k means clustering numbers in it. """ @@ -126,6 +132,19 @@ def plot_heterogeneity(heterogeneity, k): plt.show() +def plot_kmeans(data, centroids, cluster_assignment): + ax = plt.axes(projection="3d") + ax.scatter(data[:, 0], data[:, 1], data[:, 2], c=cluster_assignment, cmap="viridis") + ax.scatter( + centroids[:, 0], centroids[:, 1], centroids[:, 2], c="red", s=100, marker="x" + ) + ax.set_xlabel("X") + ax.set_ylabel("Y") + ax.set_zlabel("Z") + ax.set_title("3D K-Means Clustering Visualization") + plt.show() + + def kmeans( data, k, initial_centroids, maxiter=500, record_heterogeneity=None, verbose=False ): @@ -193,6 +212,7 @@ def kmeans( verbose=True, ) plot_heterogeneity(heterogeneity, k) + plot_kmeans(dataset["data"], centroids, cluster_assignment) def report_generator( From 9d52683ecbca6b8dcfa90a2e286d39b66ce5ffef Mon Sep 17 00:00:00 2001 From: ANANT JAIN <139585700+anant-jain01@users.noreply.github.com> Date: Sun, 31 Aug 2025 03:47:55 +0530 Subject: [PATCH 19/32] Create stalin_sort.py (#11989) * Create stalin_sort.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update stalin_sort.py * updating DIRECTORY.md * Update stalin_sort.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Maxim Smolskiy Co-authored-by: MaximSmolskiy --- DIRECTORY.md | 1 + sorts/stalin_sort.py | 47 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 sorts/stalin_sort.py diff --git a/DIRECTORY.md b/DIRECTORY.md index 2df9d56e99b7..36acb3b97f1e 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -1301,6 +1301,7 @@ * [Shell Sort](sorts/shell_sort.py) * [Shrink Shell Sort](sorts/shrink_shell_sort.py) * [Slowsort](sorts/slowsort.py) + * [Stalin Sort](sorts/stalin_sort.py) * [Stooge Sort](sorts/stooge_sort.py) * [Strand Sort](sorts/strand_sort.py) * [Tim Sort](sorts/tim_sort.py) diff --git a/sorts/stalin_sort.py b/sorts/stalin_sort.py new file mode 100644 index 000000000000..6dd5708c7f01 --- /dev/null +++ b/sorts/stalin_sort.py @@ -0,0 +1,47 @@ +""" +Stalin Sort algorithm: Removes elements that are out of order. +Elements that are not greater than or equal to the previous element are discarded. +Reference: https://medium.com/@kaweendra/the-ultimate-sorting-algorithm-6513d6968420 +""" + + +def stalin_sort(sequence: list[int]) -> list[int]: + """ + Sorts a list using the Stalin sort algorithm. + + >>> stalin_sort([4, 3, 5, 2, 1, 7]) + [4, 5, 7] + + >>> stalin_sort([1, 2, 3, 4]) + [1, 2, 3, 4] + + >>> stalin_sort([4, 5, 5, 2, 3]) + [4, 5, 5] + + >>> stalin_sort([6, 11, 12, 4, 1, 5]) + [6, 11, 12] + + >>> stalin_sort([5, 0, 4, 3]) + [5] + + >>> stalin_sort([5, 4, 3, 2, 1]) + [5] + + >>> stalin_sort([1, 2, 3, 4, 5]) + [1, 2, 3, 4, 5] + + >>> stalin_sort([1, 2, 8, 7, 6]) + [1, 2, 8] + """ + result = [sequence[0]] + for element in sequence[1:]: + if element >= result[-1]: + result.append(element) + + return result + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 0876a87186516f93c8d53ee69a600630905984a9 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 1 Sep 2025 20:33:56 +0200 Subject: [PATCH 20/32] [pre-commit.ci] pre-commit autoupdate (#12944) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.12.10 → v0.12.11](https://github.com/astral-sh/ruff-pre-commit/compare/v0.12.10...v0.12.11) 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 6eb0906fb23a..f60913a743ad 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.12.10 + rev: v0.12.11 hooks: - id: ruff-check - id: ruff-format From 544f48ff34492eba4cf452fca3eba7bdff1cda5c Mon Sep 17 00:00:00 2001 From: hema_ameh <152301559+PYDIMARRI-HEMA-HARSHINI-23-586@users.noreply.github.com> Date: Thu, 4 Sep 2025 06:19:59 +0530 Subject: [PATCH 21/32] Fix is_palindrome_recursive logic in strings/palindrome.py (#12946) * Fix: is_palindrome_recursive logic for 2-char strings * Update palindrome.py * Update palindrome.py * Update palindrome.py --------- Co-authored-by: Maxim Smolskiy --- strings/palindrome.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/strings/palindrome.py b/strings/palindrome.py index bfdb3ddcf396..e765207e5942 100644 --- a/strings/palindrome.py +++ b/strings/palindrome.py @@ -11,6 +11,8 @@ "BB": True, "ABC": False, "amanaplanacanalpanama": True, # "a man a plan a canal panama" + "abcdba": False, + "AB": False, } # Ensure our test data is valid assert all((key == key[::-1]) is value for key, value in test_data.items()) @@ -61,7 +63,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) <= 2: + if len(s) <= 1: return True if s[0] == s[len(s) - 1]: return is_palindrome_recursive(s[1:-1]) From 4ce1185f9e92ecea6805c8ee77404a56d8f70ea9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 5 Sep 2025 08:23:36 +0200 Subject: [PATCH 22/32] Bump actions/setup-python from 5 to 6 (#12952) Bumps [actions/setup-python](https://github.com/actions/setup-python) from 5 to 6. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v5...v6) --- updated-dependencies: - dependency-name: actions/setup-python dependency-version: '6' 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/directory_writer.yml | 2 +- .github/workflows/project_euler.yml | 4 ++-- .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 69192db0c4c6..731e3fad3b85 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,7 +14,7 @@ jobs: with: enable-cache: true cache-dependency-glob: uv.lock - - uses: actions/setup-python@v5 + - uses: actions/setup-python@v6 with: python-version: 3.x allow-prereleases: true diff --git a/.github/workflows/directory_writer.yml b/.github/workflows/directory_writer.yml index f5167f8d1a58..9a4682677c00 100644 --- a/.github/workflows/directory_writer.yml +++ b/.github/workflows/directory_writer.yml @@ -9,7 +9,7 @@ jobs: - uses: actions/checkout@v5 with: fetch-depth: 0 - - uses: actions/setup-python@v5 + - uses: actions/setup-python@v6 with: python-version: 3.x - name: Write DIRECTORY.md diff --git a/.github/workflows/project_euler.yml b/.github/workflows/project_euler.yml index 8b8cb2a1e68f..f52ff280b29a 100644 --- a/.github/workflows/project_euler.yml +++ b/.github/workflows/project_euler.yml @@ -16,7 +16,7 @@ jobs: steps: - uses: actions/checkout@v5 - uses: astral-sh/setup-uv@v6 - - uses: actions/setup-python@v5 + - uses: actions/setup-python@v6 with: python-version: 3.x - run: uv sync --group=euler-validate --group=test @@ -26,7 +26,7 @@ jobs: steps: - uses: actions/checkout@v5 - uses: astral-sh/setup-uv@v6 - - uses: actions/setup-python@v5 + - uses: actions/setup-python@v6 with: python-version: 3.x - run: uv sync --group=euler-validate --group=test diff --git a/.github/workflows/sphinx.yml b/.github/workflows/sphinx.yml index e28fa04f3ab4..bd253dc3de65 100644 --- a/.github/workflows/sphinx.yml +++ b/.github/workflows/sphinx.yml @@ -27,7 +27,7 @@ jobs: steps: - uses: actions/checkout@v5 - uses: astral-sh/setup-uv@v6 - - uses: actions/setup-python@v5 + - uses: actions/setup-python@v6 with: python-version: 3.13 allow-prereleases: true From 8f1a6b0ca0cbe563ea3f48c43695a07959b923a1 Mon Sep 17 00:00:00 2001 From: Juan Dupierris Date: Mon, 8 Sep 2025 01:41:07 +0200 Subject: [PATCH 23/32] Adding the function is_proth_number (#12399) * Adding the function isProthNumber(n : int) which returns true if n is a Proth number * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fixing the issues of the isprothnumber function * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * New fixes on isprothnumber() * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fixes on isprothnumber() * Fixes on isprothnumber * Fixes on isprothnumber() * Fixes on isprothnumber * Update proth_number.py * Update proth_number.py * Update proth_number.py * Update proth_number.py * Update proth_number.py * Update proth_number.py * Update proth_number.py * Update proth_number.py --------- Co-authored-by: Juanitoupipou Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Maxim Smolskiy --- maths/special_numbers/proth_number.py | 50 +++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/maths/special_numbers/proth_number.py b/maths/special_numbers/proth_number.py index 47747ed260f7..b9b827b6a5a2 100644 --- a/maths/special_numbers/proth_number.py +++ b/maths/special_numbers/proth_number.py @@ -59,6 +59,50 @@ def proth(number: int) -> int: return proth_list[number - 1] +def is_proth_number(number: int) -> bool: + """ + :param number: positive integer number + :return: true if number is a Proth number, false otherwise + >>> is_proth_number(1) + False + >>> is_proth_number(2) + False + >>> is_proth_number(3) + True + >>> is_proth_number(4) + False + >>> is_proth_number(5) + True + >>> is_proth_number(34) + False + >>> is_proth_number(-1) + Traceback (most recent call last): + ... + ValueError: Input value of [number=-1] must be > 0 + >>> is_proth_number(6.0) + Traceback (most recent call last): + ... + TypeError: Input value of [number=6.0] must be an integer + """ + if not isinstance(number, int): + message = f"Input value of [{number=}] must be an integer" + raise TypeError(message) + + if number <= 0: + message = f"Input value of [{number=}] must be > 0" + raise ValueError(message) + + if number == 1: + return False + + number -= 1 + n = 0 + while number % 2 == 0: + n += 1 + number //= 2 + return number < 2**n + + if __name__ == "__main__": import doctest @@ -73,3 +117,9 @@ def proth(number: int) -> int: continue print(f"The {number}th Proth number: {value}") + + for number in [1, 2, 3, 4, 5, 9, 13, 49, 57, 193, 241, 163, 201]: + if is_proth_number(number): + print(f"{number} is a Proth number") + else: + print(f"{number} is not a Proth number") From 18c853d301eb03ca2ba829250c3d415485e49d8b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 8 Sep 2025 20:46:04 +0200 Subject: [PATCH 24/32] [pre-commit.ci] pre-commit autoupdate (#12961) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.12.11 → v0.12.12](https://github.com/astral-sh/ruff-pre-commit/compare/v0.12.11...v0.12.12) 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 f60913a743ad..c30442a2a6f6 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.12.11 + rev: v0.12.12 hooks: - id: ruff-check - id: ruff-format From 63180d7e243a95ce28c2b52abcd7c81c452f623f Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Sat, 13 Sep 2025 00:56:14 +0200 Subject: [PATCH 25/32] pre-commit autoupdate 2025-09-11 (#12963) * pre-commit autoupdate 2025-09-11 * [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 | 2 +- data_structures/arrays/sudoku_solver.py | 2 +- data_structures/trie/radix_tree.py | 4 ++-- graphs/graph_adjacency_list.py | 10 +++++----- graphs/graph_adjacency_matrix.py | 10 +++++----- knapsack/tests/test_greedy_knapsack.py | 12 +++++++----- linear_algebra/gaussian_elimination.py | 2 +- linear_algebra/jacobi_iteration_method.py | 4 ++-- machine_learning/polynomial_regression.py | 2 +- machine_learning/principle_component_analysis.py | 2 +- maths/chinese_remainder_theorem.py | 2 +- maths/modular_division.py | 4 ++-- neural_network/convolution_neural_network.py | 4 ++-- project_euler/problem_551/sol1.py | 2 +- pyproject.toml | 1 - scheduling/multi_level_feedback_queue.py | 2 +- 16 files changed, 33 insertions(+), 32 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c30442a2a6f6..4af51c08d8a4 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.12.12 + rev: v0.13.0 hooks: - id: ruff-check - id: ruff-format diff --git a/data_structures/arrays/sudoku_solver.py b/data_structures/arrays/sudoku_solver.py index 4c722f12fd6e..07269e2a69cc 100644 --- a/data_structures/arrays/sudoku_solver.py +++ b/data_structures/arrays/sudoku_solver.py @@ -149,7 +149,7 @@ def search(values): 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) + _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]) diff --git a/data_structures/trie/radix_tree.py b/data_structures/trie/radix_tree.py index caf566a6ce30..bd2306befa79 100644 --- a/data_structures/trie/radix_tree.py +++ b/data_structures/trie/radix_tree.py @@ -115,7 +115,7 @@ def find(self, word: str) -> bool: if not incoming_node: return False else: - matching_string, remaining_prefix, remaining_word = incoming_node.match( + _matching_string, remaining_prefix, remaining_word = incoming_node.match( word ) # If there is remaining prefix, the word can't be on the tree @@ -144,7 +144,7 @@ def delete(self, word: str) -> bool: if not incoming_node: return False else: - matching_string, remaining_prefix, remaining_word = incoming_node.match( + _matching_string, remaining_prefix, remaining_word = incoming_node.match( word ) # If there is remaining prefix, the word can't be on the tree diff --git a/graphs/graph_adjacency_list.py b/graphs/graph_adjacency_list.py index 244e59e0e1bf..c901e2cf3dac 100644 --- a/graphs/graph_adjacency_list.py +++ b/graphs/graph_adjacency_list.py @@ -448,7 +448,7 @@ def test_remove_edge(self) -> None: ( undirected_graph, directed_graph, - random_vertices, + _random_vertices, random_edges, ) = self.__generate_graphs(20, 0, 100, 4) @@ -502,7 +502,7 @@ def test_add_vertex_exception_check(self) -> None: undirected_graph, directed_graph, random_vertices, - random_edges, + _random_edges, ) = self.__generate_graphs(20, 0, 100, 4) for vertex in random_vertices: @@ -516,7 +516,7 @@ def test_remove_vertex_exception_check(self) -> None: undirected_graph, directed_graph, random_vertices, - random_edges, + _random_edges, ) = self.__generate_graphs(20, 0, 100, 4) for i in range(101): @@ -530,7 +530,7 @@ def test_add_edge_exception_check(self) -> None: ( undirected_graph, directed_graph, - random_vertices, + _random_vertices, random_edges, ) = self.__generate_graphs(20, 0, 100, 4) @@ -569,7 +569,7 @@ def test_contains_edge_exception_check(self) -> None: undirected_graph, directed_graph, random_vertices, - random_edges, + _random_edges, ) = self.__generate_graphs(20, 0, 100, 4) for vertex in random_vertices: diff --git a/graphs/graph_adjacency_matrix.py b/graphs/graph_adjacency_matrix.py index 8eeeae786513..6dca0fbbcf05 100644 --- a/graphs/graph_adjacency_matrix.py +++ b/graphs/graph_adjacency_matrix.py @@ -469,7 +469,7 @@ def test_remove_edge(self) -> None: ( undirected_graph, directed_graph, - random_vertices, + _random_vertices, random_edges, ) = self.__generate_graphs(20, 0, 100, 4) @@ -523,7 +523,7 @@ def test_add_vertex_exception_check(self) -> None: undirected_graph, directed_graph, random_vertices, - random_edges, + _random_edges, ) = self.__generate_graphs(20, 0, 100, 4) for vertex in random_vertices: @@ -537,7 +537,7 @@ def test_remove_vertex_exception_check(self) -> None: undirected_graph, directed_graph, random_vertices, - random_edges, + _random_edges, ) = self.__generate_graphs(20, 0, 100, 4) for i in range(101): @@ -551,7 +551,7 @@ def test_add_edge_exception_check(self) -> None: ( undirected_graph, directed_graph, - random_vertices, + _random_vertices, random_edges, ) = self.__generate_graphs(20, 0, 100, 4) @@ -590,7 +590,7 @@ def test_contains_edge_exception_check(self) -> None: undirected_graph, directed_graph, random_vertices, - random_edges, + _random_edges, ) = self.__generate_graphs(20, 0, 100, 4) for vertex in random_vertices: diff --git a/knapsack/tests/test_greedy_knapsack.py b/knapsack/tests/test_greedy_knapsack.py index e6a40084109e..7ebaddd3c99e 100644 --- a/knapsack/tests/test_greedy_knapsack.py +++ b/knapsack/tests/test_greedy_knapsack.py @@ -28,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 - pytest.raises(ValueError, match="max_weight must greater than zero.") + pytest.raises(ValueError, match=r"max_weight must greater than zero.") def test_negative_profit_value(self): """ @@ -38,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 - pytest.raises(ValueError, match="Weight can not be negative.") + pytest.raises(ValueError, match=r"Weight can not be negative.") def test_negative_weight_value(self): """ @@ -48,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 - pytest.raises(ValueError, match="Profit can not be negative.") + pytest.raises(ValueError, match=r"Profit can not be negative.") def test_null_max_weight(self): """ @@ -58,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 - pytest.raises(ValueError, match="max_weight must greater than zero.") + pytest.raises(ValueError, match=r"max_weight must greater than zero.") def test_unequal_list_length(self): """ @@ -68,7 +68,9 @@ def test_unequal_list_length(self): # profit = [10, 20, 30, 40, 50] # weight = [2, 4, 6, 8, 10, 12] # max_weight = 100 - pytest.raises(IndexError, match="The length of profit and weight must be same.") + pytest.raises( + IndexError, match=r"The length of profit and weight must be same." + ) if __name__ == "__main__": diff --git a/linear_algebra/gaussian_elimination.py b/linear_algebra/gaussian_elimination.py index 6f4075b710fd..cf816940b0d1 100644 --- a/linear_algebra/gaussian_elimination.py +++ b/linear_algebra/gaussian_elimination.py @@ -33,7 +33,7 @@ def retroactive_resolution( [ 0.5]]) """ - rows, columns = np.shape(coefficients) + rows, _columns = np.shape(coefficients) x: NDArray[float64] = np.zeros((rows, 1), dtype=float) for row in reversed(range(rows)): diff --git a/linear_algebra/jacobi_iteration_method.py b/linear_algebra/jacobi_iteration_method.py index 2cc9c103018b..0f9fcde7af6c 100644 --- a/linear_algebra/jacobi_iteration_method.py +++ b/linear_algebra/jacobi_iteration_method.py @@ -112,7 +112,7 @@ def jacobi_iteration_method( (coefficient_matrix, constant_matrix), axis=1 ) - rows, cols = table.shape + rows, _cols = table.shape strictly_diagonally_dominant(table) @@ -149,7 +149,7 @@ def jacobi_iteration_method( # 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) + _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 diff --git a/machine_learning/polynomial_regression.py b/machine_learning/polynomial_regression.py index 212f40bea197..f52177df1292 100644 --- a/machine_learning/polynomial_regression.py +++ b/machine_learning/polynomial_regression.py @@ -93,7 +93,7 @@ def _design_matrix(data: np.ndarray, degree: int) -> np.ndarray: ... ValueError: Data must have dimensions N x 1 """ - rows, *remaining = data.shape + _rows, *remaining = data.shape if remaining: raise ValueError("Data must have dimensions N x 1") diff --git a/machine_learning/principle_component_analysis.py b/machine_learning/principle_component_analysis.py index 46ccdb968494..174500d89620 100644 --- a/machine_learning/principle_component_analysis.py +++ b/machine_learning/principle_component_analysis.py @@ -65,7 +65,7 @@ def main() -> None: """ Driver function to execute PCA and display results. """ - data_x, data_y = collect_dataset() + data_x, _data_y = collect_dataset() # Number of principal components to retain n_components = 2 diff --git a/maths/chinese_remainder_theorem.py b/maths/chinese_remainder_theorem.py index 18af63d106e8..b7a7712ae917 100644 --- a/maths/chinese_remainder_theorem.py +++ b/maths/chinese_remainder_theorem.py @@ -65,7 +65,7 @@ def invert_modulo(a: int, n: int) -> int: 1 """ - (b, x) = extended_euclid(a, n) + (b, _x) = extended_euclid(a, n) if b < 0: b = (b % n + n) % n return b diff --git a/maths/modular_division.py b/maths/modular_division.py index 2f8f4479b27d..94f12b3e096e 100644 --- a/maths/modular_division.py +++ b/maths/modular_division.py @@ -31,7 +31,7 @@ def modular_division(a: int, b: int, n: int) -> int: assert n > 1 assert a > 0 assert greatest_common_divisor(a, n) == 1 - (d, t, s) = extended_gcd(n, a) # Implemented below + (_d, _t, s) = extended_gcd(n, a) # Implemented below x = (b * s) % n return x @@ -47,7 +47,7 @@ def invert_modulo(a: int, n: int) -> int: 1 """ - (b, x) = extended_euclid(a, n) # Implemented below + (b, _x) = extended_euclid(a, n) # Implemented below if b < 0: b = (b % n + n) % n return b diff --git a/neural_network/convolution_neural_network.py b/neural_network/convolution_neural_network.py index d4ac360a98de..6b1aa50c7981 100644 --- a/neural_network/convolution_neural_network.py +++ b/neural_network/convolution_neural_network.py @@ -317,7 +317,7 @@ def predict(self, datas_test): print((" - - Shape: Test_Data ", np.shape(datas_test))) for p in range(len(datas_test)): data_test = np.asmatrix(datas_test[p]) - data_focus1, data_conved1 = self.convolute( + _data_focus1, data_conved1 = self.convolute( data_test, self.conv1, self.w_conv1, @@ -339,7 +339,7 @@ def predict(self, datas_test): def convolution(self, data): # return the data of image after convoluting process so we can check it out data_test = np.asmatrix(data) - data_focus1, data_conved1 = self.convolute( + _data_focus1, data_conved1 = self.convolute( data_test, self.conv1, self.w_conv1, diff --git a/project_euler/problem_551/sol1.py b/project_euler/problem_551/sol1.py index 100e9d41dd31..e13cf77a776d 100644 --- a/project_euler/problem_551/sol1.py +++ b/project_euler/problem_551/sol1.py @@ -185,7 +185,7 @@ def solution(n: int = 10**15) -> int: i = 1 dn = 0 while True: - diff, terms_jumped = next_term(digits, 20, i + dn, n) + _diff, terms_jumped = next_term(digits, 20, i + dn, n) dn += terms_jumped if dn == n - i: break diff --git a/pyproject.toml b/pyproject.toml index b680cc0d439e..71eb730f1329 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -124,7 +124,6 @@ lint.ignore = [ "S311", # Standard pseudo-random generators are not suitable for cryptographic purposes -- FIX ME "SIM905", # Consider using a list literal instead of `str.split` -- DO NOT FIX "SLF001", # Private member accessed: `_Iterator` -- FIX ME - "UP038", # Use `X | Y` in `{}` call instead of `(X, Y)` -- DO NOT FIX ] lint.per-file-ignores."data_structures/hashing/tests/test_hash_map.py" = [ diff --git a/scheduling/multi_level_feedback_queue.py b/scheduling/multi_level_feedback_queue.py index abee3c85c5a5..58ba2afa0e67 100644 --- a/scheduling/multi_level_feedback_queue.py +++ b/scheduling/multi_level_feedback_queue.py @@ -255,7 +255,7 @@ def multi_level_feedback_queue(self) -> deque[Process]: # all queues except last one have round_robin algorithm for i in range(self.number_of_queues - 1): - finished, self.ready_queue = self.round_robin( + _finished, self.ready_queue = self.round_robin( self.ready_queue, self.time_slices[i] ) # the last queue has first_come_first_served algorithm From 0ee534edde76462eeace007afdba3c6a4d43dbf0 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 15 Sep 2025 20:17:29 +0200 Subject: [PATCH 26/32] [pre-commit.ci] pre-commit autoupdate (#12969) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/mirrors-mypy: v1.17.1 → v1.18.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.17.1...v1.18.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 4af51c08d8a4..9fbeb9a08682 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -47,7 +47,7 @@ repos: - id: validate-pyproject - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.17.1 + rev: v1.18.1 hooks: - id: mypy args: From 4ec71a303b4794d5c702c50e1c64b9175ed72b71 Mon Sep 17 00:00:00 2001 From: lighting9999 Date: Thu, 18 Sep 2025 21:55:59 +0800 Subject: [PATCH 27/32] fix covid_stats_via_xpath.py (#12975) * fix covid_stats_via_xpath.py Improve error handling. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix covid_stats_via_xpath.py typo * [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 * upgrade covid_stats_via_xpath.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update and fix covid_stats_via_xpath.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- web_programming/covid_stats_via_xpath.py | 44 +++++++++++++++++------- 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/web_programming/covid_stats_via_xpath.py b/web_programming/covid_stats_via_xpath.py index 9c016ba414ea..88a248610441 100644 --- a/web_programming/covid_stats_via_xpath.py +++ b/web_programming/covid_stats_via_xpath.py @@ -1,7 +1,8 @@ """ -This is to show simple COVID19 info fetching from worldometers archive site using lxml -* The main motivation to use lxml in place of bs4 is that it is faster and therefore -more convenient to use in Python web projects (e.g. Django or Flask-based) +This script demonstrates fetching simple COVID-19 statistics from the +Worldometers archive site using lxml. lxml is chosen over BeautifulSoup +for its speed and convenience in Python web projects (such as Django or +Flask). """ # /// script @@ -25,15 +26,34 @@ class CovidData(NamedTuple): def covid_stats( - url: str = "https://web.archive.org/web/20250825095350/https://www.worldometers.info/coronavirus/", + url: str = ( + "https://web.archive.org/web/20250825095350/" + "https://www.worldometers.info/coronavirus/" + ), ) -> CovidData: xpath_str = '//div[@class = "maincounter-number"]/span/text()' - return CovidData( - *html.fromstring(httpx.get(url, timeout=10).content).xpath(xpath_str) + try: + response = httpx.get(url, timeout=10).raise_for_status() + except httpx.TimeoutException: + print( + "Request timed out. Please check your network connection " + "or try again later." + ) + return CovidData("N/A", "N/A", "N/A") + except httpx.HTTPStatusError as e: + print(f"HTTP error occurred: {e}") + return CovidData("N/A", "N/A", "N/A") + data = html.fromstring(response.content).xpath(xpath_str) + if len(data) != 3: + print("Unexpected data format. The page structure may have changed.") + data = "N/A", "N/A", "N/A" + return CovidData(*data) + + +if __name__ == "__main__": + fmt = ( + "Total COVID-19 cases in the world: {}\n" + "Total deaths due to COVID-19 in the world: {}\n" + "Total COVID-19 patients recovered in the world: {}" ) - - -fmt = """Total COVID-19 cases in the world: {} -Total deaths due to COVID-19 in the world: {} -Total COVID-19 patients recovered in the world: {}""" -print(fmt.format(*covid_stats())) + print(fmt.format(*covid_stats())) From e696e4dc9007b56d9e14d40d203c36ef861ab1b2 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 23 Sep 2025 02:18:01 +0200 Subject: [PATCH 28/32] [pre-commit.ci] pre-commit autoupdate (#12988) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.13.0 → v0.13.1](https://github.com/astral-sh/ruff-pre-commit/compare/v0.13.0...v0.13.1) - [github.com/pre-commit/mirrors-mypy: v1.18.1 → v1.18.2](https://github.com/pre-commit/mirrors-mypy/compare/v1.18.1...v1.18.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 9fbeb9a08682..2f7c43b06af2 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.13.0 + rev: v0.13.1 hooks: - id: ruff-check - id: ruff-format @@ -47,7 +47,7 @@ repos: - id: validate-pyproject - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.18.1 + rev: v1.18.2 hooks: - id: mypy args: From 8d1fb262dab5d3f04c87f5433e8e2a6b9547ece4 Mon Sep 17 00:00:00 2001 From: kathrynpete <166650430+kathrynpete@users.noreply.github.com> Date: Tue, 23 Sep 2025 02:51:49 -0400 Subject: [PATCH 29/32] Added edit_distance test cases (#12984) * Added edit_distance test cases * Update edit_distance.py --------- Co-authored-by: Maxim Smolskiy --- strings/edit_distance.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/strings/edit_distance.py b/strings/edit_distance.py index e842c8555c8e..77ed23037937 100644 --- a/strings/edit_distance.py +++ b/strings/edit_distance.py @@ -14,6 +14,20 @@ def edit_distance(source: str, target: str) -> int: >>> edit_distance("GATTIC", "GALTIC") 1 + >>> edit_distance("NUM3", "HUM2") + 2 + >>> edit_distance("cap", "CAP") + 3 + >>> edit_distance("Cat", "") + 3 + >>> edit_distance("cat", "cat") + 0 + >>> edit_distance("", "123456789") + 9 + >>> edit_distance("Be@uty", "Beautyyyy!") + 5 + >>> edit_distance("lstring", "lsstring") + 1 """ if len(source) == 0: return len(target) From c0ad5bbde403b8db3097745e500e3f086607ee8f Mon Sep 17 00:00:00 2001 From: Dylanskyep <149001171+Dylanskyep@users.noreply.github.com> Date: Wed, 24 Sep 2025 15:18:44 -0400 Subject: [PATCH 30/32] Add doctests for cross function. Contributes to #9943 (#12991) * Add doctests for cross function. Contributes to #9943 * Update sudoku_solver.py --------- Co-authored-by: Maxim Smolskiy --- data_structures/arrays/sudoku_solver.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/data_structures/arrays/sudoku_solver.py b/data_structures/arrays/sudoku_solver.py index 07269e2a69cc..d2fa43bbf298 100644 --- a/data_structures/arrays/sudoku_solver.py +++ b/data_structures/arrays/sudoku_solver.py @@ -11,6 +11,19 @@ def cross(items_a, items_b): """ Cross product of elements in A and elements in B. + + >>> cross('AB', '12') + ['A1', 'A2', 'B1', 'B2'] + >>> cross('ABC', '123') + ['A1', 'A2', 'A3', 'B1', 'B2', 'B3', 'C1', 'C2', 'C3'] + >>> cross('ABC', '1234') + ['A1', 'A2', 'A3', 'A4', 'B1', 'B2', 'B3', 'B4', 'C1', 'C2', 'C3', 'C4'] + >>> cross('', '12') + [] + >>> cross('A', '') + [] + >>> cross('', '') + [] """ return [a + b for a in items_a for b in items_b] From a71618f891da36ae6a8f9b58273f586ca6acf2a4 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 29 Sep 2025 20:30:14 +0200 Subject: [PATCH 31/32] [pre-commit.ci] pre-commit autoupdate (#13006) 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.13.1 → v0.13.2](https://github.com/astral-sh/ruff-pre-commit/compare/v0.13.1...v0.13.2) * ci: autoupdate_schedule: monthly --------- 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 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2f7c43b06af2..5c66d306b0e0 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,3 +1,6 @@ +ci: + autoupdate_schedule: monthly + repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v6.0.0 @@ -16,7 +19,7 @@ repos: - id: auto-walrus - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.13.1 + rev: v0.13.2 hooks: - id: ruff-check - id: ruff-format From ebe0046e46325ac2fb0ed478088ae32e79ae2328 Mon Sep 17 00:00:00 2001 From: Tarannum Date: Mon, 6 Oct 2025 01:21:08 +0530 Subject: [PATCH 32/32] Added search in a rotated sorted array in searches --- searches/search_in_a_rotated_sorted_array.py | 78 ++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 searches/search_in_a_rotated_sorted_array.py diff --git a/searches/search_in_a_rotated_sorted_array.py b/searches/search_in_a_rotated_sorted_array.py new file mode 100644 index 000000000000..0a3488a342bf --- /dev/null +++ b/searches/search_in_a_rotated_sorted_array.py @@ -0,0 +1,78 @@ +def search_in_rotated_sorted_array(nums: list[int], target: int) -> int: + """ + Search for a target value in a rotated sorted array. + + This function implements a modified binary search to find the index of a target + value in an array that was originally sorted in ascending order but then rotated + at some pivot point unknown to you beforehand. + + Args: + nums: A list of integers that is sorted in ascending order and then rotated + at some pivot point. Example: [4, 5, 6, 7, 0, 1, 2] + target: The integer value to search for in the array. + + Returns: + int: The index of target in nums if found, otherwise -1. + + Raises: + ValueError: If nums is empty. + + Examples: + >>> search_in_rotated_sorted_array([4, 5, 6, 7, 0, 1, 2], 0) + 4 + >>> search_in_rotated_sorted_array([4, 5, 6, 7, 0, 1, 2], 3) + -1 + >>> search_in_rotated_sorted_array([1], 0) + -1 + >>> search_in_rotated_sorted_array([1], 1) + 0 + >>> search_in_rotated_sorted_array([], 1) + Traceback (most recent call last): + ... + ValueError: nums cannot be empty + """ + if not nums: + raise ValueError("nums cannot be empty") + + left_index, right_index = 0, len(nums) - 1 + + while left_index <= right_index: + middle_index = (left_index + right_index) // 2 + + if nums[middle_index] == target: + return middle_index + + # Check if left half is sorted + if nums[left_index] <= nums[middle_index]: + # Target is in the sorted left half + if nums[left_index] <= target < nums[middle_index]: + right_index = middle_index - 1 + else: + left_index = middle_index + 1 + # Right half is sorted + elif nums[middle_index] < target <= nums[right_index]: + left_index = middle_index + 1 + else: + right_index = middle_index - 1 + + return -1 + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + + # Additional test cases + test_cases = [ + ([4, 5, 6, 7, 0, 1, 2], 0), + ([4, 5, 6, 7, 0, 1, 2], 3), + ([1], 0), + ([1], 1), + ([3, 1], 1), + ([3, 1], 3), + ] + + for nums, target in test_cases: + result = search_in_rotated_sorted_array(nums, target) + print(f"search_in_rotated_sorted_array({nums}, {target}) = {result}")