From b3b0a91da1040a337e784da773a78ba5a42afa60 Mon Sep 17 00:00:00 2001 From: lighting9999 Date: Sun, 22 Jun 2025 09:02:55 +0800 Subject: [PATCH 001/107] Add aliquot_sum.py new function and dotests --- maths/aliquot_sum.py | 160 ++++++++++++++++++++++++++++++++----------- 1 file changed, 121 insertions(+), 39 deletions(-) diff --git a/maths/aliquot_sum.py b/maths/aliquot_sum.py index 9c58aa61d19e..e5f4be8aae50 100644 --- a/maths/aliquot_sum.py +++ b/maths/aliquot_sum.py @@ -1,48 +1,130 @@ -def aliquot_sum(input_num: int) -> int: +def aliquot_sum( + input_num: int, return_factors: bool = False +) -> int | tuple[int, list[int]]: """ - Finds the aliquot sum of an input integer, where the - aliquot sum of a number n is defined as the sum of all - natural numbers less than n that divide n evenly. For - example, the aliquot sum of 15 is 1 + 3 + 5 = 9. This is - a simple O(n) implementation. - @param input_num: a positive integer whose aliquot sum is to be found - @return: the aliquot sum of input_num, if input_num is positive. - Otherwise, raise a ValueError - Wikipedia Explanation: https://en.wikipedia.org/wiki/Aliquot_sum - - >>> aliquot_sum(15) - 9 - >>> aliquot_sum(6) - 6 - >>> aliquot_sum(-1) - Traceback (most recent call last): - ... - ValueError: Input must be positive - >>> aliquot_sum(0) - Traceback (most recent call last): - ... - ValueError: Input must be positive - >>> aliquot_sum(1.6) - Traceback (most recent call last): - ... - ValueError: Input must be an integer - >>> aliquot_sum(12) - 16 - >>> aliquot_sum(1) - 0 - >>> aliquot_sum(19) - 1 + Calculates the aliquot sum of a positive integer. The aliquot sum is defined as + the sum of all proper divisors of a number (all divisors except the number itself). + + This implementation uses an optimized O(sqrt(n)) algorithm for efficiency. + + Args: + input_num: Positive integer to calculate aliquot sum for + return_factors: If True, returns tuple (aliquot_sum, sorted_factor_list) + + Returns: + Aliquot sum if return_factors=False + Tuple (aliquot_sum, sorted_factor_list) if return_factors=True + + Raises: + TypeError: If input is not an integer + ValueError: If input is not positive + + Examples: + >>> aliquot_sum(15) + 9 + >>> aliquot_sum(15, True) + (9, [1, 3, 5]) + >>> aliquot_sum(1) + 0 """ + # Validate input type - must be integer if not isinstance(input_num, int): - raise ValueError("Input must be an integer") + raise TypeError("Input must be an integer") + + # Validate input value - must be positive if input_num <= 0: - raise ValueError("Input must be positive") - return sum( - divisor for divisor in range(1, input_num // 2 + 1) if input_num % divisor == 0 - ) + raise ValueError("Input must be positive integer") + + # Special case: 1 has no proper divisors + if input_num == 1: + # Return empty factor list if requested + return (0, []) if return_factors else 0 + + # Initialize factors list with 1 (always a divisor) + factors = [1] + total = 1 # Start sum with 1 + + # Calculate square root as optimization boundary + sqrt_num = int(input_num**0.5) + + # Iterate potential divisors from 2 to square root + for divisor in range(2, sqrt_num + 1): + # Check if divisor is a factor + if input_num % divisor == 0: + # Add divisor to factors list + factors.append(divisor) + total += divisor + + # Calculate complement (pair factor) + complement = input_num // divisor + + # Avoid duplicate for perfect squares + if complement != divisor: + factors.append(complement) + total += complement + + # Sort factors for consistent output + factors.sort() + + # Return based on return_factors flag + return (total, factors) if return_factors else total + + +def classify_number(n: int) -> str: + """ + Classifies a number based on its aliquot sum: + - Perfect: aliquot sum = number + - Abundant: aliquot sum > number + - Deficient: aliquot sum < number + + Args: + n: Positive integer to classify + + Returns: + Classification string ("Perfect", "Abundant", or "Deficient") + + Raises: + ValueError: If input is not positive + + Examples: + >>> classify_number(6) + 'Perfect' + >>> classify_number(12) + 'Abundant' + >>> classify_number(19) + 'Deficient' + """ + # Validate input + if n <= 0: + raise ValueError("Input must be positive integer") + + # Special case: 1 is always deficient + if n == 1: + return "Deficient" + + # Calculate aliquot sum + s = aliquot_sum(n) + + # Determine classification + if s == n: + return "Perfect" + return "Abundant" if s > n else "Deficient" if __name__ == "__main__": import doctest - + + # Run embedded doctests for verification doctest.testmod() + + # Additional demonstration examples + print("Aliquot sum of 28:", aliquot_sum(28)) # Perfect number + print("Factors of 28:", aliquot_sum(28, True)[1]) + print("Classification of 28:", classify_number(28)) + + # Large number performance test + try: + print("\nCalculating aliquot sum for 10^9...") + print("Result:", aliquot_sum(10**9)) # 1497558336 + except Exception as e: + print(f"Error occurred: {e}") From 0b7a90f7b9b875c9a3ea269c17bda0c0dd3da444 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 22 Jun 2025 01:09:56 +0000 Subject: [PATCH 002/107] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- maths/aliquot_sum.py | 52 ++++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/maths/aliquot_sum.py b/maths/aliquot_sum.py index e5f4be8aae50..77a2e512666b 100644 --- a/maths/aliquot_sum.py +++ b/maths/aliquot_sum.py @@ -4,21 +4,21 @@ def aliquot_sum( """ Calculates the aliquot sum of a positive integer. The aliquot sum is defined as the sum of all proper divisors of a number (all divisors except the number itself). - + This implementation uses an optimized O(sqrt(n)) algorithm for efficiency. - + Args: input_num: Positive integer to calculate aliquot sum for return_factors: If True, returns tuple (aliquot_sum, sorted_factor_list) - + Returns: Aliquot sum if return_factors=False Tuple (aliquot_sum, sorted_factor_list) if return_factors=True - + Raises: TypeError: If input is not an integer ValueError: If input is not positive - + Examples: >>> aliquot_sum(15) 9 @@ -30,23 +30,23 @@ def aliquot_sum( # Validate input type - must be integer if not isinstance(input_num, int): raise TypeError("Input must be an integer") - + # Validate input value - must be positive if input_num <= 0: raise ValueError("Input must be positive integer") - + # Special case: 1 has no proper divisors if input_num == 1: # Return empty factor list if requested return (0, []) if return_factors else 0 - + # Initialize factors list with 1 (always a divisor) - factors = [1] + factors = [1] total = 1 # Start sum with 1 - + # Calculate square root as optimization boundary sqrt_num = int(input_num**0.5) - + # Iterate potential divisors from 2 to square root for divisor in range(2, sqrt_num + 1): # Check if divisor is a factor @@ -54,18 +54,18 @@ def aliquot_sum( # Add divisor to factors list factors.append(divisor) total += divisor - + # Calculate complement (pair factor) complement = input_num // divisor - + # Avoid duplicate for perfect squares - if complement != divisor: + if complement != divisor: factors.append(complement) total += complement - + # Sort factors for consistent output factors.sort() - + # Return based on return_factors flag return (total, factors) if return_factors else total @@ -76,16 +76,16 @@ def classify_number(n: int) -> str: - Perfect: aliquot sum = number - Abundant: aliquot sum > number - Deficient: aliquot sum < number - + Args: n: Positive integer to classify - + Returns: Classification string ("Perfect", "Abundant", or "Deficient") - + Raises: ValueError: If input is not positive - + Examples: >>> classify_number(6) 'Perfect' @@ -97,14 +97,14 @@ def classify_number(n: int) -> str: # Validate input if n <= 0: raise ValueError("Input must be positive integer") - + # Special case: 1 is always deficient if n == 1: return "Deficient" - + # Calculate aliquot sum s = aliquot_sum(n) - + # Determine classification if s == n: return "Perfect" @@ -113,15 +113,15 @@ def classify_number(n: int) -> str: if __name__ == "__main__": import doctest - + # Run embedded doctests for verification doctest.testmod() - + # Additional demonstration examples print("Aliquot sum of 28:", aliquot_sum(28)) # Perfect number print("Factors of 28:", aliquot_sum(28, True)[1]) print("Classification of 28:", classify_number(28)) - + # Large number performance test try: print("\nCalculating aliquot sum for 10^9...") From 1b0205c63971367220a38358346e6bf01f968ed1 Mon Sep 17 00:00:00 2001 From: lighting9999 Date: Sun, 22 Jun 2025 09:14:25 +0800 Subject: [PATCH 003/107] Update aliquot_sum.py --- maths/aliquot_sum.py | 73 +++++++++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 32 deletions(-) diff --git a/maths/aliquot_sum.py b/maths/aliquot_sum.py index 77a2e512666b..8a28459d7887 100644 --- a/maths/aliquot_sum.py +++ b/maths/aliquot_sum.py @@ -4,21 +4,21 @@ def aliquot_sum( """ Calculates the aliquot sum of a positive integer. The aliquot sum is defined as the sum of all proper divisors of a number (all divisors except the number itself). - + This implementation uses an optimized O(sqrt(n)) algorithm for efficiency. - + Args: input_num: Positive integer to calculate aliquot sum for return_factors: If True, returns tuple (aliquot_sum, sorted_factor_list) - + Returns: Aliquot sum if return_factors=False Tuple (aliquot_sum, sorted_factor_list) if return_factors=True - + Raises: TypeError: If input is not an integer ValueError: If input is not positive - + Examples: >>> aliquot_sum(15) 9 @@ -30,23 +30,23 @@ def aliquot_sum( # Validate input type - must be integer if not isinstance(input_num, int): raise TypeError("Input must be an integer") - + # Validate input value - must be positive if input_num <= 0: raise ValueError("Input must be positive integer") - + # Special case: 1 has no proper divisors if input_num == 1: # Return empty factor list if requested return (0, []) if return_factors else 0 - + # Initialize factors list with 1 (always a divisor) - factors = [1] + factors = [1] total = 1 # Start sum with 1 - + # Calculate square root as optimization boundary sqrt_num = int(input_num**0.5) - + # Iterate potential divisors from 2 to square root for divisor in range(2, sqrt_num + 1): # Check if divisor is a factor @@ -54,18 +54,18 @@ def aliquot_sum( # Add divisor to factors list factors.append(divisor) total += divisor - + # Calculate complement (pair factor) complement = input_num // divisor - + # Avoid duplicate for perfect squares - if complement != divisor: + if complement != divisor: factors.append(complement) total += complement - + # Sort factors for consistent output factors.sort() - + # Return based on return_factors flag return (total, factors) if return_factors else total @@ -76,16 +76,16 @@ def classify_number(n: int) -> str: - Perfect: aliquot sum = number - Abundant: aliquot sum > number - Deficient: aliquot sum < number - + Args: n: Positive integer to classify - + Returns: Classification string ("Perfect", "Abundant", or "Deficient") - + Raises: ValueError: If input is not positive - + Examples: >>> classify_number(6) 'Perfect' @@ -97,14 +97,14 @@ def classify_number(n: int) -> str: # Validate input if n <= 0: raise ValueError("Input must be positive integer") - + # Special case: 1 is always deficient if n == 1: return "Deficient" - - # Calculate aliquot sum - s = aliquot_sum(n) - + + # Calculate aliquot sum (must be int only) + s = aliquot_sum(n) # type: ignore[assignment] + # Determine classification if s == n: return "Perfect" @@ -113,18 +113,27 @@ def classify_number(n: int) -> str: if __name__ == "__main__": import doctest - + # Run embedded doctests for verification doctest.testmod() - + # Additional demonstration examples print("Aliquot sum of 28:", aliquot_sum(28)) # Perfect number - print("Factors of 28:", aliquot_sum(28, True)[1]) + + # Handle tuple return type safely + result = aliquot_sum(28, True) + if isinstance(result, tuple): + print("Factors of 28:", result[1]) + print("Classification of 28:", classify_number(28)) - - # Large number performance test + + # Large number performance test (catch specific errors) try: print("\nCalculating aliquot sum for 10^9...") print("Result:", aliquot_sum(10**9)) # 1497558336 - except Exception as e: - print(f"Error occurred: {e}") + except (TypeError, ValueError) as e: + print(f"Input error: {e}") + except MemoryError: + print("Memory error: Number too large") + except OverflowError: + print("Overflow error: Calculation too large") From 99174568380feb0c6a3b70caae7a2811364637a7 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 22 Jun 2025 01:14:48 +0000 Subject: [PATCH 004/107] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- maths/aliquot_sum.py | 56 ++++++++++++++++++++++---------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/maths/aliquot_sum.py b/maths/aliquot_sum.py index 8a28459d7887..040d3adbb691 100644 --- a/maths/aliquot_sum.py +++ b/maths/aliquot_sum.py @@ -4,21 +4,21 @@ def aliquot_sum( """ Calculates the aliquot sum of a positive integer. The aliquot sum is defined as the sum of all proper divisors of a number (all divisors except the number itself). - + This implementation uses an optimized O(sqrt(n)) algorithm for efficiency. - + Args: input_num: Positive integer to calculate aliquot sum for return_factors: If True, returns tuple (aliquot_sum, sorted_factor_list) - + Returns: Aliquot sum if return_factors=False Tuple (aliquot_sum, sorted_factor_list) if return_factors=True - + Raises: TypeError: If input is not an integer ValueError: If input is not positive - + Examples: >>> aliquot_sum(15) 9 @@ -30,23 +30,23 @@ def aliquot_sum( # Validate input type - must be integer if not isinstance(input_num, int): raise TypeError("Input must be an integer") - + # Validate input value - must be positive if input_num <= 0: raise ValueError("Input must be positive integer") - + # Special case: 1 has no proper divisors if input_num == 1: # Return empty factor list if requested return (0, []) if return_factors else 0 - + # Initialize factors list with 1 (always a divisor) - factors = [1] + factors = [1] total = 1 # Start sum with 1 - + # Calculate square root as optimization boundary sqrt_num = int(input_num**0.5) - + # Iterate potential divisors from 2 to square root for divisor in range(2, sqrt_num + 1): # Check if divisor is a factor @@ -54,18 +54,18 @@ def aliquot_sum( # Add divisor to factors list factors.append(divisor) total += divisor - + # Calculate complement (pair factor) complement = input_num // divisor - + # Avoid duplicate for perfect squares - if complement != divisor: + if complement != divisor: factors.append(complement) total += complement - + # Sort factors for consistent output factors.sort() - + # Return based on return_factors flag return (total, factors) if return_factors else total @@ -76,16 +76,16 @@ def classify_number(n: int) -> str: - Perfect: aliquot sum = number - Abundant: aliquot sum > number - Deficient: aliquot sum < number - + Args: n: Positive integer to classify - + Returns: Classification string ("Perfect", "Abundant", or "Deficient") - + Raises: ValueError: If input is not positive - + Examples: >>> classify_number(6) 'Perfect' @@ -97,14 +97,14 @@ def classify_number(n: int) -> str: # Validate input if n <= 0: raise ValueError("Input must be positive integer") - + # Special case: 1 is always deficient if n == 1: return "Deficient" - + # Calculate aliquot sum (must be int only) s = aliquot_sum(n) # type: ignore[assignment] - + # Determine classification if s == n: return "Perfect" @@ -113,20 +113,20 @@ def classify_number(n: int) -> str: if __name__ == "__main__": import doctest - + # Run embedded doctests for verification doctest.testmod() - + # Additional demonstration examples print("Aliquot sum of 28:", aliquot_sum(28)) # Perfect number - + # Handle tuple return type safely result = aliquot_sum(28, True) if isinstance(result, tuple): print("Factors of 28:", result[1]) - + print("Classification of 28:", classify_number(28)) - + # Large number performance test (catch specific errors) try: print("\nCalculating aliquot sum for 10^9...") From 9869c0100e62050af6bc39ef2ebdc70b642bedf9 Mon Sep 17 00:00:00 2001 From: lighting9999 Date: Sun, 22 Jun 2025 09:19:14 +0800 Subject: [PATCH 005/107] Update aliquot_sum.py --- maths/aliquot_sum.py | 74 +++++++++++++++++++++++--------------------- 1 file changed, 39 insertions(+), 35 deletions(-) diff --git a/maths/aliquot_sum.py b/maths/aliquot_sum.py index 040d3adbb691..560fc2d438cb 100644 --- a/maths/aliquot_sum.py +++ b/maths/aliquot_sum.py @@ -4,21 +4,21 @@ def aliquot_sum( """ Calculates the aliquot sum of a positive integer. The aliquot sum is defined as the sum of all proper divisors of a number (all divisors except the number itself). - + This implementation uses an optimized O(sqrt(n)) algorithm for efficiency. - + Args: input_num: Positive integer to calculate aliquot sum for return_factors: If True, returns tuple (aliquot_sum, sorted_factor_list) - + Returns: Aliquot sum if return_factors=False Tuple (aliquot_sum, sorted_factor_list) if return_factors=True - + Raises: TypeError: If input is not an integer ValueError: If input is not positive - + Examples: >>> aliquot_sum(15) 9 @@ -30,23 +30,23 @@ def aliquot_sum( # Validate input type - must be integer if not isinstance(input_num, int): raise TypeError("Input must be an integer") - + # Validate input value - must be positive if input_num <= 0: raise ValueError("Input must be positive integer") - + # Special case: 1 has no proper divisors if input_num == 1: # Return empty factor list if requested return (0, []) if return_factors else 0 - + # Initialize factors list with 1 (always a divisor) - factors = [1] + factors = [1] total = 1 # Start sum with 1 - + # Calculate square root as optimization boundary sqrt_num = int(input_num**0.5) - + # Iterate potential divisors from 2 to square root for divisor in range(2, sqrt_num + 1): # Check if divisor is a factor @@ -54,18 +54,18 @@ def aliquot_sum( # Add divisor to factors list factors.append(divisor) total += divisor - + # Calculate complement (pair factor) complement = input_num // divisor - + # Avoid duplicate for perfect squares - if complement != divisor: + if complement != divisor: factors.append(complement) total += complement - + # Sort factors for consistent output factors.sort() - + # Return based on return_factors flag return (total, factors) if return_factors else total @@ -76,16 +76,16 @@ def classify_number(n: int) -> str: - Perfect: aliquot sum = number - Abundant: aliquot sum > number - Deficient: aliquot sum < number - + Args: n: Positive integer to classify - + Returns: Classification string ("Perfect", "Abundant", or "Deficient") - + Raises: ValueError: If input is not positive - + Examples: >>> classify_number(6) 'Perfect' @@ -97,14 +97,14 @@ def classify_number(n: int) -> str: # Validate input if n <= 0: raise ValueError("Input must be positive integer") - + # Special case: 1 is always deficient if n == 1: return "Deficient" - - # Calculate aliquot sum (must be int only) - s = aliquot_sum(n) # type: ignore[assignment] - + + # Calculate aliquot sum (explicitly request integer-only result) + s = aliquot_sum(n, return_factors=False) + # Determine classification if s == n: return "Perfect" @@ -113,27 +113,31 @@ def classify_number(n: int) -> str: if __name__ == "__main__": import doctest - + # Run embedded doctests for verification doctest.testmod() - + # Additional demonstration examples print("Aliquot sum of 28:", aliquot_sum(28)) # Perfect number - - # Handle tuple return type safely - result = aliquot_sum(28, True) - if isinstance(result, tuple): - print("Factors of 28:", result[1]) - + + # Get factors for 28 (using explicit tuple handling) + factor_result = aliquot_sum(28, True) + # Since we requested factors, result is tuple (sum, factors) + if isinstance(factor_result, tuple): + print("Factors of 28:", factor_result[1]) + print("Classification of 28:", classify_number(28)) - - # Large number performance test (catch specific errors) + + # Large number performance test with targeted exception handling try: print("\nCalculating aliquot sum for 10^9...") print("Result:", aliquot_sum(10**9)) # 1497558336 except (TypeError, ValueError) as e: + # Handle input-related errors print(f"Input error: {e}") except MemoryError: + # Handle potential memory issues with large numbers print("Memory error: Number too large") except OverflowError: + # Handle numeric overflow print("Overflow error: Calculation too large") From 596281d802e41b7d657006078d7e7eac059aad7a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 22 Jun 2025 01:19:36 +0000 Subject: [PATCH 006/107] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- maths/aliquot_sum.py | 56 ++++++++++++++++++++++---------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/maths/aliquot_sum.py b/maths/aliquot_sum.py index 560fc2d438cb..87b62baeb198 100644 --- a/maths/aliquot_sum.py +++ b/maths/aliquot_sum.py @@ -4,21 +4,21 @@ def aliquot_sum( """ Calculates the aliquot sum of a positive integer. The aliquot sum is defined as the sum of all proper divisors of a number (all divisors except the number itself). - + This implementation uses an optimized O(sqrt(n)) algorithm for efficiency. - + Args: input_num: Positive integer to calculate aliquot sum for return_factors: If True, returns tuple (aliquot_sum, sorted_factor_list) - + Returns: Aliquot sum if return_factors=False Tuple (aliquot_sum, sorted_factor_list) if return_factors=True - + Raises: TypeError: If input is not an integer ValueError: If input is not positive - + Examples: >>> aliquot_sum(15) 9 @@ -30,23 +30,23 @@ def aliquot_sum( # Validate input type - must be integer if not isinstance(input_num, int): raise TypeError("Input must be an integer") - + # Validate input value - must be positive if input_num <= 0: raise ValueError("Input must be positive integer") - + # Special case: 1 has no proper divisors if input_num == 1: # Return empty factor list if requested return (0, []) if return_factors else 0 - + # Initialize factors list with 1 (always a divisor) - factors = [1] + factors = [1] total = 1 # Start sum with 1 - + # Calculate square root as optimization boundary sqrt_num = int(input_num**0.5) - + # Iterate potential divisors from 2 to square root for divisor in range(2, sqrt_num + 1): # Check if divisor is a factor @@ -54,18 +54,18 @@ def aliquot_sum( # Add divisor to factors list factors.append(divisor) total += divisor - + # Calculate complement (pair factor) complement = input_num // divisor - + # Avoid duplicate for perfect squares - if complement != divisor: + if complement != divisor: factors.append(complement) total += complement - + # Sort factors for consistent output factors.sort() - + # Return based on return_factors flag return (total, factors) if return_factors else total @@ -76,16 +76,16 @@ def classify_number(n: int) -> str: - Perfect: aliquot sum = number - Abundant: aliquot sum > number - Deficient: aliquot sum < number - + Args: n: Positive integer to classify - + Returns: Classification string ("Perfect", "Abundant", or "Deficient") - + Raises: ValueError: If input is not positive - + Examples: >>> classify_number(6) 'Perfect' @@ -97,14 +97,14 @@ def classify_number(n: int) -> str: # Validate input if n <= 0: raise ValueError("Input must be positive integer") - + # Special case: 1 is always deficient if n == 1: return "Deficient" - + # Calculate aliquot sum (explicitly request integer-only result) s = aliquot_sum(n, return_factors=False) - + # Determine classification if s == n: return "Perfect" @@ -113,21 +113,21 @@ def classify_number(n: int) -> str: if __name__ == "__main__": import doctest - + # Run embedded doctests for verification doctest.testmod() - + # Additional demonstration examples print("Aliquot sum of 28:", aliquot_sum(28)) # Perfect number - + # Get factors for 28 (using explicit tuple handling) factor_result = aliquot_sum(28, True) # Since we requested factors, result is tuple (sum, factors) if isinstance(factor_result, tuple): print("Factors of 28:", factor_result[1]) - + print("Classification of 28:", classify_number(28)) - + # Large number performance test with targeted exception handling try: print("\nCalculating aliquot sum for 10^9...") From dfd6c97e816d5644932a2ed1fd13cc617df020d2 Mon Sep 17 00:00:00 2001 From: lighting9999 Date: Sun, 22 Jun 2025 09:36:33 +0800 Subject: [PATCH 007/107] Update aliquot_sum.py --- maths/aliquot_sum.py | 74 ++++++++++++++++++++++---------------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/maths/aliquot_sum.py b/maths/aliquot_sum.py index 87b62baeb198..cd5c7953ca2d 100644 --- a/maths/aliquot_sum.py +++ b/maths/aliquot_sum.py @@ -1,24 +1,31 @@ +from typing import overload, Tuple + +@overload +def aliquot_sum(input_num: int, return_factors: bool = False) -> int: ... +@overload +def aliquot_sum(input_num: int, return_factors: bool = True) -> Tuple[int, list[int]]: ... + def aliquot_sum( input_num: int, return_factors: bool = False -) -> int | tuple[int, list[int]]: +) -> int | Tuple[int, list[int]]: """ Calculates the aliquot sum of a positive integer. The aliquot sum is defined as the sum of all proper divisors of a number (all divisors except the number itself). - + This implementation uses an optimized O(sqrt(n)) algorithm for efficiency. - + Args: input_num: Positive integer to calculate aliquot sum for return_factors: If True, returns tuple (aliquot_sum, sorted_factor_list) - + Returns: Aliquot sum if return_factors=False Tuple (aliquot_sum, sorted_factor_list) if return_factors=True - + Raises: TypeError: If input is not an integer ValueError: If input is not positive - + Examples: >>> aliquot_sum(15) 9 @@ -30,23 +37,22 @@ def aliquot_sum( # Validate input type - must be integer if not isinstance(input_num, int): raise TypeError("Input must be an integer") - + # Validate input value - must be positive if input_num <= 0: raise ValueError("Input must be positive integer") - + # Special case: 1 has no proper divisors if input_num == 1: - # Return empty factor list if requested return (0, []) if return_factors else 0 - + # Initialize factors list with 1 (always a divisor) - factors = [1] + factors = [1] total = 1 # Start sum with 1 - + # Calculate square root as optimization boundary sqrt_num = int(input_num**0.5) - + # Iterate potential divisors from 2 to square root for divisor in range(2, sqrt_num + 1): # Check if divisor is a factor @@ -54,18 +60,18 @@ def aliquot_sum( # Add divisor to factors list factors.append(divisor) total += divisor - + # Calculate complement (pair factor) complement = input_num // divisor - + # Avoid duplicate for perfect squares - if complement != divisor: + if complement != divisor: factors.append(complement) total += complement - + # Sort factors for consistent output factors.sort() - + # Return based on return_factors flag return (total, factors) if return_factors else total @@ -76,16 +82,16 @@ def classify_number(n: int) -> str: - Perfect: aliquot sum = number - Abundant: aliquot sum > number - Deficient: aliquot sum < number - + Args: n: Positive integer to classify - + Returns: Classification string ("Perfect", "Abundant", or "Deficient") - + Raises: ValueError: If input is not positive - + Examples: >>> classify_number(6) 'Perfect' @@ -97,14 +103,14 @@ def classify_number(n: int) -> str: # Validate input if n <= 0: raise ValueError("Input must be positive integer") - + # Special case: 1 is always deficient if n == 1: return "Deficient" - - # Calculate aliquot sum (explicitly request integer-only result) - s = aliquot_sum(n, return_factors=False) - + + # Calculate aliquot sum (using only the integer version) + s = aliquot_sum(n) # Default returns only integer + # Determine classification if s == n: return "Perfect" @@ -113,21 +119,15 @@ def classify_number(n: int) -> str: if __name__ == "__main__": import doctest - + # Run embedded doctests for verification doctest.testmod() - + # Additional demonstration examples print("Aliquot sum of 28:", aliquot_sum(28)) # Perfect number - - # Get factors for 28 (using explicit tuple handling) - factor_result = aliquot_sum(28, True) - # Since we requested factors, result is tuple (sum, factors) - if isinstance(factor_result, tuple): - print("Factors of 28:", factor_result[1]) - + print("Factors of 28:", aliquot_sum(28, return_factors=True)[1]) print("Classification of 28:", classify_number(28)) - + # Large number performance test with targeted exception handling try: print("\nCalculating aliquot sum for 10^9...") From 51ba6ba1b6721e904e15f5424d2b27e4b7575b00 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 22 Jun 2025 01:36:57 +0000 Subject: [PATCH 008/107] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- maths/aliquot_sum.py | 58 +++++++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/maths/aliquot_sum.py b/maths/aliquot_sum.py index cd5c7953ca2d..f04f0ff0e848 100644 --- a/maths/aliquot_sum.py +++ b/maths/aliquot_sum.py @@ -1,9 +1,13 @@ from typing import overload, Tuple + @overload def aliquot_sum(input_num: int, return_factors: bool = False) -> int: ... @overload -def aliquot_sum(input_num: int, return_factors: bool = True) -> Tuple[int, list[int]]: ... +def aliquot_sum( + input_num: int, return_factors: bool = True +) -> Tuple[int, list[int]]: ... + def aliquot_sum( input_num: int, return_factors: bool = False @@ -11,21 +15,21 @@ def aliquot_sum( """ Calculates the aliquot sum of a positive integer. The aliquot sum is defined as the sum of all proper divisors of a number (all divisors except the number itself). - + This implementation uses an optimized O(sqrt(n)) algorithm for efficiency. - + Args: input_num: Positive integer to calculate aliquot sum for return_factors: If True, returns tuple (aliquot_sum, sorted_factor_list) - + Returns: Aliquot sum if return_factors=False Tuple (aliquot_sum, sorted_factor_list) if return_factors=True - + Raises: TypeError: If input is not an integer ValueError: If input is not positive - + Examples: >>> aliquot_sum(15) 9 @@ -37,22 +41,22 @@ def aliquot_sum( # Validate input type - must be integer if not isinstance(input_num, int): raise TypeError("Input must be an integer") - + # Validate input value - must be positive if input_num <= 0: raise ValueError("Input must be positive integer") - + # Special case: 1 has no proper divisors if input_num == 1: return (0, []) if return_factors else 0 - + # Initialize factors list with 1 (always a divisor) - factors = [1] + factors = [1] total = 1 # Start sum with 1 - + # Calculate square root as optimization boundary sqrt_num = int(input_num**0.5) - + # Iterate potential divisors from 2 to square root for divisor in range(2, sqrt_num + 1): # Check if divisor is a factor @@ -60,18 +64,18 @@ def aliquot_sum( # Add divisor to factors list factors.append(divisor) total += divisor - + # Calculate complement (pair factor) complement = input_num // divisor - + # Avoid duplicate for perfect squares - if complement != divisor: + if complement != divisor: factors.append(complement) total += complement - + # Sort factors for consistent output factors.sort() - + # Return based on return_factors flag return (total, factors) if return_factors else total @@ -82,16 +86,16 @@ def classify_number(n: int) -> str: - Perfect: aliquot sum = number - Abundant: aliquot sum > number - Deficient: aliquot sum < number - + Args: n: Positive integer to classify - + Returns: Classification string ("Perfect", "Abundant", or "Deficient") - + Raises: ValueError: If input is not positive - + Examples: >>> classify_number(6) 'Perfect' @@ -103,14 +107,14 @@ def classify_number(n: int) -> str: # Validate input if n <= 0: raise ValueError("Input must be positive integer") - + # Special case: 1 is always deficient if n == 1: return "Deficient" - + # Calculate aliquot sum (using only the integer version) s = aliquot_sum(n) # Default returns only integer - + # Determine classification if s == n: return "Perfect" @@ -119,15 +123,15 @@ def classify_number(n: int) -> str: if __name__ == "__main__": import doctest - + # Run embedded doctests for verification doctest.testmod() - + # Additional demonstration examples print("Aliquot sum of 28:", aliquot_sum(28)) # Perfect number print("Factors of 28:", aliquot_sum(28, return_factors=True)[1]) print("Classification of 28:", classify_number(28)) - + # Large number performance test with targeted exception handling try: print("\nCalculating aliquot sum for 10^9...") From 63a40f4ae86a38478cfbf222453499f4ae98fee3 Mon Sep 17 00:00:00 2001 From: lighting9999 Date: Sun, 22 Jun 2025 09:41:27 +0800 Subject: [PATCH 009/107] Update aliquot_sum.py --- maths/aliquot_sum.py | 76 ++++++++++++++++++++++---------------------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/maths/aliquot_sum.py b/maths/aliquot_sum.py index f04f0ff0e848..25d67ae7b50a 100644 --- a/maths/aliquot_sum.py +++ b/maths/aliquot_sum.py @@ -1,35 +1,27 @@ -from typing import overload, Tuple - - -@overload -def aliquot_sum(input_num: int, return_factors: bool = False) -> int: ... -@overload -def aliquot_sum( - input_num: int, return_factors: bool = True -) -> Tuple[int, list[int]]: ... - +from __future__ import annotations # Enable modern type hints +from typing import overload def aliquot_sum( input_num: int, return_factors: bool = False -) -> int | Tuple[int, list[int]]: +) -> int | tuple[int, list[int]]: """ Calculates the aliquot sum of a positive integer. The aliquot sum is defined as the sum of all proper divisors of a number (all divisors except the number itself). - + This implementation uses an optimized O(sqrt(n)) algorithm for efficiency. - + Args: input_num: Positive integer to calculate aliquot sum for return_factors: If True, returns tuple (aliquot_sum, sorted_factor_list) - + Returns: Aliquot sum if return_factors=False Tuple (aliquot_sum, sorted_factor_list) if return_factors=True - + Raises: TypeError: If input is not an integer ValueError: If input is not positive - + Examples: >>> aliquot_sum(15) 9 @@ -41,22 +33,23 @@ def aliquot_sum( # Validate input type - must be integer if not isinstance(input_num, int): raise TypeError("Input must be an integer") - + # Validate input value - must be positive if input_num <= 0: raise ValueError("Input must be positive integer") - + # Special case: 1 has no proper divisors if input_num == 1: + # Return empty factor list if requested return (0, []) if return_factors else 0 - + # Initialize factors list with 1 (always a divisor) - factors = [1] + factors = [1] total = 1 # Start sum with 1 - + # Calculate square root as optimization boundary sqrt_num = int(input_num**0.5) - + # Iterate potential divisors from 2 to square root for divisor in range(2, sqrt_num + 1): # Check if divisor is a factor @@ -64,18 +57,18 @@ def aliquot_sum( # Add divisor to factors list factors.append(divisor) total += divisor - + # Calculate complement (pair factor) complement = input_num // divisor - + # Avoid duplicate for perfect squares - if complement != divisor: + if complement != divisor: factors.append(complement) total += complement - + # Sort factors for consistent output factors.sort() - + # Return based on return_factors flag return (total, factors) if return_factors else total @@ -86,16 +79,16 @@ def classify_number(n: int) -> str: - Perfect: aliquot sum = number - Abundant: aliquot sum > number - Deficient: aliquot sum < number - + Args: n: Positive integer to classify - + Returns: Classification string ("Perfect", "Abundant", or "Deficient") - + Raises: ValueError: If input is not positive - + Examples: >>> classify_number(6) 'Perfect' @@ -107,14 +100,14 @@ def classify_number(n: int) -> str: # Validate input if n <= 0: raise ValueError("Input must be positive integer") - + # Special case: 1 is always deficient if n == 1: return "Deficient" - + # Calculate aliquot sum (using only the integer version) s = aliquot_sum(n) # Default returns only integer - + # Determine classification if s == n: return "Perfect" @@ -123,15 +116,22 @@ def classify_number(n: int) -> str: if __name__ == "__main__": import doctest - + # Run embedded doctests for verification doctest.testmod() - + # Additional demonstration examples print("Aliquot sum of 28:", aliquot_sum(28)) # Perfect number - print("Factors of 28:", aliquot_sum(28, return_factors=True)[1]) + + # Get factors for 28 with type-safe access + factor_result = aliquot_sum(28, return_factors=True) + # Since we know return_factors=True returns a tuple, we can safely unpack + if isinstance(factor_result, tuple): + _, factors = factor_result + print("Factors of 28:", factors) + print("Classification of 28:", classify_number(28)) - + # Large number performance test with targeted exception handling try: print("\nCalculating aliquot sum for 10^9...") From 3c73639aab416f7fbcf4225febc143287719a627 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 22 Jun 2025 01:41:49 +0000 Subject: [PATCH 010/107] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- maths/aliquot_sum.py | 57 ++++++++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/maths/aliquot_sum.py b/maths/aliquot_sum.py index 25d67ae7b50a..e93d731b8aef 100644 --- a/maths/aliquot_sum.py +++ b/maths/aliquot_sum.py @@ -1,27 +1,28 @@ from __future__ import annotations # Enable modern type hints from typing import overload + def aliquot_sum( input_num: int, return_factors: bool = False ) -> int | tuple[int, list[int]]: """ Calculates the aliquot sum of a positive integer. The aliquot sum is defined as the sum of all proper divisors of a number (all divisors except the number itself). - + This implementation uses an optimized O(sqrt(n)) algorithm for efficiency. - + Args: input_num: Positive integer to calculate aliquot sum for return_factors: If True, returns tuple (aliquot_sum, sorted_factor_list) - + Returns: Aliquot sum if return_factors=False Tuple (aliquot_sum, sorted_factor_list) if return_factors=True - + Raises: TypeError: If input is not an integer ValueError: If input is not positive - + Examples: >>> aliquot_sum(15) 9 @@ -33,23 +34,23 @@ def aliquot_sum( # Validate input type - must be integer if not isinstance(input_num, int): raise TypeError("Input must be an integer") - + # Validate input value - must be positive if input_num <= 0: raise ValueError("Input must be positive integer") - + # Special case: 1 has no proper divisors if input_num == 1: # Return empty factor list if requested return (0, []) if return_factors else 0 - + # Initialize factors list with 1 (always a divisor) - factors = [1] + factors = [1] total = 1 # Start sum with 1 - + # Calculate square root as optimization boundary sqrt_num = int(input_num**0.5) - + # Iterate potential divisors from 2 to square root for divisor in range(2, sqrt_num + 1): # Check if divisor is a factor @@ -57,18 +58,18 @@ def aliquot_sum( # Add divisor to factors list factors.append(divisor) total += divisor - + # Calculate complement (pair factor) complement = input_num // divisor - + # Avoid duplicate for perfect squares - if complement != divisor: + if complement != divisor: factors.append(complement) total += complement - + # Sort factors for consistent output factors.sort() - + # Return based on return_factors flag return (total, factors) if return_factors else total @@ -79,16 +80,16 @@ def classify_number(n: int) -> str: - Perfect: aliquot sum = number - Abundant: aliquot sum > number - Deficient: aliquot sum < number - + Args: n: Positive integer to classify - + Returns: Classification string ("Perfect", "Abundant", or "Deficient") - + Raises: ValueError: If input is not positive - + Examples: >>> classify_number(6) 'Perfect' @@ -100,14 +101,14 @@ def classify_number(n: int) -> str: # Validate input if n <= 0: raise ValueError("Input must be positive integer") - + # Special case: 1 is always deficient if n == 1: return "Deficient" - + # Calculate aliquot sum (using only the integer version) s = aliquot_sum(n) # Default returns only integer - + # Determine classification if s == n: return "Perfect" @@ -116,22 +117,22 @@ def classify_number(n: int) -> str: if __name__ == "__main__": import doctest - + # Run embedded doctests for verification doctest.testmod() - + # Additional demonstration examples print("Aliquot sum of 28:", aliquot_sum(28)) # Perfect number - + # Get factors for 28 with type-safe access factor_result = aliquot_sum(28, return_factors=True) # Since we know return_factors=True returns a tuple, we can safely unpack if isinstance(factor_result, tuple): _, factors = factor_result print("Factors of 28:", factors) - + print("Classification of 28:", classify_number(28)) - + # Large number performance test with targeted exception handling try: print("\nCalculating aliquot sum for 10^9...") From b4b1a81879426fdad0c6dbbc75b39257b0dc0b78 Mon Sep 17 00:00:00 2001 From: lighting9999 Date: Sun, 22 Jun 2025 09:45:31 +0800 Subject: [PATCH 011/107] Update aliquot_sum.py --- maths/aliquot_sum.py | 73 ++++++++++++++++++++++---------------------- 1 file changed, 36 insertions(+), 37 deletions(-) diff --git a/maths/aliquot_sum.py b/maths/aliquot_sum.py index e93d731b8aef..b7ef93d782e8 100644 --- a/maths/aliquot_sum.py +++ b/maths/aliquot_sum.py @@ -1,6 +1,4 @@ from __future__ import annotations # Enable modern type hints -from typing import overload - def aliquot_sum( input_num: int, return_factors: bool = False @@ -8,21 +6,21 @@ def aliquot_sum( """ Calculates the aliquot sum of a positive integer. The aliquot sum is defined as the sum of all proper divisors of a number (all divisors except the number itself). - + This implementation uses an optimized O(sqrt(n)) algorithm for efficiency. - + Args: input_num: Positive integer to calculate aliquot sum for return_factors: If True, returns tuple (aliquot_sum, sorted_factor_list) - + Returns: Aliquot sum if return_factors=False Tuple (aliquot_sum, sorted_factor_list) if return_factors=True - + Raises: TypeError: If input is not an integer ValueError: If input is not positive - + Examples: >>> aliquot_sum(15) 9 @@ -34,23 +32,23 @@ def aliquot_sum( # Validate input type - must be integer if not isinstance(input_num, int): raise TypeError("Input must be an integer") - + # Validate input value - must be positive if input_num <= 0: raise ValueError("Input must be positive integer") - + # Special case: 1 has no proper divisors if input_num == 1: # Return empty factor list if requested return (0, []) if return_factors else 0 - + # Initialize factors list with 1 (always a divisor) - factors = [1] + factors = [1] total = 1 # Start sum with 1 - + # Calculate square root as optimization boundary sqrt_num = int(input_num**0.5) - + # Iterate potential divisors from 2 to square root for divisor in range(2, sqrt_num + 1): # Check if divisor is a factor @@ -58,18 +56,18 @@ def aliquot_sum( # Add divisor to factors list factors.append(divisor) total += divisor - + # Calculate complement (pair factor) complement = input_num // divisor - + # Avoid duplicate for perfect squares - if complement != divisor: + if complement != divisor: factors.append(complement) total += complement - + # Sort factors for consistent output factors.sort() - + # Return based on return_factors flag return (total, factors) if return_factors else total @@ -80,16 +78,16 @@ def classify_number(n: int) -> str: - Perfect: aliquot sum = number - Abundant: aliquot sum > number - Deficient: aliquot sum < number - + Args: n: Positive integer to classify - + Returns: Classification string ("Perfect", "Abundant", or "Deficient") - + Raises: ValueError: If input is not positive - + Examples: >>> classify_number(6) 'Perfect' @@ -101,38 +99,39 @@ def classify_number(n: int) -> str: # Validate input if n <= 0: raise ValueError("Input must be positive integer") - + # Special case: 1 is always deficient if n == 1: return "Deficient" - - # Calculate aliquot sum (using only the integer version) - s = aliquot_sum(n) # Default returns only integer - + + # Explicitly request integer-only aliquot sum + s = aliquot_sum(n, return_factors=False) + # Determine classification if s == n: return "Perfect" - return "Abundant" if s > n else "Deficient" + elif s > n: + return "Abundant" + else: + return "Deficient" if __name__ == "__main__": import doctest - + # Run embedded doctests for verification doctest.testmod() - + # Additional demonstration examples print("Aliquot sum of 28:", aliquot_sum(28)) # Perfect number - + # Get factors for 28 with type-safe access factor_result = aliquot_sum(28, return_factors=True) - # Since we know return_factors=True returns a tuple, we can safely unpack - if isinstance(factor_result, tuple): - _, factors = factor_result - print("Factors of 28:", factors) - + # Since we requested factors, we know it's a tuple + print("Factors of 28:", factor_result[1]) + print("Classification of 28:", classify_number(28)) - + # Large number performance test with targeted exception handling try: print("\nCalculating aliquot sum for 10^9...") From d8d2d8c8f04454b218a3fda36c352b4d97431c8f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 22 Jun 2025 01:45:53 +0000 Subject: [PATCH 012/107] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- maths/aliquot_sum.py | 57 ++++++++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/maths/aliquot_sum.py b/maths/aliquot_sum.py index b7ef93d782e8..418b886609b6 100644 --- a/maths/aliquot_sum.py +++ b/maths/aliquot_sum.py @@ -1,26 +1,27 @@ from __future__ import annotations # Enable modern type hints + def aliquot_sum( input_num: int, return_factors: bool = False ) -> int | tuple[int, list[int]]: """ Calculates the aliquot sum of a positive integer. The aliquot sum is defined as the sum of all proper divisors of a number (all divisors except the number itself). - + This implementation uses an optimized O(sqrt(n)) algorithm for efficiency. - + Args: input_num: Positive integer to calculate aliquot sum for return_factors: If True, returns tuple (aliquot_sum, sorted_factor_list) - + Returns: Aliquot sum if return_factors=False Tuple (aliquot_sum, sorted_factor_list) if return_factors=True - + Raises: TypeError: If input is not an integer ValueError: If input is not positive - + Examples: >>> aliquot_sum(15) 9 @@ -32,23 +33,23 @@ def aliquot_sum( # Validate input type - must be integer if not isinstance(input_num, int): raise TypeError("Input must be an integer") - + # Validate input value - must be positive if input_num <= 0: raise ValueError("Input must be positive integer") - + # Special case: 1 has no proper divisors if input_num == 1: # Return empty factor list if requested return (0, []) if return_factors else 0 - + # Initialize factors list with 1 (always a divisor) - factors = [1] + factors = [1] total = 1 # Start sum with 1 - + # Calculate square root as optimization boundary sqrt_num = int(input_num**0.5) - + # Iterate potential divisors from 2 to square root for divisor in range(2, sqrt_num + 1): # Check if divisor is a factor @@ -56,18 +57,18 @@ def aliquot_sum( # Add divisor to factors list factors.append(divisor) total += divisor - + # Calculate complement (pair factor) complement = input_num // divisor - + # Avoid duplicate for perfect squares - if complement != divisor: + if complement != divisor: factors.append(complement) total += complement - + # Sort factors for consistent output factors.sort() - + # Return based on return_factors flag return (total, factors) if return_factors else total @@ -78,16 +79,16 @@ def classify_number(n: int) -> str: - Perfect: aliquot sum = number - Abundant: aliquot sum > number - Deficient: aliquot sum < number - + Args: n: Positive integer to classify - + Returns: Classification string ("Perfect", "Abundant", or "Deficient") - + Raises: ValueError: If input is not positive - + Examples: >>> classify_number(6) 'Perfect' @@ -99,14 +100,14 @@ def classify_number(n: int) -> str: # Validate input if n <= 0: raise ValueError("Input must be positive integer") - + # Special case: 1 is always deficient if n == 1: return "Deficient" - + # Explicitly request integer-only aliquot sum s = aliquot_sum(n, return_factors=False) - + # Determine classification if s == n: return "Perfect" @@ -118,20 +119,20 @@ def classify_number(n: int) -> str: if __name__ == "__main__": import doctest - + # Run embedded doctests for verification doctest.testmod() - + # Additional demonstration examples print("Aliquot sum of 28:", aliquot_sum(28)) # Perfect number - + # Get factors for 28 with type-safe access factor_result = aliquot_sum(28, return_factors=True) # Since we requested factors, we know it's a tuple print("Factors of 28:", factor_result[1]) - + print("Classification of 28:", classify_number(28)) - + # Large number performance test with targeted exception handling try: print("\nCalculating aliquot sum for 10^9...") From d8b62195537dc7c9e5fd5edb265b74038bdaea8f Mon Sep 17 00:00:00 2001 From: lighting9999 Date: Sun, 22 Jun 2025 09:57:03 +0800 Subject: [PATCH 013/107] Update atbash.py --- ciphers/atbash.py | 66 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 45 insertions(+), 21 deletions(-) diff --git a/ciphers/atbash.py b/ciphers/atbash.py index 4e8f663ed02d..408e1e5f1a1e 100644 --- a/ciphers/atbash.py +++ b/ciphers/atbash.py @@ -1,54 +1,78 @@ """https://en.wikipedia.org/wiki/Atbash""" import string +from timeit import timeit # Moved to top-level as required def atbash_slow(sequence: str) -> str: """ + Atbash cipher implementation using ordinal values. + Encodes/decodes by reversing the alphabet. + >>> atbash_slow("ABCDEFG") 'ZYXWVUT' - + >>> atbash_slow("aW;;123BX") 'zD;;123YC' """ output = "" - for i in sequence: - extract = ord(i) - if 65 <= extract <= 90: - output += chr(155 - extract) - elif 97 <= extract <= 122: - output += chr(219 - extract) - else: - output += i + for char in sequence: + code = ord(char) + if 65 <= code <= 90: # Uppercase A-Z + output += chr(155 - code) + elif 97 <= code <= 122: # Lowercase a-z + output += chr(219 - code) + else: # Non-alphabetic characters + output += char return output def atbash(sequence: str) -> str: """ + Optimized Atbash cipher implementation using string translation. + More efficient than ordinal-based approach. + >>> atbash("ABCDEFG") 'ZYXWVUT' - + >>> atbash("aW;;123BX") 'zD;;123YC' """ + # Create translation tables letters = string.ascii_letters - letters_reversed = string.ascii_lowercase[::-1] + string.ascii_uppercase[::-1] - return "".join( - letters_reversed[letters.index(c)] if c in letters else c for c in sequence - ) + reversed_letters = string.ascii_lowercase[::-1] + string.ascii_uppercase[::-1] + + # Create translation mapping + translation = str.maketrans(letters, reversed_letters) + + # Apply translation to each character + return sequence.translate(translation) def benchmark() -> None: - """Let's benchmark our functions side-by-side...""" - from timeit import timeit - + """ + Performance comparison of both Atbash implementations. + Measures execution time using Python's timeit module. + """ print("Running performance benchmarks...") - setup = "from string import printable ; from __main__ import atbash, atbash_slow" - print(f"> atbash_slow(): {timeit('atbash_slow(printable)', setup=setup)} seconds") - print(f"> atbash(): {timeit('atbash(printable)', setup=setup)} seconds") + setup = ( + "from string import printable; " + "from __main__ import atbash, atbash_slow" + ) + # Time the slow implementation + slow_time = timeit("atbash_slow(printable)", setup=setup) + print(f"> atbash_slow(): {slow_time:.6f} seconds") + + # Time the optimized implementation + fast_time = timeit("atbash(printable)", setup=setup) + print(f"> atbash(): {fast_time:.6f} seconds") if __name__ == "__main__": - for example in ("ABCDEFGH", "123GGjj", "testStringtest", "with space"): + # Test examples + examples = ("ABCDEFGH", "123GGjj", "testStringtest", "with space") + for example in examples: print(f"{example} encrypted in atbash: {atbash(example)}") + + # Run performance comparison benchmark() From 39711d80435330d7a7703493680b2b40f8a40b4e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 22 Jun 2025 01:57:26 +0000 Subject: [PATCH 014/107] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- ciphers/atbash.py | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/ciphers/atbash.py b/ciphers/atbash.py index 408e1e5f1a1e..58561159b4fe 100644 --- a/ciphers/atbash.py +++ b/ciphers/atbash.py @@ -8,10 +8,10 @@ def atbash_slow(sequence: str) -> str: """ Atbash cipher implementation using ordinal values. Encodes/decodes by reversing the alphabet. - + >>> atbash_slow("ABCDEFG") 'ZYXWVUT' - + >>> atbash_slow("aW;;123BX") 'zD;;123YC' """ @@ -31,20 +31,20 @@ def atbash(sequence: str) -> str: """ Optimized Atbash cipher implementation using string translation. More efficient than ordinal-based approach. - + >>> atbash("ABCDEFG") 'ZYXWVUT' - + >>> atbash("aW;;123BX") 'zD;;123YC' """ # Create translation tables letters = string.ascii_letters reversed_letters = string.ascii_lowercase[::-1] + string.ascii_uppercase[::-1] - + # Create translation mapping translation = str.maketrans(letters, reversed_letters) - + # Apply translation to each character return sequence.translate(translation) @@ -55,14 +55,11 @@ def benchmark() -> None: Measures execution time using Python's timeit module. """ print("Running performance benchmarks...") - setup = ( - "from string import printable; " - "from __main__ import atbash, atbash_slow" - ) + setup = "from string import printable; from __main__ import atbash, atbash_slow" # Time the slow implementation slow_time = timeit("atbash_slow(printable)", setup=setup) print(f"> atbash_slow(): {slow_time:.6f} seconds") - + # Time the optimized implementation fast_time = timeit("atbash(printable)", setup=setup) print(f"> atbash(): {fast_time:.6f} seconds") @@ -73,6 +70,6 @@ def benchmark() -> None: examples = ("ABCDEFGH", "123GGjj", "testStringtest", "with space") for example in examples: print(f"{example} encrypted in atbash: {atbash(example)}") - + # Run performance comparison benchmark() From a3756dac00de44ee03e821380e867738493d4c08 Mon Sep 17 00:00:00 2001 From: lighting9999 Date: Sun, 22 Jun 2025 10:07:36 +0800 Subject: [PATCH 015/107] Update hill_cipher.py --- ciphers/hill_cipher.py | 39 +++++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/ciphers/hill_cipher.py b/ciphers/hill_cipher.py index 33b2529f017b..76b511397b58 100644 --- a/ciphers/hill_cipher.py +++ b/ciphers/hill_cipher.py @@ -1,5 +1,4 @@ """ - Hill Cipher: The 'HillCipher' class below implements the Hill Cipher algorithm which uses modern linear algebra techniques to encode and decode text using an encryption @@ -33,13 +32,9 @@ https://apprendre-en-ligne.net/crypto/hill/Hillciph.pdf https://www.youtube.com/watch?v=kfmNeskzs2o https://www.youtube.com/watch?v=4RhLNDqcjpA - """ - import string - import numpy as np - from maths.greatest_common_divisor import greatest_common_divisor @@ -79,7 +74,8 @@ def replace_digits(self, num: int) -> str: >>> hill_cipher.replace_digits(26) '0' """ - return self.key_string[round(num)] + # Directly use integer index without rounding + return self.key_string[num] def check_determinant(self) -> None: """ @@ -107,8 +103,10 @@ def process_text(self, text: str) -> str: >>> hill_cipher.process_text('hello') 'HELLOO' """ + # Filter valid characters and convert to uppercase chars = [char for char in text.upper() if char in self.key_string] + # Pad with last character to make length multiple of break_key last = chars[-1] while len(chars) % self.break_key != 0: chars.append(last) @@ -123,40 +121,49 @@ def encrypt(self, text: str) -> str: >>> hill_cipher.encrypt('hello') '85FF00' """ + # Preprocess text and initialize encrypted string text = self.process_text(text.upper()) encrypted = "" + # Process text in batches of size break_key for i in range(0, len(text) - self.break_key + 1, self.break_key): batch = text[i : i + self.break_key] + # Convert characters to numerical values vec = [self.replace_letters(char) for char in batch] batch_vec = np.array([vec]).T + + # Matrix multiplication with encryption key batch_encrypted = self.modulus(self.encrypt_key.dot(batch_vec)).T.tolist()[ 0 ] + # Convert numerical results back to characters encrypted_batch = "".join( - self.replace_digits(num) for num in batch_encrypted + self.replace_digits(int(round(num))) for num in batch_encrypted ) encrypted += encrypted_batch return encrypted - - def make_decrypt_key(self) -> np.ndarray: + def make_decrypt_key(self) -> np.ndarray: """ >>> hill_cipher = HillCipher(np.array([[2, 5], [1, 6]])) >>> hill_cipher.make_decrypt_key() array([[ 6, 25], [ 5, 26]]) """ + # Calculate determinant of encryption key det = round(np.linalg.det(self.encrypt_key)) if det < 0: det = det % len(self.key_string) det_inv = None + + # Find modular inverse of determinant for i in range(len(self.key_string)): if (det * i) % len(self.key_string) == 1: det_inv = i break + # Calculate inverse key matrix inv_key = ( det_inv * np.linalg.det(self.encrypt_key) * np.linalg.inv(self.encrypt_key) ) @@ -171,24 +178,29 @@ def decrypt(self, text: str) -> str: >>> hill_cipher.decrypt('85FF00') 'HELLOO' """ + # Get decryption key and preprocess text decrypt_key = self.make_decrypt_key() text = self.process_text(text.upper()) decrypted = "" + # Process text in batches of size break_key for i in range(0, len(text) - self.break_key + 1, self.break_key): batch = text[i : i + self.break_key] + # Convert characters to numerical values vec = [self.replace_letters(char) for char in batch] batch_vec = np.array([vec]).T + + # Matrix multiplication with decryption key batch_decrypted = self.modulus(decrypt_key.dot(batch_vec)).T.tolist()[0] + # Convert numerical results back to characters decrypted_batch = "".join( - self.replace_digits(num) for num in batch_decrypted + self.replace_digits(int(round(num))) for num in batch_decrypted ) decrypted += decrypted_batch return decrypted - - -def main() -> None: + def main() -> None: + """Command-line interface for Hill Cipher""" n = int(input("Enter the order of the encryption key: ")) hill_matrix = [] @@ -215,5 +227,4 @@ def main() -> None: import doctest doctest.testmod() - main() From 8663333779c30d86ab56936f2323cbf216175cdc Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 22 Jun 2025 02:07:48 +0000 Subject: [PATCH 016/107] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- ciphers/hill_cipher.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ciphers/hill_cipher.py b/ciphers/hill_cipher.py index 76b511397b58..9c8e81732810 100644 --- a/ciphers/hill_cipher.py +++ b/ciphers/hill_cipher.py @@ -131,7 +131,7 @@ def encrypt(self, text: str) -> str: # Convert characters to numerical values vec = [self.replace_letters(char) for char in batch] batch_vec = np.array([vec]).T - + # Matrix multiplication with encryption key batch_encrypted = self.modulus(self.encrypt_key.dot(batch_vec)).T.tolist()[ 0 @@ -156,7 +156,7 @@ def make_decrypt_key(self) -> np.ndarray: if det < 0: det = det % len(self.key_string) det_inv = None - + # Find modular inverse of determinant for i in range(len(self.key_string)): if (det * i) % len(self.key_string) == 1: @@ -189,7 +189,7 @@ def decrypt(self, text: str) -> str: # Convert characters to numerical values vec = [self.replace_letters(char) for char in batch] batch_vec = np.array([vec]).T - + # Matrix multiplication with decryption key batch_decrypted = self.modulus(decrypt_key.dot(batch_vec)).T.tolist()[0] # Convert numerical results back to characters From 8979274e72d93305c743bd1d9924c45532d844ad Mon Sep 17 00:00:00 2001 From: lighting9999 Date: Sun, 22 Jun 2025 10:14:50 +0800 Subject: [PATCH 017/107] Update hill_cipher.py --- ciphers/hill_cipher.py | 196 +++++++++++------------------------------ 1 file changed, 49 insertions(+), 147 deletions(-) diff --git a/ciphers/hill_cipher.py b/ciphers/hill_cipher.py index 9c8e81732810..9b1d1a96061c 100644 --- a/ciphers/hill_cipher.py +++ b/ciphers/hill_cipher.py @@ -37,194 +37,96 @@ import numpy as np from maths.greatest_common_divisor import greatest_common_divisor - class HillCipher: - key_string = string.ascii_uppercase + string.digits - # This cipher takes alphanumerics into account - # i.e. a total of 36 characters - - # take x and return x % len(key_string) - modulus = np.vectorize(lambda x: x % 36) - - to_int = np.vectorize(round) + key_string = string.ascii_uppercase + string.digits # 36 alphanumeric chars + modulus = np.vectorize(lambda x: x % 36) # Mod 36 operation + to_int = np.vectorize(round) # Round to nearest integer def __init__(self, encrypt_key: np.ndarray) -> None: - """ - encrypt_key is an NxN numpy array - """ - self.encrypt_key = self.modulus(encrypt_key) # mod36 calc's on the encrypt key - self.check_determinant() # validate the determinant of the encryption key - self.break_key = encrypt_key.shape[0] + self.encrypt_key = self.modulus(encrypt_key) + self.check_determinant() # Validate key determinant + self.break_key = encrypt_key.shape[0] # Matrix order def replace_letters(self, letter: str) -> int: - """ - >>> hill_cipher = HillCipher(np.array([[2, 5], [1, 6]])) - >>> hill_cipher.replace_letters('T') - 19 - >>> hill_cipher.replace_letters('0') - 26 - """ + """Map char to index (A=0, Z=25, 0=26, 9=35)""" return self.key_string.index(letter) def replace_digits(self, num: int) -> str: - """ - >>> hill_cipher = HillCipher(np.array([[2, 5], [1, 6]])) - >>> hill_cipher.replace_digits(19) - 'T' - >>> hill_cipher.replace_digits(26) - '0' - """ - # Directly use integer index without rounding + """Map index back to char""" return self.key_string[num] def check_determinant(self) -> None: - """ - >>> hill_cipher = HillCipher(np.array([[2, 5], [1, 6]])) - >>> hill_cipher.check_determinant() - """ + """Ensure det(key) is coprime with 36""" det = round(np.linalg.det(self.encrypt_key)) - if det < 0: - det = det % len(self.key_string) - - req_l = len(self.key_string) + det %= len(self.key_string) + if greatest_common_divisor(det, len(self.key_string)) != 1: - 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) + raise ValueError(f"Det {det} not coprime with 36. Try another key.") def process_text(self, text: str) -> str: - """ - >>> hill_cipher = HillCipher(np.array([[2, 5], [1, 6]])) - >>> hill_cipher.process_text('Testing Hill Cipher') - 'TESTINGHILLCIPHERR' - >>> hill_cipher.process_text('hello') - 'HELLOO' - """ - # Filter valid characters and convert to uppercase - chars = [char for char in text.upper() if char in self.key_string] - - # Pad with last character to make length multiple of break_key - last = chars[-1] + """Convert to uppercase, remove invalid chars, pad to multiple of break_key""" + chars = [c for c in text.upper() if c in self.key_string] + last = chars[-1] if chars else 'A' while len(chars) % self.break_key != 0: chars.append(last) - return "".join(chars) def encrypt(self, text: str) -> str: - """ - >>> hill_cipher = HillCipher(np.array([[2, 5], [1, 6]])) - >>> hill_cipher.encrypt('testing hill cipher') - 'WHXYJOLM9C6XT085LL' - >>> hill_cipher.encrypt('hello') - '85FF00' - """ - # Preprocess text and initialize encrypted string + """Encrypt text using Hill cipher""" text = self.process_text(text.upper()) encrypted = "" - - # Process text in batches of size break_key - for i in range(0, len(text) - self.break_key + 1, self.break_key): - batch = text[i : i + self.break_key] - # Convert characters to numerical values - vec = [self.replace_letters(char) for char in batch] + for i in range(0, len(text), self.break_key): + batch = text[i:i+self.break_key] + vec = [self.replace_letters(c) for c in batch] batch_vec = np.array([vec]).T - - # Matrix multiplication with encryption key - batch_encrypted = self.modulus(self.encrypt_key.dot(batch_vec)).T.tolist()[ - 0 - ] - # Convert numerical results back to characters - encrypted_batch = "".join( - self.replace_digits(int(round(num))) for num in batch_encrypted - ) - encrypted += encrypted_batch - + batch_encrypted = self.modulus(self.encrypt_key.dot(batch_vec)).T.tolist()[0] + encrypted += "".join(self.replace_digits(int(round(n))) for n in batch_encrypted) return encrypted - def make_decrypt_key(self) -> np.ndarray: - """ - >>> hill_cipher = HillCipher(np.array([[2, 5], [1, 6]])) - >>> hill_cipher.make_decrypt_key() - array([[ 6, 25], - [ 5, 26]]) - """ - # Calculate determinant of encryption key - det = round(np.linalg.det(self.encrypt_key)) + def make_decrypt_key(self) -> np.ndarray: + """Calculate decryption key matrix""" + det = round(np.linalg.det(self.encrypt_key)) if det < 0: - det = det % len(self.key_string) - det_inv = None - - # Find modular inverse of determinant - for i in range(len(self.key_string)): - if (det * i) % len(self.key_string) == 1: - det_inv = i - break - - # Calculate inverse key matrix - inv_key = ( - det_inv * np.linalg.det(self.encrypt_key) * np.linalg.inv(self.encrypt_key) - ) - + det %= len(self.key_string) + + # Find modular inverse of det + det_inv = next(i for i in range(36) if (det * i) % 36 == 1) + + # Calculate inverse key + inv_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(np.array([[2, 5], [1, 6]])) - >>> hill_cipher.decrypt('WHXYJOLM9C6XT085LL') - 'TESTINGHILLCIPHERR' - >>> hill_cipher.decrypt('85FF00') - 'HELLOO' - """ - # Get decryption key and preprocess text + """Decrypt text using Hill cipher""" decrypt_key = self.make_decrypt_key() text = self.process_text(text.upper()) decrypted = "" - - # Process text in batches of size break_key - for i in range(0, len(text) - self.break_key + 1, self.break_key): - batch = text[i : i + self.break_key] - # Convert characters to numerical values - vec = [self.replace_letters(char) for char in batch] + for i in range(0, len(text), self.break_key): + batch = text[i:i+self.break_key] + vec = [self.replace_letters(c) for c in batch] batch_vec = np.array([vec]).T - - # Matrix multiplication with decryption key batch_decrypted = self.modulus(decrypt_key.dot(batch_vec)).T.tolist()[0] - # Convert numerical results back to characters - decrypted_batch = "".join( - self.replace_digits(int(round(num))) for num in batch_decrypted - ) - decrypted += decrypted_batch - + decrypted += "".join(self.replace_digits(int(round(n))) for n in batch_decrypted) return decrypted - def main() -> None: - """Command-line interface for Hill Cipher""" - n = int(input("Enter the order of the encryption key: ")) - hill_matrix = [] - - print("Enter each row of the encryption key with space separated integers") - for _ in range(n): - row = [int(x) for x in input().split()] - hill_matrix.append(row) - - 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") +def main() -> None: + """CLI for Hill Cipher""" + n = int(input("Enter key order: ")) + print(f"Enter {n} rows of space-separated integers:") + matrix = [list(map(int, input().split())) for _ in range(n)] + + hc = HillCipher(np.array(matrix)) + + option = input("1. Encrypt\n2. Decrypt\nChoose: ") + text = input("Enter text: ") + if option == "1": - text_e = input("What text would you like to encrypt?: ") - print("Your encrypted text is:") - print(hc.encrypt(text_e)) + print("Encrypted:", hc.encrypt(text)) elif option == "2": - text_d = input("What text would you like to decrypt?: ") - print("Your decrypted text is:") - print(hc.decrypt(text_d)) - + print("Decrypted:", hc.decrypt(text)) if __name__ == "__main__": import doctest - doctest.testmod() main() From 6e993194539759c9012de3cb89c871e5b503de1e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 22 Jun 2025 02:15:13 +0000 Subject: [PATCH 018/107] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- ciphers/hill_cipher.py | 39 ++++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/ciphers/hill_cipher.py b/ciphers/hill_cipher.py index 9b1d1a96061c..4ec263dea611 100644 --- a/ciphers/hill_cipher.py +++ b/ciphers/hill_cipher.py @@ -33,10 +33,12 @@ https://www.youtube.com/watch?v=kfmNeskzs2o https://www.youtube.com/watch?v=4RhLNDqcjpA """ + import string import numpy as np from maths.greatest_common_divisor import greatest_common_divisor + class HillCipher: key_string = string.ascii_uppercase + string.digits # 36 alphanumeric chars modulus = np.vectorize(lambda x: x % 36) # Mod 36 operation @@ -60,14 +62,14 @@ def check_determinant(self) -> None: det = round(np.linalg.det(self.encrypt_key)) if det < 0: det %= len(self.key_string) - + if greatest_common_divisor(det, len(self.key_string)) != 1: raise ValueError(f"Det {det} not coprime with 36. Try another key.") def process_text(self, text: str) -> str: """Convert to uppercase, remove invalid chars, pad to multiple of break_key""" chars = [c for c in text.upper() if c in self.key_string] - last = chars[-1] if chars else 'A' + last = chars[-1] if chars else "A" while len(chars) % self.break_key != 0: chars.append(last) return "".join(chars) @@ -77,11 +79,15 @@ def encrypt(self, text: str) -> str: text = self.process_text(text.upper()) encrypted = "" for i in range(0, len(text), self.break_key): - batch = text[i:i+self.break_key] + batch = text[i : i + self.break_key] vec = [self.replace_letters(c) for c in batch] batch_vec = np.array([vec]).T - batch_encrypted = self.modulus(self.encrypt_key.dot(batch_vec)).T.tolist()[0] - encrypted += "".join(self.replace_digits(int(round(n))) for n in batch_encrypted) + batch_encrypted = self.modulus(self.encrypt_key.dot(batch_vec)).T.tolist()[ + 0 + ] + encrypted += "".join( + self.replace_digits(int(round(n))) for n in batch_encrypted + ) return encrypted def make_decrypt_key(self) -> np.ndarray: @@ -89,12 +95,14 @@ def make_decrypt_key(self) -> np.ndarray: det = round(np.linalg.det(self.encrypt_key)) if det < 0: det %= len(self.key_string) - + # Find modular inverse of det det_inv = next(i for i in range(36) if (det * i) % 36 == 1) - + # Calculate inverse key - inv_key = det_inv * np.linalg.det(self.encrypt_key) * np.linalg.inv(self.encrypt_key) + inv_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: @@ -103,30 +111,35 @@ def decrypt(self, text: str) -> str: text = self.process_text(text.upper()) decrypted = "" for i in range(0, len(text), self.break_key): - batch = text[i:i+self.break_key] + batch = text[i : i + self.break_key] vec = [self.replace_letters(c) for c in batch] batch_vec = np.array([vec]).T batch_decrypted = self.modulus(decrypt_key.dot(batch_vec)).T.tolist()[0] - decrypted += "".join(self.replace_digits(int(round(n))) for n in batch_decrypted) + decrypted += "".join( + self.replace_digits(int(round(n))) for n in batch_decrypted + ) return decrypted + def main() -> None: """CLI for Hill Cipher""" n = int(input("Enter key order: ")) print(f"Enter {n} rows of space-separated integers:") matrix = [list(map(int, input().split())) for _ in range(n)] - + hc = HillCipher(np.array(matrix)) - + option = input("1. Encrypt\n2. Decrypt\nChoose: ") text = input("Enter text: ") - + if option == "1": print("Encrypted:", hc.encrypt(text)) elif option == "2": print("Decrypted:", hc.decrypt(text)) + if __name__ == "__main__": import doctest + doctest.testmod() main() From c8755d643ef77719abb98dc2bdf873fa4d0c15db Mon Sep 17 00:00:00 2001 From: lighting9999 Date: Sun, 22 Jun 2025 10:19:08 +0800 Subject: [PATCH 019/107] Update hill_cipher.py --- ciphers/hill_cipher.py | 42 +++++++++++++++--------------------------- 1 file changed, 15 insertions(+), 27 deletions(-) diff --git a/ciphers/hill_cipher.py b/ciphers/hill_cipher.py index 4ec263dea611..2dfca11a96b0 100644 --- a/ciphers/hill_cipher.py +++ b/ciphers/hill_cipher.py @@ -33,12 +33,10 @@ https://www.youtube.com/watch?v=kfmNeskzs2o https://www.youtube.com/watch?v=4RhLNDqcjpA """ - import string import numpy as np from maths.greatest_common_divisor import greatest_common_divisor - class HillCipher: key_string = string.ascii_uppercase + string.digits # 36 alphanumeric chars modulus = np.vectorize(lambda x: x % 36) # Mod 36 operation @@ -62,14 +60,15 @@ def check_determinant(self) -> None: det = round(np.linalg.det(self.encrypt_key)) if det < 0: det %= len(self.key_string) - + + error_msg = f"Det {det} not coprime with 36. Try another key." if greatest_common_divisor(det, len(self.key_string)) != 1: - raise ValueError(f"Det {det} not coprime with 36. Try another key.") + raise ValueError(error_msg) def process_text(self, text: str) -> str: """Convert to uppercase, remove invalid chars, pad to multiple of break_key""" chars = [c for c in text.upper() if c in self.key_string] - last = chars[-1] if chars else "A" + last = chars[-1] if chars else 'A' while len(chars) % self.break_key != 0: chars.append(last) return "".join(chars) @@ -79,15 +78,11 @@ def encrypt(self, text: str) -> str: text = self.process_text(text.upper()) encrypted = "" for i in range(0, len(text), self.break_key): - batch = text[i : i + self.break_key] + batch = text[i:i+self.break_key] vec = [self.replace_letters(c) for c in batch] batch_vec = np.array([vec]).T - batch_encrypted = self.modulus(self.encrypt_key.dot(batch_vec)).T.tolist()[ - 0 - ] - encrypted += "".join( - self.replace_digits(int(round(n))) for n in batch_encrypted - ) + batch_encrypted = self.modulus(self.encrypt_key.dot(batch_vec)).T.tolist()[0] + encrypted += "".join(self.replace_digits(round(n)) for n in batch_encrypted) return encrypted def make_decrypt_key(self) -> np.ndarray: @@ -95,14 +90,12 @@ def make_decrypt_key(self) -> np.ndarray: det = round(np.linalg.det(self.encrypt_key)) if det < 0: det %= len(self.key_string) - + # Find modular inverse of det det_inv = next(i for i in range(36) if (det * i) % 36 == 1) - + # Calculate inverse key - inv_key = ( - det_inv * np.linalg.det(self.encrypt_key) * np.linalg.inv(self.encrypt_key) - ) + inv_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: @@ -111,35 +104,30 @@ def decrypt(self, text: str) -> str: text = self.process_text(text.upper()) decrypted = "" for i in range(0, len(text), self.break_key): - batch = text[i : i + self.break_key] + batch = text[i:i+self.break_key] vec = [self.replace_letters(c) for c in batch] batch_vec = np.array([vec]).T batch_decrypted = self.modulus(decrypt_key.dot(batch_vec)).T.tolist()[0] - decrypted += "".join( - self.replace_digits(int(round(n))) for n in batch_decrypted - ) + decrypted += "".join(self.replace_digits(round(n)) for n in batch_decrypted) return decrypted - def main() -> None: """CLI for Hill Cipher""" n = int(input("Enter key order: ")) print(f"Enter {n} rows of space-separated integers:") matrix = [list(map(int, input().split())) for _ in range(n)] - + hc = HillCipher(np.array(matrix)) - + option = input("1. Encrypt\n2. Decrypt\nChoose: ") text = input("Enter text: ") - + if option == "1": print("Encrypted:", hc.encrypt(text)) elif option == "2": print("Decrypted:", hc.decrypt(text)) - if __name__ == "__main__": import doctest - doctest.testmod() main() From e60ccc9dc1aa737aca8308b730249958cdf81046 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 22 Jun 2025 02:19:31 +0000 Subject: [PATCH 020/107] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- ciphers/hill_cipher.py | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/ciphers/hill_cipher.py b/ciphers/hill_cipher.py index 2dfca11a96b0..39aaf811d42a 100644 --- a/ciphers/hill_cipher.py +++ b/ciphers/hill_cipher.py @@ -33,10 +33,12 @@ https://www.youtube.com/watch?v=kfmNeskzs2o https://www.youtube.com/watch?v=4RhLNDqcjpA """ + import string import numpy as np from maths.greatest_common_divisor import greatest_common_divisor + class HillCipher: key_string = string.ascii_uppercase + string.digits # 36 alphanumeric chars modulus = np.vectorize(lambda x: x % 36) # Mod 36 operation @@ -60,7 +62,7 @@ def check_determinant(self) -> None: det = round(np.linalg.det(self.encrypt_key)) if det < 0: det %= len(self.key_string) - + error_msg = f"Det {det} not coprime with 36. Try another key." if greatest_common_divisor(det, len(self.key_string)) != 1: raise ValueError(error_msg) @@ -68,7 +70,7 @@ def check_determinant(self) -> None: def process_text(self, text: str) -> str: """Convert to uppercase, remove invalid chars, pad to multiple of break_key""" chars = [c for c in text.upper() if c in self.key_string] - last = chars[-1] if chars else 'A' + last = chars[-1] if chars else "A" while len(chars) % self.break_key != 0: chars.append(last) return "".join(chars) @@ -78,10 +80,12 @@ def encrypt(self, text: str) -> str: text = self.process_text(text.upper()) encrypted = "" for i in range(0, len(text), self.break_key): - batch = text[i:i+self.break_key] + batch = text[i : i + self.break_key] vec = [self.replace_letters(c) for c in batch] batch_vec = np.array([vec]).T - batch_encrypted = self.modulus(self.encrypt_key.dot(batch_vec)).T.tolist()[0] + batch_encrypted = self.modulus(self.encrypt_key.dot(batch_vec)).T.tolist()[ + 0 + ] encrypted += "".join(self.replace_digits(round(n)) for n in batch_encrypted) return encrypted @@ -90,12 +94,14 @@ def make_decrypt_key(self) -> np.ndarray: det = round(np.linalg.det(self.encrypt_key)) if det < 0: det %= len(self.key_string) - + # Find modular inverse of det det_inv = next(i for i in range(36) if (det * i) % 36 == 1) - + # Calculate inverse key - inv_key = det_inv * np.linalg.det(self.encrypt_key) * np.linalg.inv(self.encrypt_key) + inv_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: @@ -104,30 +110,33 @@ def decrypt(self, text: str) -> str: text = self.process_text(text.upper()) decrypted = "" for i in range(0, len(text), self.break_key): - batch = text[i:i+self.break_key] + batch = text[i : i + self.break_key] vec = [self.replace_letters(c) for c in batch] batch_vec = np.array([vec]).T batch_decrypted = self.modulus(decrypt_key.dot(batch_vec)).T.tolist()[0] decrypted += "".join(self.replace_digits(round(n)) for n in batch_decrypted) return decrypted + def main() -> None: """CLI for Hill Cipher""" n = int(input("Enter key order: ")) print(f"Enter {n} rows of space-separated integers:") matrix = [list(map(int, input().split())) for _ in range(n)] - + hc = HillCipher(np.array(matrix)) - + option = input("1. Encrypt\n2. Decrypt\nChoose: ") text = input("Enter text: ") - + if option == "1": print("Encrypted:", hc.encrypt(text)) elif option == "2": print("Decrypted:", hc.decrypt(text)) + if __name__ == "__main__": import doctest + doctest.testmod() main() From 1655a82949583278ff6eaccd547527cb90c78aad Mon Sep 17 00:00:00 2001 From: lighting9999 Date: Sun, 22 Jun 2025 10:22:31 +0800 Subject: [PATCH 021/107] Update hill_cipher.py --- ciphers/hill_cipher.py | 71 +++++++++--------------------------------- 1 file changed, 14 insertions(+), 57 deletions(-) diff --git a/ciphers/hill_cipher.py b/ciphers/hill_cipher.py index 39aaf811d42a..ee353686dd1e 100644 --- a/ciphers/hill_cipher.py +++ b/ciphers/hill_cipher.py @@ -1,44 +1,8 @@ -""" -Hill Cipher: -The 'HillCipher' class below implements the Hill Cipher algorithm which uses -modern linear algebra techniques to encode and decode text using an encryption -key matrix. - -Algorithm: -Let the order of the encryption key be N (as it is a square matrix). -Your text is divided into batches of length N and converted to numerical vectors -by a simple mapping starting with A=0 and so on. - -The key is then multiplied with the newly created batch vector to obtain the -encoded vector. After each multiplication modular 36 calculations are performed -on the vectors so as to bring the numbers between 0 and 36 and then mapped with -their corresponding alphanumerics. - -While decrypting, the decrypting key is found which is the inverse of the -encrypting key modular 36. The same process is repeated for decrypting to get -the original message back. - -Constraints: -The determinant of the encryption key matrix must be relatively prime w.r.t 36. - -Note: -This implementation only considers alphanumerics in the text. If the length of -the text to be encrypted is not a multiple of the break key(the length of one -batch of letters), the last character of the text is added to the text until the -length of the text reaches a multiple of the break_key. So the text after -decrypting might be a little different than the original text. - -References: -https://apprendre-en-ligne.net/crypto/hill/Hillciph.pdf -https://www.youtube.com/watch?v=kfmNeskzs2o -https://www.youtube.com/watch?v=4RhLNDqcjpA -""" - import string + import numpy as np from maths.greatest_common_divisor import greatest_common_divisor - class HillCipher: key_string = string.ascii_uppercase + string.digits # 36 alphanumeric chars modulus = np.vectorize(lambda x: x % 36) # Mod 36 operation @@ -62,7 +26,7 @@ def check_determinant(self) -> None: det = round(np.linalg.det(self.encrypt_key)) if det < 0: det %= len(self.key_string) - + error_msg = f"Det {det} not coprime with 36. Try another key." if greatest_common_divisor(det, len(self.key_string)) != 1: raise ValueError(error_msg) @@ -70,7 +34,7 @@ def check_determinant(self) -> None: def process_text(self, text: str) -> str: """Convert to uppercase, remove invalid chars, pad to multiple of break_key""" chars = [c for c in text.upper() if c in self.key_string] - last = chars[-1] if chars else "A" + last = chars[-1] if chars else 'A' while len(chars) % self.break_key != 0: chars.append(last) return "".join(chars) @@ -80,13 +44,11 @@ def encrypt(self, text: str) -> str: text = self.process_text(text.upper()) encrypted = "" for i in range(0, len(text), self.break_key): - batch = text[i : i + self.break_key] + batch = text[i:i+self.break_key] vec = [self.replace_letters(c) for c in batch] batch_vec = np.array([vec]).T - batch_encrypted = self.modulus(self.encrypt_key.dot(batch_vec)).T.tolist()[ - 0 - ] - encrypted += "".join(self.replace_digits(round(n)) for n in batch_encrypted) + batch_encrypted = self.modulus(self.encrypt_key.dot(batch_vec)).T.tolist()[0] + encrypted += "".join(self.replace_digits(round(n)) for n in batch_encrypted return encrypted def make_decrypt_key(self) -> np.ndarray: @@ -94,14 +56,12 @@ def make_decrypt_key(self) -> np.ndarray: det = round(np.linalg.det(self.encrypt_key)) if det < 0: det %= len(self.key_string) - + # Find modular inverse of det det_inv = next(i for i in range(36) if (det * i) % 36 == 1) - + # Calculate inverse key - inv_key = ( - det_inv * np.linalg.det(self.encrypt_key) * np.linalg.inv(self.encrypt_key) - ) + inv_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: @@ -110,33 +70,30 @@ def decrypt(self, text: str) -> str: text = self.process_text(text.upper()) decrypted = "" for i in range(0, len(text), self.break_key): - batch = text[i : i + self.break_key] + batch = text[i:i+self.break_key] vec = [self.replace_letters(c) for c in batch] batch_vec = np.array([vec]).T batch_decrypted = self.modulus(decrypt_key.dot(batch_vec)).T.tolist()[0] - decrypted += "".join(self.replace_digits(round(n)) for n in batch_decrypted) + decrypted += "".join(self.replace_digits(round(n)) for n in batch_decrypted return decrypted - def main() -> None: """CLI for Hill Cipher""" n = int(input("Enter key order: ")) print(f"Enter {n} rows of space-separated integers:") matrix = [list(map(int, input().split())) for _ in range(n)] - + hc = HillCipher(np.array(matrix)) - + option = input("1. Encrypt\n2. Decrypt\nChoose: ") text = input("Enter text: ") - + if option == "1": print("Encrypted:", hc.encrypt(text)) elif option == "2": print("Decrypted:", hc.decrypt(text)) - if __name__ == "__main__": import doctest - doctest.testmod() main() From d060dda897e8cc8d1cab46f2c6218b42b7a5cd32 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 22 Jun 2025 02:22:45 +0000 Subject: [PATCH 022/107] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- ciphers/hill_cipher.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ciphers/hill_cipher.py b/ciphers/hill_cipher.py index ee353686dd1e..94ad8f6d6f26 100644 --- a/ciphers/hill_cipher.py +++ b/ciphers/hill_cipher.py @@ -26,7 +26,7 @@ def check_determinant(self) -> None: det = round(np.linalg.det(self.encrypt_key)) if det < 0: det %= len(self.key_string) - + error_msg = f"Det {det} not coprime with 36. Try another key." if greatest_common_divisor(det, len(self.key_string)) != 1: raise ValueError(error_msg) @@ -56,10 +56,10 @@ def make_decrypt_key(self) -> np.ndarray: det = round(np.linalg.det(self.encrypt_key)) if det < 0: det %= len(self.key_string) - + # Find modular inverse of det det_inv = next(i for i in range(36) if (det * i) % 36 == 1) - + # Calculate inverse key inv_key = det_inv * np.linalg.det(self.encrypt_key) * np.linalg.inv(self.encrypt_key) return self.to_int(self.modulus(inv_key)) @@ -82,12 +82,12 @@ def main() -> None: n = int(input("Enter key order: ")) print(f"Enter {n} rows of space-separated integers:") matrix = [list(map(int, input().split())) for _ in range(n)] - + hc = HillCipher(np.array(matrix)) - + option = input("1. Encrypt\n2. Decrypt\nChoose: ") text = input("Enter text: ") - + if option == "1": print("Encrypted:", hc.encrypt(text)) elif option == "2": From e09fa4bae8a4da599100f3adea574742db7adf33 Mon Sep 17 00:00:00 2001 From: lighting9999 Date: Sun, 22 Jun 2025 10:25:18 +0800 Subject: [PATCH 023/107] Update hill_cipher.py --- ciphers/hill_cipher.py | 66 +++++++++++++++++++++++++----------------- 1 file changed, 40 insertions(+), 26 deletions(-) diff --git a/ciphers/hill_cipher.py b/ciphers/hill_cipher.py index 94ad8f6d6f26..f14bd266f49e 100644 --- a/ciphers/hill_cipher.py +++ b/ciphers/hill_cipher.py @@ -4,35 +4,35 @@ from maths.greatest_common_divisor import greatest_common_divisor class HillCipher: - key_string = string.ascii_uppercase + string.digits # 36 alphanumeric chars - modulus = np.vectorize(lambda x: x % 36) # Mod 36 operation - to_int = np.vectorize(round) # Round to nearest integer + key_string = string.ascii_uppercase + string.digits # 36 chars + modulus = np.vectorize(lambda x: x % 36) # Mod 36 + to_int = np.vectorize(round) # Round numbers def __init__(self, encrypt_key: np.ndarray) -> None: self.encrypt_key = self.modulus(encrypt_key) - self.check_determinant() # Validate key determinant - self.break_key = encrypt_key.shape[0] # Matrix order + self.check_determinant() # Validate key + self.break_key = encrypt_key.shape[0] # Matrix size def replace_letters(self, letter: str) -> int: - """Map char to index (A=0, Z=25, 0=26, 9=35)""" + """Char to index (A=0, 0=26)""" return self.key_string.index(letter) def replace_digits(self, num: int) -> str: - """Map index back to char""" + """Index to char""" return self.key_string[num] def check_determinant(self) -> None: - """Ensure det(key) is coprime with 36""" + """Ensure det(key) coprime with 36""" det = round(np.linalg.det(self.encrypt_key)) if det < 0: det %= len(self.key_string) - + error_msg = f"Det {det} not coprime with 36. Try another key." if greatest_common_divisor(det, len(self.key_string)) != 1: raise ValueError(error_msg) def process_text(self, text: str) -> str: - """Convert to uppercase, remove invalid chars, pad to multiple of break_key""" + """Uppercase, filter, pad text""" chars = [c for c in text.upper() if c in self.key_string] last = chars[-1] if chars else 'A' while len(chars) % self.break_key != 0: @@ -40,32 +40,41 @@ def process_text(self, text: str) -> str: return "".join(chars) def encrypt(self, text: str) -> str: - """Encrypt text using Hill cipher""" + """Encrypt with Hill cipher""" text = self.process_text(text.upper()) encrypted = "" for i in range(0, len(text), self.break_key): batch = text[i:i+self.break_key] vec = [self.replace_letters(c) for c in batch] batch_vec = np.array([vec]).T - batch_encrypted = self.modulus(self.encrypt_key.dot(batch_vec)).T.tolist()[0] - encrypted += "".join(self.replace_digits(round(n)) for n in batch_encrypted + product = self.encrypt_key.dot(batch_vec) + modulated = self.modulus(product) + batch_encrypted = modulated.T.tolist()[0] + encrypted_batch = "".join( + self.replace_digits(round(n)) for n in batch_encrypted + ) + encrypted += encrypted_batch return encrypted def make_decrypt_key(self) -> np.ndarray: - """Calculate decryption key matrix""" + """Create decryption key""" det = round(np.linalg.det(self.encrypt_key)) if det < 0: det %= len(self.key_string) - - # Find modular inverse of det + + # Find det modular inverse det_inv = next(i for i in range(36) if (det * i) % 36 == 1) - - # Calculate inverse key - inv_key = det_inv * np.linalg.det(self.encrypt_key) * np.linalg.inv(self.encrypt_key) + + # Compute inverse key + inv_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: - """Decrypt text using Hill cipher""" + """Decrypt with Hill cipher""" decrypt_key = self.make_decrypt_key() text = self.process_text(text.upper()) decrypted = "" @@ -73,21 +82,26 @@ def decrypt(self, text: str) -> str: batch = text[i:i+self.break_key] vec = [self.replace_letters(c) for c in batch] batch_vec = np.array([vec]).T - batch_decrypted = self.modulus(decrypt_key.dot(batch_vec)).T.tolist()[0] - decrypted += "".join(self.replace_digits(round(n)) for n in batch_decrypted + product = decrypt_key.dot(batch_vec) + modulated = self.modulus(product) + batch_decrypted = modulated.T.tolist()[0] + decrypted_batch = "".join( + self.replace_digits(round(n)) for n in batch_decrypted + ) + decrypted += decrypted_batch return decrypted def main() -> None: - """CLI for Hill Cipher""" + """Hill Cipher CLI""" n = int(input("Enter key order: ")) print(f"Enter {n} rows of space-separated integers:") matrix = [list(map(int, input().split())) for _ in range(n)] - + hc = HillCipher(np.array(matrix)) - + option = input("1. Encrypt\n2. Decrypt\nChoose: ") text = input("Enter text: ") - + if option == "1": print("Encrypted:", hc.encrypt(text)) elif option == "2": From dd469e45ba3654d30335b4010f9668e8ce88aeaf Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 22 Jun 2025 02:25:40 +0000 Subject: [PATCH 024/107] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- ciphers/hill_cipher.py | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/ciphers/hill_cipher.py b/ciphers/hill_cipher.py index f14bd266f49e..8be5d72e83c1 100644 --- a/ciphers/hill_cipher.py +++ b/ciphers/hill_cipher.py @@ -3,6 +3,7 @@ import numpy as np from maths.greatest_common_divisor import greatest_common_divisor + class HillCipher: key_string = string.ascii_uppercase + string.digits # 36 chars modulus = np.vectorize(lambda x: x % 36) # Mod 36 @@ -26,7 +27,7 @@ def check_determinant(self) -> None: det = round(np.linalg.det(self.encrypt_key)) if det < 0: det %= len(self.key_string) - + error_msg = f"Det {det} not coprime with 36. Try another key." if greatest_common_divisor(det, len(self.key_string)) != 1: raise ValueError(error_msg) @@ -34,7 +35,7 @@ def check_determinant(self) -> None: def process_text(self, text: str) -> str: """Uppercase, filter, pad text""" chars = [c for c in text.upper() if c in self.key_string] - last = chars[-1] if chars else 'A' + last = chars[-1] if chars else "A" while len(chars) % self.break_key != 0: chars.append(last) return "".join(chars) @@ -44,7 +45,7 @@ def encrypt(self, text: str) -> str: text = self.process_text(text.upper()) encrypted = "" for i in range(0, len(text), self.break_key): - batch = text[i:i+self.break_key] + batch = text[i : i + self.break_key] vec = [self.replace_letters(c) for c in batch] batch_vec = np.array([vec]).T product = self.encrypt_key.dot(batch_vec) @@ -61,15 +62,13 @@ def make_decrypt_key(self) -> np.ndarray: det = round(np.linalg.det(self.encrypt_key)) if det < 0: det %= len(self.key_string) - + # Find det modular inverse det_inv = next(i for i in range(36) if (det * i) % 36 == 1) - + # Compute inverse key inv_key = ( - det_inv * - np.linalg.det(self.encrypt_key) * - np.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)) @@ -79,7 +78,7 @@ def decrypt(self, text: str) -> str: text = self.process_text(text.upper()) decrypted = "" for i in range(0, len(text), self.break_key): - batch = text[i:i+self.break_key] + batch = text[i : i + self.break_key] vec = [self.replace_letters(c) for c in batch] batch_vec = np.array([vec]).T product = decrypt_key.dot(batch_vec) @@ -91,23 +90,26 @@ def decrypt(self, text: str) -> str: decrypted += decrypted_batch return decrypted + def main() -> None: """Hill Cipher CLI""" n = int(input("Enter key order: ")) print(f"Enter {n} rows of space-separated integers:") matrix = [list(map(int, input().split())) for _ in range(n)] - + hc = HillCipher(np.array(matrix)) - + option = input("1. Encrypt\n2. Decrypt\nChoose: ") text = input("Enter text: ") - + if option == "1": print("Encrypted:", hc.encrypt(text)) elif option == "2": print("Decrypted:", hc.decrypt(text)) + if __name__ == "__main__": import doctest + doctest.testmod() main() From aff990af0ee301d934f98d57bb4ead50a3011223 Mon Sep 17 00:00:00 2001 From: lighting9999 Date: Sun, 22 Jun 2025 10:28:08 +0800 Subject: [PATCH 025/107] Update hill_cipher.py --- ciphers/hill_cipher.py | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/ciphers/hill_cipher.py b/ciphers/hill_cipher.py index 8be5d72e83c1..5f45b50d19fb 100644 --- a/ciphers/hill_cipher.py +++ b/ciphers/hill_cipher.py @@ -1,8 +1,8 @@ import string import numpy as np -from maths.greatest_common_divisor import greatest_common_divisor +from maths.greatest_common_divisor import greatest_common_divisor class HillCipher: key_string = string.ascii_uppercase + string.digits # 36 chars @@ -27,7 +27,7 @@ def check_determinant(self) -> None: det = round(np.linalg.det(self.encrypt_key)) if det < 0: det %= len(self.key_string) - + error_msg = f"Det {det} not coprime with 36. Try another key." if greatest_common_divisor(det, len(self.key_string)) != 1: raise ValueError(error_msg) @@ -35,7 +35,7 @@ def check_determinant(self) -> None: def process_text(self, text: str) -> str: """Uppercase, filter, pad text""" chars = [c for c in text.upper() if c in self.key_string] - last = chars[-1] if chars else "A" + last = chars[-1] if chars else 'A' while len(chars) % self.break_key != 0: chars.append(last) return "".join(chars) @@ -45,7 +45,7 @@ def encrypt(self, text: str) -> str: text = self.process_text(text.upper()) encrypted = "" for i in range(0, len(text), self.break_key): - batch = text[i : i + self.break_key] + batch = text[i:i+self.break_key] vec = [self.replace_letters(c) for c in batch] batch_vec = np.array([vec]).T product = self.encrypt_key.dot(batch_vec) @@ -62,13 +62,15 @@ def make_decrypt_key(self) -> np.ndarray: det = round(np.linalg.det(self.encrypt_key)) if det < 0: det %= len(self.key_string) - + # Find det modular inverse det_inv = next(i for i in range(36) if (det * i) % 36 == 1) - + # Compute inverse key inv_key = ( - det_inv * np.linalg.det(self.encrypt_key) * np.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)) @@ -78,7 +80,7 @@ def decrypt(self, text: str) -> str: text = self.process_text(text.upper()) decrypted = "" for i in range(0, len(text), self.break_key): - batch = text[i : i + self.break_key] + batch = text[i:i+self.break_key] vec = [self.replace_letters(c) for c in batch] batch_vec = np.array([vec]).T product = decrypt_key.dot(batch_vec) @@ -90,26 +92,23 @@ def decrypt(self, text: str) -> str: decrypted += decrypted_batch return decrypted - def main() -> None: """Hill Cipher CLI""" n = int(input("Enter key order: ")) print(f"Enter {n} rows of space-separated integers:") matrix = [list(map(int, input().split())) for _ in range(n)] - + hc = HillCipher(np.array(matrix)) - + option = input("1. Encrypt\n2. Decrypt\nChoose: ") text = input("Enter text: ") - + if option == "1": print("Encrypted:", hc.encrypt(text)) elif option == "2": print("Decrypted:", hc.decrypt(text)) - if __name__ == "__main__": import doctest - doctest.testmod() main() From 5d9b6adb469da3b0a43b2a4da16c93f2ea27d5cf Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 22 Jun 2025 02:28:31 +0000 Subject: [PATCH 026/107] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- ciphers/hill_cipher.py | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/ciphers/hill_cipher.py b/ciphers/hill_cipher.py index 5f45b50d19fb..b5296d99dd42 100644 --- a/ciphers/hill_cipher.py +++ b/ciphers/hill_cipher.py @@ -4,6 +4,7 @@ from maths.greatest_common_divisor import greatest_common_divisor + class HillCipher: key_string = string.ascii_uppercase + string.digits # 36 chars modulus = np.vectorize(lambda x: x % 36) # Mod 36 @@ -27,7 +28,7 @@ def check_determinant(self) -> None: det = round(np.linalg.det(self.encrypt_key)) if det < 0: det %= len(self.key_string) - + error_msg = f"Det {det} not coprime with 36. Try another key." if greatest_common_divisor(det, len(self.key_string)) != 1: raise ValueError(error_msg) @@ -35,7 +36,7 @@ def check_determinant(self) -> None: def process_text(self, text: str) -> str: """Uppercase, filter, pad text""" chars = [c for c in text.upper() if c in self.key_string] - last = chars[-1] if chars else 'A' + last = chars[-1] if chars else "A" while len(chars) % self.break_key != 0: chars.append(last) return "".join(chars) @@ -45,7 +46,7 @@ def encrypt(self, text: str) -> str: text = self.process_text(text.upper()) encrypted = "" for i in range(0, len(text), self.break_key): - batch = text[i:i+self.break_key] + batch = text[i : i + self.break_key] vec = [self.replace_letters(c) for c in batch] batch_vec = np.array([vec]).T product = self.encrypt_key.dot(batch_vec) @@ -62,15 +63,13 @@ def make_decrypt_key(self) -> np.ndarray: det = round(np.linalg.det(self.encrypt_key)) if det < 0: det %= len(self.key_string) - + # Find det modular inverse det_inv = next(i for i in range(36) if (det * i) % 36 == 1) - + # Compute inverse key inv_key = ( - det_inv * - np.linalg.det(self.encrypt_key) * - np.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)) @@ -80,7 +79,7 @@ def decrypt(self, text: str) -> str: text = self.process_text(text.upper()) decrypted = "" for i in range(0, len(text), self.break_key): - batch = text[i:i+self.break_key] + batch = text[i : i + self.break_key] vec = [self.replace_letters(c) for c in batch] batch_vec = np.array([vec]).T product = decrypt_key.dot(batch_vec) @@ -92,23 +91,26 @@ def decrypt(self, text: str) -> str: decrypted += decrypted_batch return decrypted + def main() -> None: """Hill Cipher CLI""" n = int(input("Enter key order: ")) print(f"Enter {n} rows of space-separated integers:") matrix = [list(map(int, input().split())) for _ in range(n)] - + hc = HillCipher(np.array(matrix)) - + option = input("1. Encrypt\n2. Decrypt\nChoose: ") text = input("Enter text: ") - + if option == "1": print("Encrypted:", hc.encrypt(text)) elif option == "2": print("Decrypted:", hc.decrypt(text)) + if __name__ == "__main__": import doctest + doctest.testmod() main() From b45bd32140e4b35ba021211a1d4e57d6fb4fa9a1 Mon Sep 17 00:00:00 2001 From: lighting9999 Date: Sun, 22 Jun 2025 10:35:11 +0800 Subject: [PATCH 027/107] Update aliquot_sum.py --- maths/aliquot_sum.py | 142 +++++++++++++++---------------------------- 1 file changed, 48 insertions(+), 94 deletions(-) diff --git a/maths/aliquot_sum.py b/maths/aliquot_sum.py index 418b886609b6..ce61c9ba303e 100644 --- a/maths/aliquot_sum.py +++ b/maths/aliquot_sum.py @@ -1,148 +1,102 @@ -from __future__ import annotations # Enable modern type hints +from __future__ import annotations +import doctest +from typing import overload, Tuple + +@overload +def aliquot_sum(input_num: int) -> int: ... +@overload +def aliquot_sum(input_num: int, return_factors: bool) -> Tuple[int, list[int]]: ... def aliquot_sum( input_num: int, return_factors: bool = False -) -> int | tuple[int, list[int]]: +) -> int | Tuple[int, list[int]]: """ - Calculates the aliquot sum of a positive integer. The aliquot sum is defined as - the sum of all proper divisors of a number (all divisors except the number itself). - - This implementation uses an optimized O(sqrt(n)) algorithm for efficiency. - + Calculates the aliquot sum of a positive integer. + The aliquot sum is the sum of all proper divisors of a number. + Args: - input_num: Positive integer to calculate aliquot sum for - return_factors: If True, returns tuple (aliquot_sum, sorted_factor_list) - + input_num: Positive integer + return_factors: If True, returns (sum, sorted_factor_list) + Returns: - Aliquot sum if return_factors=False - Tuple (aliquot_sum, sorted_factor_list) if return_factors=True - + Aliquot sum or (sum, factors) if return_factors=True + Raises: - TypeError: If input is not an integer - ValueError: If input is not positive - + TypeError: If input not integer + ValueError: If input not positive + Examples: >>> aliquot_sum(15) 9 >>> aliquot_sum(15, True) (9, [1, 3, 5]) - >>> aliquot_sum(1) - 0 """ - # Validate input type - must be integer + # Validate input if not isinstance(input_num, int): raise TypeError("Input must be an integer") - - # Validate input value - must be positive if input_num <= 0: raise ValueError("Input must be positive integer") - + # Special case: 1 has no proper divisors if input_num == 1: - # Return empty factor list if requested return (0, []) if return_factors else 0 - - # Initialize factors list with 1 (always a divisor) + + # Initialize factors and total factors = [1] - total = 1 # Start sum with 1 - - # Calculate square root as optimization boundary + total = 1 sqrt_num = int(input_num**0.5) - - # Iterate potential divisors from 2 to square root + + # Find factors efficiently for divisor in range(2, sqrt_num + 1): - # Check if divisor is a factor if input_num % divisor == 0: - # Add divisor to factors list factors.append(divisor) total += divisor - - # Calculate complement (pair factor) complement = input_num // divisor - - # Avoid duplicate for perfect squares if complement != divisor: factors.append(complement) total += complement - - # Sort factors for consistent output + factors.sort() - - # Return based on return_factors flag return (total, factors) if return_factors else total - def classify_number(n: int) -> str: """ - Classifies a number based on its aliquot sum: - - Perfect: aliquot sum = number - - Abundant: aliquot sum > number - - Deficient: aliquot sum < number - - Args: - n: Positive integer to classify - - Returns: - Classification string ("Perfect", "Abundant", or "Deficient") - - Raises: - ValueError: If input is not positive - + Classifies number based on aliquot sum: + - Perfect: sum = number + - Abundant: sum > number + - Deficient: sum < number + Examples: >>> classify_number(6) 'Perfect' >>> classify_number(12) 'Abundant' - >>> classify_number(19) - 'Deficient' """ - # Validate input if n <= 0: raise ValueError("Input must be positive integer") - - # Special case: 1 is always deficient if n == 1: return "Deficient" - - # Explicitly request integer-only aliquot sum - s = aliquot_sum(n, return_factors=False) - - # Determine classification + + s = aliquot_sum(n) # Always returns int if s == n: return "Perfect" - elif s > n: - return "Abundant" - else: - return "Deficient" - + return "Abundant" if s > n else "Deficient" if __name__ == "__main__": - import doctest - - # Run embedded doctests for verification doctest.testmod() - - # Additional demonstration examples - print("Aliquot sum of 28:", aliquot_sum(28)) # Perfect number - - # Get factors for 28 with type-safe access - factor_result = aliquot_sum(28, return_factors=True) - # Since we requested factors, we know it's a tuple - print("Factors of 28:", factor_result[1]) - + + print("Aliquot sum of 28:", aliquot_sum(28)) + + # Handle tuple return explicitly + result = aliquot_sum(28, True) + if isinstance(result, tuple): + print("Factors of 28:", result[1]) + print("Classification of 28:", classify_number(28)) - - # Large number performance test with targeted exception handling + + # Large number test try: - print("\nCalculating aliquot sum for 10^9...") - print("Result:", aliquot_sum(10**9)) # 1497558336 + print("Aliquot sum for 10^9:", aliquot_sum(10**9)) except (TypeError, ValueError) as e: - # Handle input-related errors - print(f"Input error: {e}") - except MemoryError: - # Handle potential memory issues with large numbers - print("Memory error: Number too large") - except OverflowError: - # Handle numeric overflow - print("Overflow error: Calculation too large") + print(f"Error: {e}") From f3a0ef603df06c0620042ba4f0cc25c07e295f83 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 22 Jun 2025 02:35:33 +0000 Subject: [PATCH 028/107] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- maths/aliquot_sum.py | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/maths/aliquot_sum.py b/maths/aliquot_sum.py index ce61c9ba303e..9223539f0868 100644 --- a/maths/aliquot_sum.py +++ b/maths/aliquot_sum.py @@ -3,29 +3,31 @@ import doctest from typing import overload, Tuple + @overload def aliquot_sum(input_num: int) -> int: ... @overload def aliquot_sum(input_num: int, return_factors: bool) -> Tuple[int, list[int]]: ... + def aliquot_sum( input_num: int, return_factors: bool = False ) -> int | Tuple[int, list[int]]: """ - Calculates the aliquot sum of a positive integer. + Calculates the aliquot sum of a positive integer. The aliquot sum is the sum of all proper divisors of a number. - + Args: input_num: Positive integer return_factors: If True, returns (sum, sorted_factor_list) - + Returns: Aliquot sum or (sum, factors) if return_factors=True - + Raises: TypeError: If input not integer ValueError: If input not positive - + Examples: >>> aliquot_sum(15) 9 @@ -37,16 +39,16 @@ def aliquot_sum( raise TypeError("Input must be an integer") if input_num <= 0: raise ValueError("Input must be positive integer") - + # Special case: 1 has no proper divisors if input_num == 1: return (0, []) if return_factors else 0 - + # Initialize factors and total factors = [1] total = 1 sqrt_num = int(input_num**0.5) - + # Find factors efficiently for divisor in range(2, sqrt_num + 1): if input_num % divisor == 0: @@ -56,17 +58,18 @@ def aliquot_sum( if complement != divisor: factors.append(complement) total += complement - + factors.sort() return (total, factors) if return_factors else total + def classify_number(n: int) -> str: """ Classifies number based on aliquot sum: - Perfect: sum = number - Abundant: sum > number - Deficient: sum < number - + Examples: >>> classify_number(6) 'Perfect' @@ -77,24 +80,25 @@ def classify_number(n: int) -> str: raise ValueError("Input must be positive integer") if n == 1: return "Deficient" - + s = aliquot_sum(n) # Always returns int if s == n: return "Perfect" return "Abundant" if s > n else "Deficient" + if __name__ == "__main__": doctest.testmod() - + print("Aliquot sum of 28:", aliquot_sum(28)) - + # Handle tuple return explicitly result = aliquot_sum(28, True) if isinstance(result, tuple): print("Factors of 28:", result[1]) - + print("Classification of 28:", classify_number(28)) - + # Large number test try: print("Aliquot sum for 10^9:", aliquot_sum(10**9)) From 81e5df5e564efb9bd5d86f7a0d849c1d328cc12b Mon Sep 17 00:00:00 2001 From: lighting9999 Date: Sun, 22 Jun 2025 10:38:12 +0800 Subject: [PATCH 029/107] Update aliquot_sum.py --- maths/aliquot_sum.py | 41 ++++++++++++++++++----------------------- 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/maths/aliquot_sum.py b/maths/aliquot_sum.py index 9223539f0868..9c6f3e6ec7b9 100644 --- a/maths/aliquot_sum.py +++ b/maths/aliquot_sum.py @@ -1,33 +1,30 @@ from __future__ import annotations - import doctest -from typing import overload, Tuple - +from typing import overload @overload def aliquot_sum(input_num: int) -> int: ... @overload -def aliquot_sum(input_num: int, return_factors: bool) -> Tuple[int, list[int]]: ... - +def aliquot_sum(input_num: int, return_factors: bool) -> tuple[int, list[int]]: ... def aliquot_sum( input_num: int, return_factors: bool = False -) -> int | Tuple[int, list[int]]: +) -> int | tuple[int, list[int]]: """ - Calculates the aliquot sum of a positive integer. + Calculates the aliquot sum of a positive integer. The aliquot sum is the sum of all proper divisors of a number. - + Args: input_num: Positive integer return_factors: If True, returns (sum, sorted_factor_list) - + Returns: Aliquot sum or (sum, factors) if return_factors=True - + Raises: TypeError: If input not integer ValueError: If input not positive - + Examples: >>> aliquot_sum(15) 9 @@ -39,16 +36,16 @@ def aliquot_sum( raise TypeError("Input must be an integer") if input_num <= 0: raise ValueError("Input must be positive integer") - + # Special case: 1 has no proper divisors if input_num == 1: return (0, []) if return_factors else 0 - + # Initialize factors and total factors = [1] total = 1 sqrt_num = int(input_num**0.5) - + # Find factors efficiently for divisor in range(2, sqrt_num + 1): if input_num % divisor == 0: @@ -58,18 +55,17 @@ def aliquot_sum( if complement != divisor: factors.append(complement) total += complement - + factors.sort() return (total, factors) if return_factors else total - def classify_number(n: int) -> str: """ Classifies number based on aliquot sum: - Perfect: sum = number - Abundant: sum > number - Deficient: sum < number - + Examples: >>> classify_number(6) 'Perfect' @@ -80,25 +76,24 @@ def classify_number(n: int) -> str: raise ValueError("Input must be positive integer") if n == 1: return "Deficient" - + s = aliquot_sum(n) # Always returns int if s == n: return "Perfect" return "Abundant" if s > n else "Deficient" - if __name__ == "__main__": doctest.testmod() - + print("Aliquot sum of 28:", aliquot_sum(28)) - + # Handle tuple return explicitly result = aliquot_sum(28, True) if isinstance(result, tuple): print("Factors of 28:", result[1]) - + print("Classification of 28:", classify_number(28)) - + # Large number test try: print("Aliquot sum for 10^9:", aliquot_sum(10**9)) From 0ce22f2665ad777900b2816558edb25ed708facd Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 22 Jun 2025 02:38:35 +0000 Subject: [PATCH 030/107] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- maths/aliquot_sum.py | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/maths/aliquot_sum.py b/maths/aliquot_sum.py index 9c6f3e6ec7b9..ed0dd7e3b097 100644 --- a/maths/aliquot_sum.py +++ b/maths/aliquot_sum.py @@ -2,29 +2,31 @@ import doctest from typing import overload + @overload def aliquot_sum(input_num: int) -> int: ... @overload def aliquot_sum(input_num: int, return_factors: bool) -> tuple[int, list[int]]: ... + def aliquot_sum( input_num: int, return_factors: bool = False ) -> int | tuple[int, list[int]]: """ - Calculates the aliquot sum of a positive integer. + Calculates the aliquot sum of a positive integer. The aliquot sum is the sum of all proper divisors of a number. - + Args: input_num: Positive integer return_factors: If True, returns (sum, sorted_factor_list) - + Returns: Aliquot sum or (sum, factors) if return_factors=True - + Raises: TypeError: If input not integer ValueError: If input not positive - + Examples: >>> aliquot_sum(15) 9 @@ -36,16 +38,16 @@ def aliquot_sum( raise TypeError("Input must be an integer") if input_num <= 0: raise ValueError("Input must be positive integer") - + # Special case: 1 has no proper divisors if input_num == 1: return (0, []) if return_factors else 0 - + # Initialize factors and total factors = [1] total = 1 sqrt_num = int(input_num**0.5) - + # Find factors efficiently for divisor in range(2, sqrt_num + 1): if input_num % divisor == 0: @@ -55,17 +57,18 @@ def aliquot_sum( if complement != divisor: factors.append(complement) total += complement - + factors.sort() return (total, factors) if return_factors else total + def classify_number(n: int) -> str: """ Classifies number based on aliquot sum: - Perfect: sum = number - Abundant: sum > number - Deficient: sum < number - + Examples: >>> classify_number(6) 'Perfect' @@ -76,24 +79,25 @@ def classify_number(n: int) -> str: raise ValueError("Input must be positive integer") if n == 1: return "Deficient" - + s = aliquot_sum(n) # Always returns int if s == n: return "Perfect" return "Abundant" if s > n else "Deficient" + if __name__ == "__main__": doctest.testmod() - + print("Aliquot sum of 28:", aliquot_sum(28)) - + # Handle tuple return explicitly result = aliquot_sum(28, True) if isinstance(result, tuple): print("Factors of 28:", result[1]) - + print("Classification of 28:", classify_number(28)) - + # Large number test try: print("Aliquot sum for 10^9:", aliquot_sum(10**9)) From 7f1ce32722dbcaa6a2ebf42335e037868e4a809f Mon Sep 17 00:00:00 2001 From: lighting9999 Date: Sun, 22 Jun 2025 10:40:19 +0800 Subject: [PATCH 031/107] Update aliquot_sum.py --- maths/aliquot_sum.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maths/aliquot_sum.py b/maths/aliquot_sum.py index ed0dd7e3b097..616bf4ef96fc 100644 --- a/maths/aliquot_sum.py +++ b/maths/aliquot_sum.py @@ -1,8 +1,8 @@ from __future__ import annotations + import doctest from typing import overload - @overload def aliquot_sum(input_num: int) -> int: ... @overload From d28b569594138f7dbe9cb1a8cb2dd32985d0c471 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 22 Jun 2025 02:40:41 +0000 Subject: [PATCH 032/107] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- maths/aliquot_sum.py | 1 + 1 file changed, 1 insertion(+) diff --git a/maths/aliquot_sum.py b/maths/aliquot_sum.py index 616bf4ef96fc..c338d008b8dd 100644 --- a/maths/aliquot_sum.py +++ b/maths/aliquot_sum.py @@ -3,6 +3,7 @@ import doctest from typing import overload + @overload def aliquot_sum(input_num: int) -> int: ... @overload From 79ab7d07dc62fe5c568c71682c92d0a7aab22516 Mon Sep 17 00:00:00 2001 From: lighting9999 Date: Sun, 22 Jun 2025 10:45:51 +0800 Subject: [PATCH 033/107] Update shuffled_shift_cipher.py --- ciphers/shuffled_shift_cipher.py | 216 ++++++++++--------------------- 1 file changed, 67 insertions(+), 149 deletions(-) diff --git a/ciphers/shuffled_shift_cipher.py b/ciphers/shuffled_shift_cipher.py index 08b2cab97c69..b6b89dc56d10 100644 --- a/ciphers/shuffled_shift_cipher.py +++ b/ciphers/shuffled_shift_cipher.py @@ -3,182 +3,100 @@ import random import string - class ShuffledShiftCipher: """ - This algorithm uses the Caesar Cipher algorithm but removes the option to - use brute force to decrypt the message. - - The passcode is a random password from the selection buffer of - 1. uppercase letters of the English alphabet - 2. lowercase letters of the English alphabet - 3. digits from 0 to 9 - - Using unique characters from the passcode, the normal list of characters, - that can be allowed in the plaintext, is pivoted and shuffled. Refer to docstring - of __make_key_list() to learn more about the shuffling. - - Then, using the passcode, a number is calculated which is used to encrypt the - plaintext message with the normal shift cipher method, only in this case, the - reference, to look back at while decrypting, is shuffled. - - Each cipher object can possess an optional argument as passcode, without which a - new passcode is generated for that object automatically. - cip1 = ShuffledShiftCipher('d4usr9TWxw9wMD') - cip2 = ShuffledShiftCipher() + Enhanced Caesar Cipher with shuffled character set for stronger encryption. + Uses a passcode to generate a unique shuffled key list and shift key. """ def __init__(self, passcode: str | None = None) -> None: """ - Initializes a cipher object with a passcode as it's entity - Note: No new passcode is generated if user provides a passcode - while creating the object + Initialize cipher with optional passcode. + Generates random passcode if none provided. """ self.__passcode = passcode or self.__passcode_creator() self.__key_list = self.__make_key_list() self.__shift_key = self.__make_shift_key() def __str__(self) -> str: - """ - :return: passcode of the cipher object - """ + """Return passcode as string representation.""" return "".join(self.__passcode) - def __neg_pos(self, iterlist: list[int]) -> list[int]: - """ - Mutates the list by changing the sign of each alternate element - - :param iterlist: takes a list iterable - :return: the mutated list - - """ - for i in range(1, len(iterlist), 2): - iterlist[i] *= -1 - return iterlist + def __neg_pos(self, iter_list: list[int]) -> list[int]: + """Alternate sign of elements in list.""" + for i in range(1, len(iter_list), 2): + iter_list[i] *= -1 + return iter_list def __passcode_creator(self) -> list[str]: - """ - Creates a random password from the selection buffer of - 1. uppercase letters of the English alphabet - 2. lowercase letters of the English alphabet - 3. digits from 0 to 9 - - :rtype: list - :return: a password of a random length between 10 to 20 - """ + """Generate random passcode.""" choices = string.ascii_letters + string.digits - password = [random.choice(choices) for _ in range(random.randint(10, 20))] - return password + pass_len = random.randint(10, 20) + return [random.choice(choices) for _ in range(pass_len)] def __make_key_list(self) -> list[str]: - """ - Shuffles the ordered character choices by pivoting at breakpoints - Breakpoints are the set of characters in the passcode - - eg: - if, ABCDEFGHIJKLMNOPQRSTUVWXYZ are the possible characters - and CAMERA is the passcode - then, breakpoints = [A,C,E,M,R] # sorted set of characters from passcode - shuffled parts: [A,CB,ED,MLKJIHGF,RQPON,ZYXWVUTS] - shuffled __key_list : ACBEDMLKJIHGFRQPONZYXWVUTS - - Shuffling only 26 letters of the english alphabet can generate 26! - combinations for the shuffled list. In the program we consider, a set of - 97 characters (including letters, digits, punctuation and whitespaces), - thereby creating a possibility of 97! combinations (which is a 152 digit number - in itself), thus diminishing the possibility of a brute force approach. - Moreover, shift keys even introduce a multiple of 26 for a brute force approach - for each of the already 97! combinations. - """ - # key_list_options contain nearly all printable except few elements from - # string.whitespace - key_list_options = ( - string.ascii_letters + string.digits + string.punctuation + " \t\n" - ) - - keys_l = [] - - # creates points known as breakpoints to break the key_list_options at those - # points and pivot each substring + """Create shuffled character set using passcode breakpoints.""" + # Get printable characters except rare whitespace + key_options = string.printable.strip("\r\x0b\x0c") breakpoints = sorted(set(self.__passcode)) - temp_list: list[str] = [] - - # algorithm for creating a new shuffled list, keys_l, out of key_list_options - for i in key_list_options: - temp_list.extend(i) - - # checking breakpoints at which to pivot temporary sublist and add it into - # keys_l - if i in breakpoints or i == key_list_options[-1]: - keys_l.extend(temp_list[::-1]) - temp_list.clear() - - # returning a shuffled keys_l to prevent brute force guessing of shift key - return keys_l + shuffled = [] + temp = [] + + for char in key_options: + temp.append(char) + if char in breakpoints or char == key_options[-1]: + shuffled.extend(reversed(temp)) + temp.clear() + + return shuffled def __make_shift_key(self) -> int: - """ - sum() of the mutated list of ascii values of all characters where the - mutated list is the one returned by __neg_pos() - """ - num = sum(self.__neg_pos([ord(x) for x in self.__passcode])) + """Calculate shift key from passcode ASCII values.""" + ascii_vals = [ord(x) for x in self.__passcode] + num = sum(self.__neg_pos(ascii_vals)) return num if num > 0 else len(self.__passcode) - def decrypt(self, encoded_message: str) -> str: - """ - Performs shifting of the encoded_message w.r.t. the shuffled __key_list - to create the decoded_message - - >>> ssc = ShuffledShiftCipher('4PYIXyqeQZr44') - >>> ssc.decrypt("d>**-1z6&'5z'5z:z+-='$'>=zp:>5:#z<'.&>#") - 'Hello, this is a modified Caesar cipher' - - """ - decoded_message = "" - - # decoding shift like Caesar cipher algorithm implementing negative shift or - # reverse shift or left shift - for i in encoded_message: - position = self.__key_list.index(i) - decoded_message += self.__key_list[ - (position - self.__shift_key) % -len(self.__key_list) - ] - - return decoded_message - def encrypt(self, plaintext: str) -> str: - """ - Performs shifting of the plaintext w.r.t. the shuffled __key_list - to create the encoded_message - - >>> ssc = ShuffledShiftCipher('4PYIXyqeQZr44') - >>> ssc.encrypt('Hello, this is a modified Caesar cipher') - "d>**-1z6&'5z'5z:z+-='$'>=zp:>5:#z<'.&>#" - - """ - encoded_message = "" - - # encoding shift like Caesar cipher algorithm implementing positive shift or - # forward shift or right shift - for i in plaintext: - position = self.__key_list.index(i) - encoded_message += self.__key_list[ - (position + self.__shift_key) % len(self.__key_list) - ] - - return encoded_message - - -def test_end_to_end(msg: str = "Hello, this is a modified Caesar cipher") -> str: - """ - >>> test_end_to_end() - 'Hello, this is a modified Caesar cipher' - """ - cip1 = ShuffledShiftCipher() - return cip1.decrypt(cip1.encrypt(msg)) + """Encrypt plaintext using shuffled shift cipher.""" + encoded = [] + key_len = len(self.__key_list) + + for char in plaintext: + pos = self.__key_list.index(char) + new_pos = (pos + self.__shift_key) % key_len + encoded.append(self.__key_list[new_pos]) + + return "".join(encoded) + def decrypt(self, encoded_message: str) -> str: + """Decrypt message using shuffled shift cipher.""" + decoded = [] + key_len = len(self.__key_list) + + for char in encoded_message: + pos = self.__key_list.index(char) + new_pos = (pos - self.__shift_key) % key_len + decoded.append(self.__key_list[new_pos]) + + return "".join(decoded) + +def test_end_to_end() -> str: + """Test full encryption-decryption cycle.""" + msg = "Hello, this is a modified Caesar cipher" + cipher = ShuffledShiftCipher() + return cipher.decrypt(cipher.encrypt(msg)) if __name__ == "__main__": import doctest - doctest.testmod() + + # Example usage + cipher = ShuffledShiftCipher("SecurePass123") + original = "Encryption test!" + encrypted = cipher.encrypt(original) + decrypted = cipher.decrypt(encrypted) + + print(f"Original: {original}") + print(f"Encrypted: {encrypted}") + print(f"Decrypted: {decrypted}") + print(f"Test passed: {decrypted == original}") From 9de4e339b1a8bd6dea7b822ccf09576077ae10c2 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 22 Jun 2025 02:46:14 +0000 Subject: [PATCH 034/107] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- ciphers/shuffled_shift_cipher.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/ciphers/shuffled_shift_cipher.py b/ciphers/shuffled_shift_cipher.py index b6b89dc56d10..e014e9a64eee 100644 --- a/ciphers/shuffled_shift_cipher.py +++ b/ciphers/shuffled_shift_cipher.py @@ -3,6 +3,7 @@ import random import string + class ShuffledShiftCipher: """ Enhanced Caesar Cipher with shuffled character set for stronger encryption. @@ -11,7 +12,7 @@ class ShuffledShiftCipher: def __init__(self, passcode: str | None = None) -> None: """ - Initialize cipher with optional passcode. + Initialize cipher with optional passcode. Generates random passcode if none provided. """ self.__passcode = passcode or self.__passcode_creator() @@ -41,13 +42,13 @@ def __make_key_list(self) -> list[str]: breakpoints = sorted(set(self.__passcode)) shuffled = [] temp = [] - + for char in key_options: temp.append(char) if char in breakpoints or char == key_options[-1]: shuffled.extend(reversed(temp)) temp.clear() - + return shuffled def __make_shift_key(self) -> int: @@ -60,42 +61,45 @@ def encrypt(self, plaintext: str) -> str: """Encrypt plaintext using shuffled shift cipher.""" encoded = [] key_len = len(self.__key_list) - + for char in plaintext: pos = self.__key_list.index(char) new_pos = (pos + self.__shift_key) % key_len encoded.append(self.__key_list[new_pos]) - + return "".join(encoded) def decrypt(self, encoded_message: str) -> str: """Decrypt message using shuffled shift cipher.""" decoded = [] key_len = len(self.__key_list) - + for char in encoded_message: pos = self.__key_list.index(char) new_pos = (pos - self.__shift_key) % key_len decoded.append(self.__key_list[new_pos]) - + return "".join(decoded) + def test_end_to_end() -> str: """Test full encryption-decryption cycle.""" msg = "Hello, this is a modified Caesar cipher" cipher = ShuffledShiftCipher() return cipher.decrypt(cipher.encrypt(msg)) + if __name__ == "__main__": import doctest + doctest.testmod() - + # Example usage cipher = ShuffledShiftCipher("SecurePass123") original = "Encryption test!" encrypted = cipher.encrypt(original) decrypted = cipher.decrypt(encrypted) - + print(f"Original: {original}") print(f"Encrypted: {encrypted}") print(f"Decrypted: {decrypted}") From 31a2b3ce00b60e58ce6513aedda75806f8afdb4a Mon Sep 17 00:00:00 2001 From: lighting9999 Date: Sun, 22 Jun 2025 10:48:19 +0800 Subject: [PATCH 035/107] Update shuffled_shift_cipher.py --- ciphers/shuffled_shift_cipher.py | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/ciphers/shuffled_shift_cipher.py b/ciphers/shuffled_shift_cipher.py index e014e9a64eee..9f8471eaa042 100644 --- a/ciphers/shuffled_shift_cipher.py +++ b/ciphers/shuffled_shift_cipher.py @@ -3,7 +3,6 @@ import random import string - class ShuffledShiftCipher: """ Enhanced Caesar Cipher with shuffled character set for stronger encryption. @@ -12,7 +11,7 @@ class ShuffledShiftCipher: def __init__(self, passcode: str | None = None) -> None: """ - Initialize cipher with optional passcode. + Initialize cipher with optional passcode. Generates random passcode if none provided. """ self.__passcode = passcode or self.__passcode_creator() @@ -40,15 +39,15 @@ def __make_key_list(self) -> list[str]: # Get printable characters except rare whitespace key_options = string.printable.strip("\r\x0b\x0c") breakpoints = sorted(set(self.__passcode)) - shuffled = [] - temp = [] - + shuffled: list[str] = [] # Explicit type annotation + temp: list[str] = [] # Explicit type annotation + for char in key_options: temp.append(char) if char in breakpoints or char == key_options[-1]: shuffled.extend(reversed(temp)) temp.clear() - + return shuffled def __make_shift_key(self) -> int: @@ -59,47 +58,44 @@ def __make_shift_key(self) -> int: def encrypt(self, plaintext: str) -> str: """Encrypt plaintext using shuffled shift cipher.""" - encoded = [] + encoded: list[str] = [] # Explicit type annotation key_len = len(self.__key_list) - + for char in plaintext: pos = self.__key_list.index(char) new_pos = (pos + self.__shift_key) % key_len encoded.append(self.__key_list[new_pos]) - + return "".join(encoded) def decrypt(self, encoded_message: str) -> str: """Decrypt message using shuffled shift cipher.""" - decoded = [] + decoded: list[str] = [] # Explicit type annotation key_len = len(self.__key_list) - + for char in encoded_message: pos = self.__key_list.index(char) new_pos = (pos - self.__shift_key) % key_len decoded.append(self.__key_list[new_pos]) - + return "".join(decoded) - def test_end_to_end() -> str: """Test full encryption-decryption cycle.""" msg = "Hello, this is a modified Caesar cipher" cipher = ShuffledShiftCipher() return cipher.decrypt(cipher.encrypt(msg)) - if __name__ == "__main__": import doctest - doctest.testmod() - + # Example usage cipher = ShuffledShiftCipher("SecurePass123") original = "Encryption test!" encrypted = cipher.encrypt(original) decrypted = cipher.decrypt(encrypted) - + print(f"Original: {original}") print(f"Encrypted: {encrypted}") print(f"Decrypted: {decrypted}") From a4c5f39b8bd81364e112739b000fc3e34d675218 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 22 Jun 2025 02:48:41 +0000 Subject: [PATCH 036/107] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- ciphers/shuffled_shift_cipher.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/ciphers/shuffled_shift_cipher.py b/ciphers/shuffled_shift_cipher.py index 9f8471eaa042..e605efad51f5 100644 --- a/ciphers/shuffled_shift_cipher.py +++ b/ciphers/shuffled_shift_cipher.py @@ -3,6 +3,7 @@ import random import string + class ShuffledShiftCipher: """ Enhanced Caesar Cipher with shuffled character set for stronger encryption. @@ -11,7 +12,7 @@ class ShuffledShiftCipher: def __init__(self, passcode: str | None = None) -> None: """ - Initialize cipher with optional passcode. + Initialize cipher with optional passcode. Generates random passcode if none provided. """ self.__passcode = passcode or self.__passcode_creator() @@ -41,13 +42,13 @@ def __make_key_list(self) -> list[str]: breakpoints = sorted(set(self.__passcode)) shuffled: list[str] = [] # Explicit type annotation temp: list[str] = [] # Explicit type annotation - + for char in key_options: temp.append(char) if char in breakpoints or char == key_options[-1]: shuffled.extend(reversed(temp)) temp.clear() - + return shuffled def __make_shift_key(self) -> int: @@ -60,42 +61,45 @@ def encrypt(self, plaintext: str) -> str: """Encrypt plaintext using shuffled shift cipher.""" encoded: list[str] = [] # Explicit type annotation key_len = len(self.__key_list) - + for char in plaintext: pos = self.__key_list.index(char) new_pos = (pos + self.__shift_key) % key_len encoded.append(self.__key_list[new_pos]) - + return "".join(encoded) def decrypt(self, encoded_message: str) -> str: """Decrypt message using shuffled shift cipher.""" decoded: list[str] = [] # Explicit type annotation key_len = len(self.__key_list) - + for char in encoded_message: pos = self.__key_list.index(char) new_pos = (pos - self.__shift_key) % key_len decoded.append(self.__key_list[new_pos]) - + return "".join(decoded) + def test_end_to_end() -> str: """Test full encryption-decryption cycle.""" msg = "Hello, this is a modified Caesar cipher" cipher = ShuffledShiftCipher() return cipher.decrypt(cipher.encrypt(msg)) + if __name__ == "__main__": import doctest + doctest.testmod() - + # Example usage cipher = ShuffledShiftCipher("SecurePass123") original = "Encryption test!" encrypted = cipher.encrypt(original) decrypted = cipher.decrypt(encrypted) - + print(f"Original: {original}") print(f"Encrypted: {encrypted}") print(f"Decrypted: {decrypted}") From 488a8b8344524e907b442c6729c2791fe1997c0a Mon Sep 17 00:00:00 2001 From: lighting9999 Date: Sun, 22 Jun 2025 10:58:22 +0800 Subject: [PATCH 037/107] Update avl_tree.py --- data_structures/binary_tree/avl_tree.py | 31 +++++-------------------- 1 file changed, 6 insertions(+), 25 deletions(-) diff --git a/data_structures/binary_tree/avl_tree.py b/data_structures/binary_tree/avl_tree.py index 8558305eefe4..647b55eb3943 100644 --- a/data_structures/binary_tree/avl_tree.py +++ b/data_structures/binary_tree/avl_tree.py @@ -1,15 +1,8 @@ -""" -Implementation of an auto-balanced binary tree! -For doctests run following command: -python3 -m doctest -v avl_tree.py -For testing run: -python avl_tree.py -""" - from __future__ import annotations import math import random +import doctest from typing import Any @@ -82,9 +75,7 @@ def my_max(a: int, b: int) -> int: if a > b: return a return b - - -def right_rotation(node: MyNode) -> MyNode: + def right_rotation(node: MyNode) -> MyNode: r""" A B / \ / \ @@ -105,9 +96,7 @@ def right_rotation(node: MyNode) -> MyNode: h2 = my_max(get_height(ret.get_right()), get_height(ret.get_left())) + 1 ret.set_height(h2) return ret - - -def left_rotation(node: MyNode) -> MyNode: + def left_rotation(node: MyNode) -> MyNode: """ a mirror symmetry rotation of the left_rotation """ @@ -145,9 +134,7 @@ def rl_rotation(node: MyNode) -> MyNode: assert right_child is not None node.set_right(right_rotation(right_child)) return left_rotation(node) - - -def insert_node(node: MyNode | None, data: Any) -> MyNode | None: + def insert_node(node: MyNode | None, data: Any) -> MyNode | None: if node is None: return MyNode(data) if data < node.get_data(): @@ -175,9 +162,7 @@ def insert_node(node: MyNode | None, data: Any) -> MyNode | None: h1 = my_max(get_height(node.get_right()), get_height(node.get_left())) + 1 node.set_height(h1) return node - - -def get_right_most(root: MyNode) -> Any: + def get_right_most(root: MyNode) -> Any: while True: right_child = root.get_right() if right_child is None: @@ -240,9 +225,7 @@ def del_node(root: MyNode, data: Any) -> MyNode | None: height = my_max(get_height(root.get_right()), get_height(root.get_left())) + 1 root.set_height(height) return root - - -class AVLtree: + class AVLtree: """ An AVL tree doctest Examples: @@ -330,8 +313,6 @@ def __str__( def _test() -> None: - import doctest - doctest.testmod() From 9633a6311a9588509fe18080796957f4583c7061 Mon Sep 17 00:00:00 2001 From: lighting9999 Date: Sun, 22 Jun 2025 11:26:20 +0800 Subject: [PATCH 038/107] Update avl_tree.py --- data_structures/binary_tree/avl_tree.py | 371 +++++++----------------- 1 file changed, 101 insertions(+), 270 deletions(-) diff --git a/data_structures/binary_tree/avl_tree.py b/data_structures/binary_tree/avl_tree.py index 647b55eb3943..bbf63a52022b 100644 --- a/data_structures/binary_tree/avl_tree.py +++ b/data_structures/binary_tree/avl_tree.py @@ -5,326 +5,157 @@ import doctest from typing import Any - class MyQueue: def __init__(self) -> None: self.data: list[Any] = [] - self.head: int = 0 - self.tail: int = 0 + self.head = self.tail = 0 def is_empty(self) -> bool: return self.head == self.tail def push(self, data: Any) -> None: self.data.append(data) - self.tail = self.tail + 1 + self.tail += 1 def pop(self) -> Any: ret = self.data[self.head] - self.head = self.head + 1 + self.head += 1 return ret - def count(self) -> int: - return self.tail - self.head - - def print_queue(self) -> None: - print(self.data) - print("**************") - print(self.data[self.head : self.tail]) - - class MyNode: def __init__(self, data: Any) -> None: self.data = data - self.left: MyNode | None = None - self.right: MyNode | None = None - self.height: int = 1 - - def get_data(self) -> Any: - return self.data - - def get_left(self) -> MyNode | None: - return self.left - - def get_right(self) -> MyNode | None: - return self.right - - def get_height(self) -> int: - return self.height - - def set_data(self, data: Any) -> None: - self.data = data - - def set_left(self, node: MyNode | None) -> None: - self.left = node - - def set_right(self, node: MyNode | None) -> None: - self.right = node - - def set_height(self, height: int) -> None: - self.height = height - - -def get_height(node: MyNode | None) -> int: - if node is None: - return 0 - return node.get_height() - - -def my_max(a: int, b: int) -> int: - if a > b: - return a - return b - def right_rotation(node: MyNode) -> MyNode: - r""" - A B - / \ / \ - B C Bl A - / \ --> / / \ - Bl Br UB Br C - / - UB - UB = unbalanced node - """ - print("left rotation node:", node.get_data()) - ret = node.get_left() - assert ret is not None - node.set_left(ret.get_right()) - ret.set_right(node) - h1 = my_max(get_height(node.get_right()), get_height(node.get_left())) + 1 - node.set_height(h1) - h2 = my_max(get_height(ret.get_right()), get_height(ret.get_left())) + 1 - ret.set_height(h2) + self.left = self.right = None + self.height = 1 + + def get_data(self) -> Any: return self.data + def get_left(self) -> MyNode | None: return self.left + def get_right(self) -> MyNode | None: return self.right + def get_height(self) -> int: return self.height + def set_data(self, data: Any) -> None: self.data = data + def set_left(self, node: MyNode | None) -> None: self.left = node + def set_right(self, node: MyNode | None) -> None: self.right = node + def set_height(self, height: int) -> None: self.height = height + +def get_height(node: MyNode | None) -> int: + return node.height if node else 0 + +def my_max(a: int, b: int) -> int: + return a if a > b else b + +def right_rotation(node: MyNode) -> MyNode: + print("left rotation node:", node.data) + ret = node.left + node.left = ret.right + ret.right = node + node.height = my_max(get_height(node.right), get_height(node.left)) + 1 + ret.height = my_max(get_height(ret.right), get_height(ret.left)) + 1 return ret def left_rotation(node: MyNode) -> MyNode: - """ - a mirror symmetry rotation of the left_rotation - """ - print("right rotation node:", node.get_data()) - ret = node.get_right() - assert ret is not None - node.set_right(ret.get_left()) - ret.set_left(node) - h1 = my_max(get_height(node.get_right()), get_height(node.get_left())) + 1 - node.set_height(h1) - h2 = my_max(get_height(ret.get_right()), get_height(ret.get_left())) + 1 - ret.set_height(h2) + print("right rotation node:", node.data) + ret = node.right + node.right = ret.left + ret.left = node + node.height = my_max(get_height(node.right), get_height(node.left)) + 1 + ret.height = my_max(get_height(ret.right), get_height(ret.left)) + 1 return ret - def lr_rotation(node: MyNode) -> MyNode: - r""" - A A Br - / \ / \ / \ - B C LR Br C RR B A - / \ --> / \ --> / / \ - Bl Br B UB Bl UB C - \ / - UB Bl - RR = right_rotation LR = left_rotation - """ - left_child = node.get_left() - assert left_child is not None - node.set_left(left_rotation(left_child)) + node.left = left_rotation(node.left) return right_rotation(node) - def rl_rotation(node: MyNode) -> MyNode: - right_child = node.get_right() - assert right_child is not None - node.set_right(right_rotation(right_child)) + node.right = right_rotation(node.right) return left_rotation(node) - def insert_node(node: MyNode | None, data: Any) -> MyNode | None: - if node is None: - return MyNode(data) - if data < node.get_data(): - node.set_left(insert_node(node.get_left(), data)) - if ( - get_height(node.get_left()) - get_height(node.get_right()) == 2 - ): # an unbalance detected - left_child = node.get_left() - assert left_child is not None - if ( - data < left_child.get_data() - ): # new node is the left child of the left child - node = right_rotation(node) - else: - node = lr_rotation(node) + +def insert_node(node: MyNode | None, data: Any) -> MyNode | None: + if not node: return MyNode(data) + + if data < node.data: + node.left = insert_node(node.left, data) + if get_height(node.left) - get_height(node.right) == 2: + node = right_rotation(node) if data < node.left.data else lr_rotation(node) else: - node.set_right(insert_node(node.get_right(), data)) - if get_height(node.get_right()) - get_height(node.get_left()) == 2: - right_child = node.get_right() - assert right_child is not None - if data < right_child.get_data(): - node = rl_rotation(node) - else: - node = left_rotation(node) - h1 = my_max(get_height(node.get_right()), get_height(node.get_left())) + 1 - node.set_height(h1) + node.right = insert_node(node.right, data) + if get_height(node.right) - get_height(node.left) == 2: + node = rl_rotation(node) if data < node.right.data else left_rotation(node) + + node.height = my_max(get_height(node.right), get_height(node.left)) + 1 return node - def get_right_most(root: MyNode) -> Any: - while True: - right_child = root.get_right() - if right_child is None: - break - root = right_child - return root.get_data() - - -def get_left_most(root: MyNode) -> Any: - while True: - left_child = root.get_left() - if left_child is None: - break - root = left_child - return root.get_data() +def get_extreme(root: MyNode, is_right: bool) -> Any: + while (child := root.right if is_right else root.left): + root = child + return root.data def del_node(root: MyNode, data: Any) -> MyNode | None: - left_child = root.get_left() - right_child = root.get_right() - if root.get_data() == data: - if left_child is not None and right_child is not None: - temp_data = get_left_most(right_child) - root.set_data(temp_data) - root.set_right(del_node(right_child, temp_data)) - elif left_child is not None: - root = left_child - elif right_child is not None: - root = right_child - else: - return None - elif root.get_data() > data: - if left_child is None: - print("No such data") - return root - else: - root.set_left(del_node(left_child, data)) - # root.get_data() < data - elif right_child is None: - return root - 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()): - root = left_rotation(root) - else: - root = rl_rotation(root) - elif get_height(right_child) - get_height(left_child) == -2: - assert left_child is not None - if get_height(left_child.get_left()) > get_height(left_child.get_right()): - root = right_rotation(root) - else: - root = lr_rotation(root) - height = my_max(get_height(root.get_right()), get_height(root.get_left())) + 1 - root.set_height(height) + if root.data == data: + if root.left and root.right: + root.data = get_extreme(root.right, False) + root.right = del_node(root.right, root.data) + else: return root.left or root.right + elif root.data > data: + if not root.left: return root + root.left = del_node(root.left, data) + else: root.right = del_node(root.right, data) + + if get_height(root.right) - get_height(root.left) == 2: + root = left_rotation(root) if get_height(root.right.right) > get_height(root.right.left) else rl_rotation(root) + elif get_height(root.right) - get_height(root.left) == -2: + root = right_rotation(root) if get_height(root.left.left) > get_height(root.left.right) else lr_rotation(root) + + root.height = my_max(get_height(root.right), get_height(root.left)) + 1 return root class AVLtree: - """ - An AVL tree doctest - Examples: - >>> t = AVLtree() - >>> t.insert(4) - insert:4 - >>> print(str(t).replace(" \\n","\\n")) - 4 - ************************************* - >>> t.insert(2) - insert:2 - >>> print(str(t).replace(" \\n","\\n").replace(" \\n","\\n")) - 4 - 2 * - ************************************* - >>> t.insert(3) - insert:3 - right rotation node: 2 - left rotation node: 4 - >>> print(str(t).replace(" \\n","\\n").replace(" \\n","\\n")) - 3 - 2 4 - ************************************* - >>> t.get_height() - 2 - >>> t.del_node(3) - delete:3 - >>> print(str(t).replace(" \\n","\\n").replace(" \\n","\\n")) - 4 - 2 * - ************************************* - """ - - def __init__(self) -> None: - self.root: MyNode | None = None - - def get_height(self) -> int: - return get_height(self.root) - + def __init__(self) -> None: self.root = None + def get_height(self) -> int: return get_height(self.root) + def insert(self, data: Any) -> None: - print("insert:" + str(data)) + print(f"insert:{data}") self.root = insert_node(self.root, data) - + def del_node(self, data: Any) -> None: - print("delete:" + str(data)) - if self.root is None: - print("Tree is empty!") - return + print(f"delete:{data}") + if not self.root: return self.root = del_node(self.root, data) - - def __str__( - self, - ) -> str: # a level traversale, gives a more intuitive look on the tree - output = "" - q = MyQueue() + + def __str__(self) -> str: + if not self.root: return "" + q, output, layer, cnt = MyQueue(), "", self.get_height(), 0 q.push(self.root) - layer = self.get_height() - if layer == 0: - return output - cnt = 0 + while not q.is_empty(): node = q.pop() - space = " " * int(math.pow(2, layer - 1)) - output += space - if node is None: - output += "*" + space = " " * int(2**(layer-1)) + output += space + (str(node.data) if node else "*") + space + cnt += 1 + + if node: + q.push(node.left) + q.push(node.right) + else: q.push(None) q.push(None) - else: - output += str(node.get_data()) - q.push(node.get_left()) - q.push(node.get_right()) - output += space - cnt = cnt + 1 - for i in range(100): - if cnt == math.pow(2, i) - 1: - layer = layer - 1 - if layer == 0: - output += "\n*************************************" - return output - output += "\n" - break - output += "\n*************************************" - return output - - -def _test() -> None: - doctest.testmod() - + + if any(cnt == 2**i - 1 for i in range(10)): + layer -= 1 + output += "\n" + if layer == 0: break + + return output + "\n" + "*"*36 + def _test() -> None: doctest.testmod() if __name__ == "__main__": _test() t = AVLtree() lst = list(range(10)) random.shuffle(lst) + for i in lst: t.insert(i) - print(str(t)) + print(t) + random.shuffle(lst) for i in lst: t.del_node(i) - print(str(t)) + print(t) From 2708486c7ef0778342ea4f7f7aa3e0ffa265b82e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 22 Jun 2025 03:26:32 +0000 Subject: [PATCH 039/107] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- data_structures/binary_tree/avl_tree.py | 30 ++++++++++++------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/data_structures/binary_tree/avl_tree.py b/data_structures/binary_tree/avl_tree.py index bbf63a52022b..463c8672e40d 100644 --- a/data_structures/binary_tree/avl_tree.py +++ b/data_structures/binary_tree/avl_tree.py @@ -37,10 +37,10 @@ def set_left(self, node: MyNode | None) -> None: self.left = node def set_right(self, node: MyNode | None) -> None: self.right = node def set_height(self, height: int) -> None: self.height = height -def get_height(node: MyNode | None) -> int: +def get_height(node: MyNode | None) -> int: return node.height if node else 0 -def my_max(a: int, b: int) -> int: +def my_max(a: int, b: int) -> int: return a if a > b else b def right_rotation(node: MyNode) -> MyNode: @@ -70,7 +70,7 @@ def rl_rotation(node: MyNode) -> MyNode: def insert_node(node: MyNode | None, data: Any) -> MyNode | None: if not node: return MyNode(data) - + if data < node.data: node.left = insert_node(node.left, data) if get_height(node.left) - get_height(node.right) == 2: @@ -79,7 +79,7 @@ def insert_node(node: MyNode | None, data: Any) -> MyNode | None: node.right = insert_node(node.right, data) if get_height(node.right) - get_height(node.left) == 2: node = rl_rotation(node) if data < node.right.data else left_rotation(node) - + node.height = my_max(get_height(node.right), get_height(node.left)) + 1 return node @@ -98,50 +98,50 @@ def del_node(root: MyNode, data: Any) -> MyNode | None: if not root.left: return root root.left = del_node(root.left, data) else: root.right = del_node(root.right, data) - + if get_height(root.right) - get_height(root.left) == 2: root = left_rotation(root) if get_height(root.right.right) > get_height(root.right.left) else rl_rotation(root) elif get_height(root.right) - get_height(root.left) == -2: root = right_rotation(root) if get_height(root.left.left) > get_height(root.left.right) else lr_rotation(root) - + root.height = my_max(get_height(root.right), get_height(root.left)) + 1 return root class AVLtree: def __init__(self) -> None: self.root = None def get_height(self) -> int: return get_height(self.root) - + def insert(self, data: Any) -> None: print(f"insert:{data}") self.root = insert_node(self.root, data) - + def del_node(self, data: Any) -> None: print(f"delete:{data}") if not self.root: return self.root = del_node(self.root, data) - + def __str__(self) -> str: if not self.root: return "" q, output, layer, cnt = MyQueue(), "", self.get_height(), 0 q.push(self.root) - + while not q.is_empty(): node = q.pop() space = " " * int(2**(layer-1)) output += space + (str(node.data) if node else "*") + space cnt += 1 - + if node: q.push(node.left) q.push(node.right) else: q.push(None) q.push(None) - + if any(cnt == 2**i - 1 for i in range(10)): layer -= 1 output += "\n" if layer == 0: break - + return output + "\n" + "*"*36 def _test() -> None: doctest.testmod() @@ -150,11 +150,11 @@ def _test() -> None: doctest.testmod() t = AVLtree() lst = list(range(10)) random.shuffle(lst) - + for i in lst: t.insert(i) print(t) - + random.shuffle(lst) for i in lst: t.del_node(i) From 8fb7946a40f26d29d0889ae4def32f1c8e170cf9 Mon Sep 17 00:00:00 2001 From: lighting9999 Date: Sun, 22 Jun 2025 11:33:09 +0800 Subject: [PATCH 040/107] Update avl_tree.py --- data_structures/binary_tree/avl_tree.py | 142 ++++++++++++++++-------- 1 file changed, 97 insertions(+), 45 deletions(-) diff --git a/data_structures/binary_tree/avl_tree.py b/data_structures/binary_tree/avl_tree.py index 463c8672e40d..d59f5194dea2 100644 --- a/data_structures/binary_tree/avl_tree.py +++ b/data_structures/binary_tree/avl_tree.py @@ -1,3 +1,9 @@ +""" +Auto-balanced binary tree implementation +For doctests: python3 -m doctest -v avl_tree.py +For testing: python avl_tree.py +""" + from __future__ import annotations import math @@ -28,19 +34,34 @@ def __init__(self, data: Any) -> None: self.left = self.right = None self.height = 1 - def get_data(self) -> Any: return self.data - def get_left(self) -> MyNode | None: return self.left - def get_right(self) -> MyNode | None: return self.right - def get_height(self) -> int: return self.height - def set_data(self, data: Any) -> None: self.data = data - def set_left(self, node: MyNode | None) -> None: self.left = node - def set_right(self, node: MyNode | None) -> None: self.right = node - def set_height(self, height: int) -> None: self.height = height - -def get_height(node: MyNode | None) -> int: + def get_data(self) -> Any: + return self.data + + def get_left(self) -> MyNode | None: + return self.left + + def get_right(self) -> MyNode | None: + return self.right + + def get_height(self) -> int: + return self.height + + def set_data(self, data: Any) -> None: + self.data = data + + def set_left(self, node: MyNode | None) -> None: + self.left = node + + def set_right(self, node: MyNode | None) -> None: + self.right = node + + def set_height(self, height: int) -> None: + self.height = height + +def get_height(node: MyNode | None) -> int: return node.height if node else 0 -def my_max(a: int, b: int) -> int: +def my_max(a: int, b: int) -> int: return a if a > b else b def right_rotation(node: MyNode) -> MyNode: @@ -51,7 +72,8 @@ def right_rotation(node: MyNode) -> MyNode: node.height = my_max(get_height(node.right), get_height(node.left)) + 1 ret.height = my_max(get_height(ret.right), get_height(ret.left)) + 1 return ret - def left_rotation(node: MyNode) -> MyNode: + +def left_rotation(node: MyNode) -> MyNode: print("right rotation node:", node.data) ret = node.right node.right = ret.left @@ -69,17 +91,24 @@ def rl_rotation(node: MyNode) -> MyNode: return left_rotation(node) def insert_node(node: MyNode | None, data: Any) -> MyNode | None: - if not node: return MyNode(data) - + if not node: + return MyNode(data) + if data < node.data: node.left = insert_node(node.left, data) if get_height(node.left) - get_height(node.right) == 2: - node = right_rotation(node) if data < node.left.data else lr_rotation(node) + if data < node.left.data: + node = right_rotation(node) + else: + node = lr_rotation(node) else: node.right = insert_node(node.right, data) if get_height(node.right) - get_height(node.left) == 2: - node = rl_rotation(node) if data < node.right.data else left_rotation(node) - + if data < node.right.data: + node = rl_rotation(node) + else: + node = left_rotation(node) + node.height = my_max(get_height(node.right), get_height(node.left)) + 1 return node @@ -93,68 +122,91 @@ def del_node(root: MyNode, data: Any) -> MyNode | None: if root.left and root.right: root.data = get_extreme(root.right, False) root.right = del_node(root.right, root.data) - else: return root.left or root.right + else: + return root.left or root.right elif root.data > data: - if not root.left: return root + if not root.left: + return root root.left = del_node(root.left, data) - else: root.right = del_node(root.right, data) - - if get_height(root.right) - get_height(root.left) == 2: - root = left_rotation(root) if get_height(root.right.right) > get_height(root.right.left) else rl_rotation(root) - elif get_height(root.right) - get_height(root.left) == -2: - root = right_rotation(root) if get_height(root.left.left) > get_height(root.left.right) else lr_rotation(root) - + else: + root.right = del_node(root.right, data) + + # Handle balancing + right_height = get_height(root.right) + left_height = get_height(root.left) + + if right_height - left_height == 2: + if get_height(root.right.right) > get_height(root.right.left): + root = left_rotation(root) + else: + root = rl_rotation(root) + elif right_height - left_height == -2: + if get_height(root.left.left) > get_height(root.left.right): + root = right_rotation(root) + else: + root = lr_rotation(root) + root.height = my_max(get_height(root.right), get_height(root.left)) + 1 return root - class AVLtree: - def __init__(self) -> None: self.root = None - def get_height(self) -> int: return get_height(self.root) - +class AVLtree: + def __init__(self) -> None: + self.root = None + + def get_height(self) -> int: + return get_height(self.root) + def insert(self, data: Any) -> None: print(f"insert:{data}") self.root = insert_node(self.root, data) - + def del_node(self, data: Any) -> None: print(f"delete:{data}") - if not self.root: return + if not self.root: + return self.root = del_node(self.root, data) - + def __str__(self) -> str: - if not self.root: return "" + if not self.root: + return "" q, output, layer, cnt = MyQueue(), "", self.get_height(), 0 q.push(self.root) - + while not q.is_empty(): node = q.pop() space = " " * int(2**(layer-1)) output += space + (str(node.data) if node else "*") + space cnt += 1 - + if node: q.push(node.left) q.push(node.right) else: q.push(None) q.push(None) - - if any(cnt == 2**i - 1 for i in range(10)): - layer -= 1 - output += "\n" - if layer == 0: break - + + for i in range(10): + if cnt == 2**i - 1: + layer -= 1 + output += "\n" + if layer == 0: + break + break + return output + "\n" + "*"*36 - def _test() -> None: doctest.testmod() + +def _test() -> None: + doctest.testmod() if __name__ == "__main__": _test() t = AVLtree() lst = list(range(10)) random.shuffle(lst) - + for i in lst: t.insert(i) print(t) - + random.shuffle(lst) for i in lst: t.del_node(i) From 0196e75034e13c72c7e77921e6154f47bc7e951d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 22 Jun 2025 03:33:31 +0000 Subject: [PATCH 041/107] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- data_structures/binary_tree/avl_tree.py | 91 ++++++++++++++----------- 1 file changed, 53 insertions(+), 38 deletions(-) diff --git a/data_structures/binary_tree/avl_tree.py b/data_structures/binary_tree/avl_tree.py index d59f5194dea2..c1afa7103b82 100644 --- a/data_structures/binary_tree/avl_tree.py +++ b/data_structures/binary_tree/avl_tree.py @@ -11,6 +11,7 @@ import doctest from typing import Any + class MyQueue: def __init__(self) -> None: self.data: list[Any] = [] @@ -28,42 +29,46 @@ def pop(self) -> Any: self.head += 1 return ret + class MyNode: def __init__(self, data: Any) -> None: self.data = data self.left = self.right = None self.height = 1 - def get_data(self) -> Any: + def get_data(self) -> Any: return self.data - - def get_left(self) -> MyNode | None: + + def get_left(self) -> MyNode | None: return self.left - - def get_right(self) -> MyNode | None: + + def get_right(self) -> MyNode | None: return self.right - - def get_height(self) -> int: + + def get_height(self) -> int: return self.height - - def set_data(self, data: Any) -> None: + + def set_data(self, data: Any) -> None: self.data = data - - def set_left(self, node: MyNode | None) -> None: + + def set_left(self, node: MyNode | None) -> None: self.left = node - - def set_right(self, node: MyNode | None) -> None: + + def set_right(self, node: MyNode | None) -> None: self.right = node - - def set_height(self, height: int) -> None: + + def set_height(self, height: int) -> None: self.height = height -def get_height(node: MyNode | None) -> int: + +def get_height(node: MyNode | None) -> int: return node.height if node else 0 -def my_max(a: int, b: int) -> int: + +def my_max(a: int, b: int) -> int: return a if a > b else b + def right_rotation(node: MyNode) -> MyNode: print("left rotation node:", node.data) ret = node.left @@ -73,6 +78,7 @@ def right_rotation(node: MyNode) -> MyNode: ret.height = my_max(get_height(ret.right), get_height(ret.left)) + 1 return ret + def left_rotation(node: MyNode) -> MyNode: print("right rotation node:", node.data) ret = node.right @@ -82,18 +88,21 @@ def left_rotation(node: MyNode) -> MyNode: ret.height = my_max(get_height(ret.right), get_height(ret.left)) + 1 return ret + def lr_rotation(node: MyNode) -> MyNode: node.left = left_rotation(node.left) return right_rotation(node) + def rl_rotation(node: MyNode) -> MyNode: node.right = right_rotation(node.right) return left_rotation(node) + def insert_node(node: MyNode | None, data: Any) -> MyNode | None: if not node: return MyNode(data) - + if data < node.data: node.left = insert_node(node.left, data) if get_height(node.left) - get_height(node.right) == 2: @@ -108,15 +117,17 @@ def insert_node(node: MyNode | None, data: Any) -> MyNode | None: node = rl_rotation(node) else: node = left_rotation(node) - + node.height = my_max(get_height(node.right), get_height(node.left)) + 1 return node + def get_extreme(root: MyNode, is_right: bool) -> Any: - while (child := root.right if is_right else root.left): + while child := root.right if is_right else root.left: root = child return root.data + def del_node(root: MyNode, data: Any) -> MyNode | None: if root.data == data: if root.left and root.right: @@ -130,11 +141,11 @@ def del_node(root: MyNode, data: Any) -> MyNode | None: root.left = del_node(root.left, data) else: root.right = del_node(root.right, data) - + # Handle balancing right_height = get_height(root.right) left_height = get_height(root.left) - + if right_height - left_height == 2: if get_height(root.right.right) > get_height(root.right.left): root = left_rotation(root) @@ -145,45 +156,47 @@ def del_node(root: MyNode, data: Any) -> MyNode | None: root = right_rotation(root) else: root = lr_rotation(root) - + root.height = my_max(get_height(root.right), get_height(root.left)) + 1 return root + + class AVLtree: - def __init__(self) -> None: + def __init__(self) -> None: self.root = None - - def get_height(self) -> int: + + def get_height(self) -> int: return get_height(self.root) - + def insert(self, data: Any) -> None: print(f"insert:{data}") self.root = insert_node(self.root, data) - + def del_node(self, data: Any) -> None: print(f"delete:{data}") if not self.root: return self.root = del_node(self.root, data) - + def __str__(self) -> str: if not self.root: return "" q, output, layer, cnt = MyQueue(), "", self.get_height(), 0 q.push(self.root) - + while not q.is_empty(): node = q.pop() - space = " " * int(2**(layer-1)) + space = " " * int(2 ** (layer - 1)) output += space + (str(node.data) if node else "*") + space cnt += 1 - + if node: q.push(node.left) q.push(node.right) else: q.push(None) q.push(None) - + for i in range(10): if cnt == 2**i - 1: layer -= 1 @@ -191,22 +204,24 @@ def __str__(self) -> str: if layer == 0: break break - - return output + "\n" + "*"*36 -def _test() -> None: + return output + "\n" + "*" * 36 + + +def _test() -> None: doctest.testmod() + if __name__ == "__main__": _test() t = AVLtree() lst = list(range(10)) random.shuffle(lst) - + for i in lst: t.insert(i) print(t) - + random.shuffle(lst) for i in lst: t.del_node(i) From 1ac8d5b3706630b2dcb5c8f67e1afa7d591e086c Mon Sep 17 00:00:00 2001 From: lighting9999 Date: Sun, 22 Jun 2025 11:39:59 +0800 Subject: [PATCH 042/107] Update avl_tree.py --- data_structures/binary_tree/avl_tree.py | 231 ++++++++++-------------- 1 file changed, 97 insertions(+), 134 deletions(-) diff --git a/data_structures/binary_tree/avl_tree.py b/data_structures/binary_tree/avl_tree.py index c1afa7103b82..82a11b95355f 100644 --- a/data_structures/binary_tree/avl_tree.py +++ b/data_structures/binary_tree/avl_tree.py @@ -1,17 +1,8 @@ -""" -Auto-balanced binary tree implementation -For doctests: python3 -m doctest -v avl_tree.py -For testing: python avl_tree.py -""" - from __future__ import annotations -import math import random -import doctest from typing import Any - class MyQueue: def __init__(self) -> None: self.data: list[Any] = [] @@ -25,204 +16,176 @@ def push(self, data: Any) -> None: self.tail += 1 def pop(self) -> Any: - ret = self.data[self.head] - self.head += 1 - return ret - + return self.data[self.head] class MyNode: + __slots__ = ("data", "left", "right", "height") + def __init__(self, data: Any) -> None: self.data = data - self.left = self.right = None + self.left: MyNode | None = None + self.right: MyNode | None = None self.height = 1 - def get_data(self) -> Any: - return self.data - - def get_left(self) -> MyNode | None: - return self.left - - def get_right(self) -> MyNode | None: - return self.right - - def get_height(self) -> int: - return self.height - - def set_data(self, data: Any) -> None: - self.data = data - - def set_left(self, node: MyNode | None) -> None: - self.left = node - - def set_right(self, node: MyNode | None) -> None: - self.right = node - - def set_height(self, height: int) -> None: - self.height = height - - -def get_height(node: MyNode | None) -> int: +def get_height(node: MyNode | None) -> int: return node.height if node else 0 - -def my_max(a: int, b: int) -> int: +def my_max(a: int, b: int) -> int: return a if a > b else b - def right_rotation(node: MyNode) -> MyNode: - print("left rotation node:", node.data) - ret = node.left - node.left = ret.right - ret.right = node + left_child = node.left + if left_child is None: + return node + + node.left = left_child.right + left_child.right = node node.height = my_max(get_height(node.right), get_height(node.left)) + 1 - ret.height = my_max(get_height(ret.right), get_height(ret.left)) + 1 - return ret - + left_child.height = my_max(get_height(left_child.right), get_height(left_child.left)) + 1 + return left_child def left_rotation(node: MyNode) -> MyNode: - print("right rotation node:", node.data) - ret = node.right - node.right = ret.left - ret.left = node + right_child = node.right + if right_child is None: + return node + + node.right = right_child.left + right_child.left = node node.height = my_max(get_height(node.right), get_height(node.left)) + 1 - ret.height = my_max(get_height(ret.right), get_height(ret.left)) + 1 - return ret - + right_child.height = my_max(get_height(right_child.right), get_height(right_child.left)) + 1 + return right_child def lr_rotation(node: MyNode) -> MyNode: - node.left = left_rotation(node.left) + if node.left: + node.left = left_rotation(node.left) return right_rotation(node) - def rl_rotation(node: MyNode) -> MyNode: - node.right = right_rotation(node.right) + if node.right: + node.right = right_rotation(node.right) return left_rotation(node) - def insert_node(node: MyNode | None, data: Any) -> MyNode | None: - if not node: + if node is None: return MyNode(data) - + if data < node.data: node.left = insert_node(node.left, data) if get_height(node.left) - get_height(node.right) == 2: - if data < node.left.data: + if node.left and data < node.left.data: node = right_rotation(node) else: node = lr_rotation(node) else: node.right = insert_node(node.right, data) if get_height(node.right) - get_height(node.left) == 2: - if data < node.right.data: + if node.right and data < node.right.data: node = rl_rotation(node) else: node = left_rotation(node) - + node.height = my_max(get_height(node.right), get_height(node.left)) + 1 return node - -def get_extreme(root: MyNode, is_right: bool) -> Any: - while child := root.right if is_right else root.left: - root = child +def get_left_most(root: MyNode) -> Any: + while root.left: + root = root.left return root.data - -def del_node(root: MyNode, data: Any) -> MyNode | None: - if root.data == data: +def del_node(root: MyNode | None, data: Any) -> MyNode | None: + if root is None: + return None + + if data == root.data: if root.left and root.right: - root.data = get_extreme(root.right, False) + root.data = get_left_most(root.right) root.right = del_node(root.right, root.data) else: return root.left or root.right - elif root.data > data: - if not root.left: - return root + elif data < root.data: root.left = del_node(root.left, data) else: root.right = del_node(root.right, data) - - # Handle balancing - right_height = get_height(root.right) + + if root.left is None and root.right is None: + root.height = 1 + return root + left_height = get_height(root.left) - + right_height = get_height(root.right) + if right_height - left_height == 2: - if get_height(root.right.right) > get_height(root.right.left): + right_right = get_height(root.right.right) if root.right else 0 + right_left = get_height(root.right.left) if root.right else 0 + if right_right > right_left: root = left_rotation(root) else: root = rl_rotation(root) - elif right_height - left_height == -2: - if get_height(root.left.left) > get_height(root.left.right): + elif left_height - right_height == 2: + left_left = get_height(root.left.left) if root.left else 0 + left_right = get_height(root.left.right) if root.left else 0 + if left_left > left_right: root = right_rotation(root) else: root = lr_rotation(root) - + root.height = my_max(get_height(root.right), get_height(root.left)) + 1 return root - - class AVLtree: - def __init__(self) -> None: - self.root = None - - def get_height(self) -> int: + __slots__ = ("root",) + + def __init__(self) -> None: + self.root: MyNode | None = None + + def get_height(self) -> int: return get_height(self.root) - + def insert(self, data: Any) -> None: - print(f"insert:{data}") self.root = insert_node(self.root, data) - + def del_node(self, data: Any) -> None: - print(f"delete:{data}") - if not self.root: - return self.root = del_node(self.root, data) - + def __str__(self) -> str: - if not self.root: + if self.root is None: return "" - q, output, layer, cnt = MyQueue(), "", self.get_height(), 0 - q.push(self.root) - - while not q.is_empty(): - node = q.pop() - space = " " * int(2 ** (layer - 1)) - output += space + (str(node.data) if node else "*") + space - cnt += 1 - - if node: - q.push(node.left) - q.push(node.right) + + levels = [] + queue = [self.root] + + while queue: + current = [] + next_level = [] + + for node in queue: + if node: + current.append(str(node.data)) + next_level.append(node.left) + next_level.append(node.right) + else: + current.append("*") + next_level.append(None) + next_level.append(None) + + if any(node is not None for node in next_level): + levels.append(" ".join(current)) + queue = next_level else: - q.push(None) - q.push(None) - - for i in range(10): - if cnt == 2**i - 1: - layer -= 1 - output += "\n" - if layer == 0: - break - break - - return output + "\n" + "*" * 36 - - -def _test() -> None: - doctest.testmod() - + break + + return "\n".join(levels) + "\n" + "*"*36 -if __name__ == "__main__": - _test() +def test_avl_tree() -> None: t = AVLtree() lst = list(range(10)) random.shuffle(lst) - + for i in lst: t.insert(i) - print(t) - + random.shuffle(lst) for i in lst: t.del_node(i) - print(t) + +if __name__ == "__main__": + test_avl_tree() From 5ea1b4adba6186b9421062b8d915efc6d77b1024 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 22 Jun 2025 03:40:22 +0000 Subject: [PATCH 043/107] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- data_structures/binary_tree/avl_tree.py | 77 +++++++++++++++---------- 1 file changed, 48 insertions(+), 29 deletions(-) diff --git a/data_structures/binary_tree/avl_tree.py b/data_structures/binary_tree/avl_tree.py index 82a11b95355f..c0c8b9cadd04 100644 --- a/data_structures/binary_tree/avl_tree.py +++ b/data_structures/binary_tree/avl_tree.py @@ -3,6 +3,7 @@ import random from typing import Any + class MyQueue: def __init__(self) -> None: self.data: list[Any] = [] @@ -18,57 +19,69 @@ def push(self, data: Any) -> None: def pop(self) -> Any: return self.data[self.head] + class MyNode: __slots__ = ("data", "left", "right", "height") - + def __init__(self, data: Any) -> None: self.data = data self.left: MyNode | None = None self.right: MyNode | None = None self.height = 1 -def get_height(node: MyNode | None) -> int: + +def get_height(node: MyNode | None) -> int: return node.height if node else 0 -def my_max(a: int, b: int) -> int: + +def my_max(a: int, b: int) -> int: return a if a > b else b + def right_rotation(node: MyNode) -> MyNode: left_child = node.left if left_child is None: return node - + node.left = left_child.right left_child.right = node node.height = my_max(get_height(node.right), get_height(node.left)) + 1 - left_child.height = my_max(get_height(left_child.right), get_height(left_child.left)) + 1 + left_child.height = ( + my_max(get_height(left_child.right), get_height(left_child.left)) + 1 + ) return left_child + def left_rotation(node: MyNode) -> MyNode: right_child = node.right if right_child is None: return node - + node.right = right_child.left right_child.left = node node.height = my_max(get_height(node.right), get_height(node.left)) + 1 - right_child.height = my_max(get_height(right_child.right), get_height(right_child.left)) + 1 + right_child.height = ( + my_max(get_height(right_child.right), get_height(right_child.left)) + 1 + ) return right_child + def lr_rotation(node: MyNode) -> MyNode: if node.left: node.left = left_rotation(node.left) return right_rotation(node) + def rl_rotation(node: MyNode) -> MyNode: if node.right: node.right = right_rotation(node.right) return left_rotation(node) + def insert_node(node: MyNode | None, data: Any) -> MyNode | None: if node is None: return MyNode(data) - + if data < node.data: node.left = insert_node(node.left, data) if get_height(node.left) - get_height(node.right) == 2: @@ -83,19 +96,21 @@ def insert_node(node: MyNode | None, data: Any) -> MyNode | None: node = rl_rotation(node) else: node = left_rotation(node) - + node.height = my_max(get_height(node.right), get_height(node.left)) + 1 return node + def get_left_most(root: MyNode) -> Any: while root.left: root = root.left return root.data + def del_node(root: MyNode | None, data: Any) -> MyNode | None: if root is None: return None - + if data == root.data: if root.left and root.right: root.data = get_left_most(root.right) @@ -106,14 +121,14 @@ def del_node(root: MyNode | None, data: Any) -> MyNode | None: root.left = del_node(root.left, data) else: root.right = del_node(root.right, data) - + if root.left is None and root.right is None: root.height = 1 return root - + left_height = get_height(root.left) right_height = get_height(root.right) - + if right_height - left_height == 2: right_right = get_height(root.right.right) if root.right else 0 right_left = get_height(root.right.left) if root.right else 0 @@ -128,35 +143,37 @@ def del_node(root: MyNode | None, data: Any) -> MyNode | None: root = right_rotation(root) else: root = lr_rotation(root) - + root.height = my_max(get_height(root.right), get_height(root.left)) + 1 return root + + class AVLtree: __slots__ = ("root",) - - def __init__(self) -> None: + + def __init__(self) -> None: self.root: MyNode | None = None - - def get_height(self) -> int: + + def get_height(self) -> int: return get_height(self.root) - + def insert(self, data: Any) -> None: self.root = insert_node(self.root, data) - + def del_node(self, data: Any) -> None: self.root = del_node(self.root, data) - + def __str__(self) -> str: if self.root is None: return "" - + levels = [] queue = [self.root] - + while queue: current = [] next_level = [] - + for node in queue: if node: current.append(str(node.data)) @@ -166,26 +183,28 @@ def __str__(self) -> str: current.append("*") next_level.append(None) next_level.append(None) - + if any(node is not None for node in next_level): levels.append(" ".join(current)) queue = next_level else: break - - return "\n".join(levels) + "\n" + "*"*36 + + return "\n".join(levels) + "\n" + "*" * 36 + def test_avl_tree() -> None: t = AVLtree() lst = list(range(10)) random.shuffle(lst) - + for i in lst: t.insert(i) - + random.shuffle(lst) for i in lst: t.del_node(i) + if __name__ == "__main__": test_avl_tree() From b002fc988465376c7051dbefba474308019a6237 Mon Sep 17 00:00:00 2001 From: lighting9999 Date: Sun, 22 Jun 2025 11:45:35 +0800 Subject: [PATCH 044/107] Update avl_tree.py --- data_structures/binary_tree/avl_tree.py | 117 ++++++++++-------------- 1 file changed, 50 insertions(+), 67 deletions(-) diff --git a/data_structures/binary_tree/avl_tree.py b/data_structures/binary_tree/avl_tree.py index c0c8b9cadd04..2c95df3d5824 100644 --- a/data_structures/binary_tree/avl_tree.py +++ b/data_structures/binary_tree/avl_tree.py @@ -3,8 +3,9 @@ import random from typing import Any - class MyQueue: + __slots__ = ("data", "head", "tail") + def __init__(self) -> None: self.data: list[Any] = [] self.head = self.tail = 0 @@ -17,71 +18,61 @@ def push(self, data: Any) -> None: self.tail += 1 def pop(self) -> Any: - return self.data[self.head] - + ret = self.data[self.head] + self.head += 1 + return ret class MyNode: - __slots__ = ("data", "left", "right", "height") - + __slots__ = ("data", "height", "left", "right") # 按字母顺序排序 + def __init__(self, data: Any) -> None: self.data = data + self.height = 1 self.left: MyNode | None = None self.right: MyNode | None = None - self.height = 1 - -def get_height(node: MyNode | None) -> int: +def get_height(node: MyNode | None) -> int: return node.height if node else 0 - -def my_max(a: int, b: int) -> int: +def my_max(a: int, b: int) -> int: return a if a > b else b - def right_rotation(node: MyNode) -> MyNode: left_child = node.left if left_child is None: return node - + node.left = left_child.right left_child.right = node node.height = my_max(get_height(node.right), get_height(node.left)) + 1 - left_child.height = ( - my_max(get_height(left_child.right), get_height(left_child.left)) + 1 - ) + left_child.height = my_max(get_height(left_child.right), get_height(left_child.left)) + 1 return left_child - def left_rotation(node: MyNode) -> MyNode: right_child = node.right if right_child is None: return node - + node.right = right_child.left right_child.left = node node.height = my_max(get_height(node.right), get_height(node.left)) + 1 - right_child.height = ( - my_max(get_height(right_child.right), get_height(right_child.left)) + 1 - ) + right_child.height = my_max(get_height(right_child.right), get_height(right_child.left)) + 1 return right_child - def lr_rotation(node: MyNode) -> MyNode: if node.left: node.left = left_rotation(node.left) return right_rotation(node) - def rl_rotation(node: MyNode) -> MyNode: if node.right: node.right = right_rotation(node.right) return left_rotation(node) - def insert_node(node: MyNode | None, data: Any) -> MyNode | None: if node is None: return MyNode(data) - + if data < node.data: node.left = insert_node(node.left, data) if get_height(node.left) - get_height(node.right) == 2: @@ -96,22 +87,19 @@ def insert_node(node: MyNode | None, data: Any) -> MyNode | None: node = rl_rotation(node) else: node = left_rotation(node) - + node.height = my_max(get_height(node.right), get_height(node.left)) + 1 return node - def get_left_most(root: MyNode) -> Any: while root.left: root = root.left return root.data - def del_node(root: MyNode | None, data: Any) -> MyNode | None: if root is None: return None - - if data == root.data: + if data == root.data: if root.left and root.right: root.data = get_left_most(root.right) root.right = del_node(root.right, root.data) @@ -121,59 +109,55 @@ def del_node(root: MyNode | None, data: Any) -> MyNode | None: root.left = del_node(root.left, data) else: root.right = del_node(root.right, data) - + if root.left is None and root.right is None: root.height = 1 return root - + left_height = get_height(root.left) right_height = get_height(root.right) - + if right_height - left_height == 2: right_right = get_height(root.right.right) if root.right else 0 right_left = get_height(root.right.left) if root.right else 0 - if right_right > right_left: - root = left_rotation(root) - else: - root = rl_rotation(root) + # 使用三元表达式 + root = left_rotation(root) if right_right > right_left else rl_rotation(root) elif left_height - right_height == 2: left_left = get_height(root.left.left) if root.left else 0 left_right = get_height(root.left.right) if root.left else 0 - if left_left > left_right: - root = right_rotation(root) - else: - root = lr_rotation(root) - + # 使用三元表达式 + root = right_rotation(root) if left_left > left_right else lr_rotation(root) + root.height = my_max(get_height(root.right), get_height(root.left)) + 1 return root - -class AVLtree: +class AVLTree: __slots__ = ("root",) - - def __init__(self) -> None: + + def __init__(self) -> None: self.root: MyNode | None = None - - def get_height(self) -> int: + + def get_height(self) -> int: return get_height(self.root) - + def insert(self, data: Any) -> None: self.root = insert_node(self.root, data) - - def del_node(self, data: Any) -> None: + + def delete(self, data: Any) -> None: self.root = del_node(self.root, data) - + def __str__(self) -> str: if self.root is None: return "" - + levels = [] - queue = [self.root] - + # 明确指定类型为 MyNode | None + queue: list[MyNode | None] = [self.root] + while queue: current = [] - next_level = [] - + next_level: list[MyNode | None] = [] + for node in queue: if node: current.append(str(node.data)) @@ -181,30 +165,29 @@ def __str__(self) -> str: next_level.append(node.right) else: current.append("*") - next_level.append(None) - next_level.append(None) - + next_level.extend([None, None]) + if any(node is not None for node in next_level): levels.append(" ".join(current)) queue = next_level else: + if current: # 添加最后一行 + levels.append(" ".join(current)) break - - return "\n".join(levels) + "\n" + "*" * 36 - + + return "\n".join(levels) + "\n" + "*"*36 def test_avl_tree() -> None: - t = AVLtree() + t = AVLTree() lst = list(range(10)) random.shuffle(lst) - + for i in lst: t.insert(i) - + random.shuffle(lst) for i in lst: - t.del_node(i) - + t.delete(i) if __name__ == "__main__": test_avl_tree() From 4930f1b36c816ac681d2b0b5b8d090f9ed3aecce Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 22 Jun 2025 03:45:47 +0000 Subject: [PATCH 045/107] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- data_structures/binary_tree/avl_tree.py | 52 ++++++++++++------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/data_structures/binary_tree/avl_tree.py b/data_structures/binary_tree/avl_tree.py index 2c95df3d5824..a80c59bbb0be 100644 --- a/data_structures/binary_tree/avl_tree.py +++ b/data_structures/binary_tree/avl_tree.py @@ -5,7 +5,7 @@ class MyQueue: __slots__ = ("data", "head", "tail") - + def __init__(self) -> None: self.data: list[Any] = [] self.head = self.tail = 0 @@ -24,24 +24,24 @@ def pop(self) -> Any: class MyNode: __slots__ = ("data", "height", "left", "right") # 按字母顺序排序 - + def __init__(self, data: Any) -> None: self.data = data self.height = 1 self.left: MyNode | None = None self.right: MyNode | None = None -def get_height(node: MyNode | None) -> int: +def get_height(node: MyNode | None) -> int: return node.height if node else 0 -def my_max(a: int, b: int) -> int: +def my_max(a: int, b: int) -> int: return a if a > b else b def right_rotation(node: MyNode) -> MyNode: left_child = node.left if left_child is None: return node - + node.left = left_child.right left_child.right = node node.height = my_max(get_height(node.right), get_height(node.left)) + 1 @@ -52,7 +52,7 @@ def left_rotation(node: MyNode) -> MyNode: right_child = node.right if right_child is None: return node - + node.right = right_child.left right_child.left = node node.height = my_max(get_height(node.right), get_height(node.left)) + 1 @@ -72,7 +72,7 @@ def rl_rotation(node: MyNode) -> MyNode: def insert_node(node: MyNode | None, data: Any) -> MyNode | None: if node is None: return MyNode(data) - + if data < node.data: node.left = insert_node(node.left, data) if get_height(node.left) - get_height(node.right) == 2: @@ -87,7 +87,7 @@ def insert_node(node: MyNode | None, data: Any) -> MyNode | None: node = rl_rotation(node) else: node = left_rotation(node) - + node.height = my_max(get_height(node.right), get_height(node.left)) + 1 return node @@ -109,14 +109,14 @@ def del_node(root: MyNode | None, data: Any) -> MyNode | None: root.left = del_node(root.left, data) else: root.right = del_node(root.right, data) - + if root.left is None and root.right is None: root.height = 1 return root - + left_height = get_height(root.left) right_height = get_height(root.right) - + if right_height - left_height == 2: right_right = get_height(root.right.right) if root.right else 0 right_left = get_height(root.right.left) if root.right else 0 @@ -127,37 +127,37 @@ def del_node(root: MyNode | None, data: Any) -> MyNode | None: left_right = get_height(root.left.right) if root.left else 0 # 使用三元表达式 root = right_rotation(root) if left_left > left_right else lr_rotation(root) - + root.height = my_max(get_height(root.right), get_height(root.left)) + 1 return root class AVLTree: __slots__ = ("root",) - - def __init__(self) -> None: + + def __init__(self) -> None: self.root: MyNode | None = None - - def get_height(self) -> int: + + def get_height(self) -> int: return get_height(self.root) - + def insert(self, data: Any) -> None: self.root = insert_node(self.root, data) - + def delete(self, data: Any) -> None: self.root = del_node(self.root, data) - + def __str__(self) -> str: if self.root is None: return "" - + levels = [] # 明确指定类型为 MyNode | None queue: list[MyNode | None] = [self.root] - + while queue: current = [] next_level: list[MyNode | None] = [] - + for node in queue: if node: current.append(str(node.data)) @@ -166,7 +166,7 @@ def __str__(self) -> str: else: current.append("*") next_level.extend([None, None]) - + if any(node is not None for node in next_level): levels.append(" ".join(current)) queue = next_level @@ -174,17 +174,17 @@ def __str__(self) -> str: if current: # 添加最后一行 levels.append(" ".join(current)) break - + return "\n".join(levels) + "\n" + "*"*36 def test_avl_tree() -> None: t = AVLTree() lst = list(range(10)) random.shuffle(lst) - + for i in lst: t.insert(i) - + random.shuffle(lst) for i in lst: t.delete(i) From 954ae88ffec4ca88df9a0efcb99a872a4d9f29b7 Mon Sep 17 00:00:00 2001 From: lighting9999 Date: Sun, 22 Jun 2025 11:53:15 +0800 Subject: [PATCH 046/107] Update avl_tree.py --- data_structures/binary_tree/avl_tree.py | 109 +++++++++++++++--------- 1 file changed, 68 insertions(+), 41 deletions(-) diff --git a/data_structures/binary_tree/avl_tree.py b/data_structures/binary_tree/avl_tree.py index a80c59bbb0be..41469579f995 100644 --- a/data_structures/binary_tree/avl_tree.py +++ b/data_structures/binary_tree/avl_tree.py @@ -5,7 +5,7 @@ class MyQueue: __slots__ = ("data", "head", "tail") - + def __init__(self) -> None: self.data: list[Any] = [] self.head = self.tail = 0 @@ -23,40 +23,63 @@ def pop(self) -> Any: return ret class MyNode: - __slots__ = ("data", "height", "left", "right") # 按字母顺序排序 - + __slots__ = ("data", "height", "left", "right") + def __init__(self, data: Any) -> None: self.data = data self.height = 1 self.left: MyNode | None = None self.right: MyNode | None = None -def get_height(node: MyNode | None) -> int: +def get_height(node: MyNode | None) -> int: return node.height if node else 0 -def my_max(a: int, b: int) -> int: +def my_max(a: int, b: int) -> int: return a if a > b else b def right_rotation(node: MyNode) -> MyNode: left_child = node.left if left_child is None: return node - + node.left = left_child.right left_child.right = node - node.height = my_max(get_height(node.right), get_height(node.left)) + 1 - left_child.height = my_max(get_height(left_child.right), get_height(left_child.left)) + 1 + + # 拆分长表达式 + node_height = my_max( + get_height(node.right), + get_height(node.left) + ) + 1 + node.height = node_height + + left_height = my_max( + get_height(left_child.right), + get_height(left_child.left) + ) + 1 + left_child.height = left_height + return left_child - def left_rotation(node: MyNode) -> MyNode: right_child = node.right if right_child is None: return node - + node.right = right_child.left right_child.left = node - node.height = my_max(get_height(node.right), get_height(node.left)) + 1 - right_child.height = my_max(get_height(right_child.right), get_height(right_child.left)) + 1 + + # 拆分长表达式 + node_height = my_max( + get_height(node.right), + get_height(node.left) + ) + 1 + node.height = node_height + + right_height = my_max( + get_height(right_child.right), + get_height(right_child.left) + ) + 1 + right_child.height = right_height + return right_child def lr_rotation(node: MyNode) -> MyNode: @@ -72,7 +95,7 @@ def rl_rotation(node: MyNode) -> MyNode: def insert_node(node: MyNode | None, data: Any) -> MyNode | None: if node is None: return MyNode(data) - + if data < node.data: node.left = insert_node(node.left, data) if get_height(node.left) - get_height(node.right) == 2: @@ -87,8 +110,11 @@ def insert_node(node: MyNode | None, data: Any) -> MyNode | None: node = rl_rotation(node) else: node = left_rotation(node) - - node.height = my_max(get_height(node.right), get_height(node.left)) + 1 + + node.height = my_max( + get_height(node.right), + get_height(node.left) + ) + 1 return node def get_left_most(root: MyNode) -> Any: @@ -99,7 +125,8 @@ def get_left_most(root: MyNode) -> Any: def del_node(root: MyNode | None, data: Any) -> MyNode | None: if root is None: return None - if data == root.data: + + if data == root.data: if root.left and root.right: root.data = get_left_most(root.right) root.right = del_node(root.right, root.data) @@ -109,55 +136,54 @@ def del_node(root: MyNode | None, data: Any) -> MyNode | None: root.left = del_node(root.left, data) else: root.right = del_node(root.right, data) - + if root.left is None and root.right is None: root.height = 1 return root - + left_height = get_height(root.left) right_height = get_height(root.right) - + if right_height - left_height == 2: right_right = get_height(root.right.right) if root.right else 0 right_left = get_height(root.right.left) if root.right else 0 - # 使用三元表达式 root = left_rotation(root) if right_right > right_left else rl_rotation(root) elif left_height - right_height == 2: left_left = get_height(root.left.left) if root.left else 0 left_right = get_height(root.left.right) if root.left else 0 - # 使用三元表达式 root = right_rotation(root) if left_left > left_right else lr_rotation(root) - - root.height = my_max(get_height(root.right), get_height(root.left)) + 1 + + root.height = my_max( + get_height(root.right), + get_height(root.left) + ) + 1 return root - class AVLTree: __slots__ = ("root",) - - def __init__(self) -> None: + + def __init__(self) -> None: self.root: MyNode | None = None - - def get_height(self) -> int: + + def get_height(self) -> int: return get_height(self.root) - + def insert(self, data: Any) -> None: self.root = insert_node(self.root, data) - + def delete(self, data: Any) -> None: self.root = del_node(self.root, data) - + def __str__(self) -> str: if self.root is None: return "" - + levels = [] - # 明确指定类型为 MyNode | None queue: list[MyNode | None] = [self.root] - + while queue: current = [] next_level: list[MyNode | None] = [] - + for node in queue: if node: current.append(str(node.data)) @@ -165,26 +191,27 @@ def __str__(self) -> str: next_level.append(node.right) else: current.append("*") - next_level.extend([None, None]) - + next_level.append(None) + next_level.append(None) + if any(node is not None for node in next_level): levels.append(" ".join(current)) queue = next_level else: - if current: # 添加最后一行 + if current: levels.append(" ".join(current)) break - + return "\n".join(levels) + "\n" + "*"*36 def test_avl_tree() -> None: t = AVLTree() lst = list(range(10)) random.shuffle(lst) - + for i in lst: t.insert(i) - + random.shuffle(lst) for i in lst: t.delete(i) From b66c9e051cf63a59f857d4b7cc7599251f71d870 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 22 Jun 2025 03:53:36 +0000 Subject: [PATCH 047/107] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- data_structures/binary_tree/avl_tree.py | 116 ++++++++++++------------ 1 file changed, 58 insertions(+), 58 deletions(-) diff --git a/data_structures/binary_tree/avl_tree.py b/data_structures/binary_tree/avl_tree.py index 41469579f995..482e8d40328b 100644 --- a/data_structures/binary_tree/avl_tree.py +++ b/data_structures/binary_tree/avl_tree.py @@ -3,9 +3,10 @@ import random from typing import Any + class MyQueue: __slots__ = ("data", "head", "tail") - + def __init__(self) -> None: self.data: list[Any] = [] self.head = self.tail = 0 @@ -22,80 +23,79 @@ def pop(self) -> Any: self.head += 1 return ret + class MyNode: __slots__ = ("data", "height", "left", "right") - + def __init__(self, data: Any) -> None: self.data = data self.height = 1 self.left: MyNode | None = None self.right: MyNode | None = None -def get_height(node: MyNode | None) -> int: + +def get_height(node: MyNode | None) -> int: return node.height if node else 0 -def my_max(a: int, b: int) -> int: + +def my_max(a: int, b: int) -> int: return a if a > b else b + def right_rotation(node: MyNode) -> MyNode: left_child = node.left if left_child is None: return node - + node.left = left_child.right left_child.right = node - + # 拆分长表达式 - node_height = my_max( - get_height(node.right), - get_height(node.left) - ) + 1 + node_height = my_max(get_height(node.right), get_height(node.left)) + 1 node.height = node_height - - left_height = my_max( - get_height(left_child.right), - get_height(left_child.left) - ) + 1 + + left_height = my_max(get_height(left_child.right), get_height(left_child.left)) + 1 left_child.height = left_height - + return left_child + + def left_rotation(node: MyNode) -> MyNode: right_child = node.right if right_child is None: return node - + node.right = right_child.left right_child.left = node - + # 拆分长表达式 - node_height = my_max( - get_height(node.right), - get_height(node.left) - ) + 1 + node_height = my_max(get_height(node.right), get_height(node.left)) + 1 node.height = node_height - - right_height = my_max( - get_height(right_child.right), - get_height(right_child.left) - ) + 1 + + right_height = ( + my_max(get_height(right_child.right), get_height(right_child.left)) + 1 + ) right_child.height = right_height - + return right_child + def lr_rotation(node: MyNode) -> MyNode: if node.left: node.left = left_rotation(node.left) return right_rotation(node) + def rl_rotation(node: MyNode) -> MyNode: if node.right: node.right = right_rotation(node.right) return left_rotation(node) + def insert_node(node: MyNode | None, data: Any) -> MyNode | None: if node is None: return MyNode(data) - + if data < node.data: node.left = insert_node(node.left, data) if get_height(node.left) - get_height(node.right) == 2: @@ -110,22 +110,21 @@ def insert_node(node: MyNode | None, data: Any) -> MyNode | None: node = rl_rotation(node) else: node = left_rotation(node) - - node.height = my_max( - get_height(node.right), - get_height(node.left) - ) + 1 + + node.height = my_max(get_height(node.right), get_height(node.left)) + 1 return node + def get_left_most(root: MyNode) -> Any: while root.left: root = root.left return root.data + def del_node(root: MyNode | None, data: Any) -> MyNode | None: if root is None: return None - + if data == root.data: if root.left and root.right: root.data = get_left_most(root.right) @@ -136,14 +135,14 @@ def del_node(root: MyNode | None, data: Any) -> MyNode | None: root.left = del_node(root.left, data) else: root.right = del_node(root.right, data) - + if root.left is None and root.right is None: root.height = 1 return root - + left_height = get_height(root.left) right_height = get_height(root.right) - + if right_height - left_height == 2: right_right = get_height(root.right.right) if root.right else 0 right_left = get_height(root.right.left) if root.right else 0 @@ -152,38 +151,37 @@ def del_node(root: MyNode | None, data: Any) -> MyNode | None: left_left = get_height(root.left.left) if root.left else 0 left_right = get_height(root.left.right) if root.left else 0 root = right_rotation(root) if left_left > left_right else lr_rotation(root) - - root.height = my_max( - get_height(root.right), - get_height(root.left) - ) + 1 + + root.height = my_max(get_height(root.right), get_height(root.left)) + 1 return root + + class AVLTree: __slots__ = ("root",) - - def __init__(self) -> None: + + def __init__(self) -> None: self.root: MyNode | None = None - - def get_height(self) -> int: + + def get_height(self) -> int: return get_height(self.root) - + def insert(self, data: Any) -> None: self.root = insert_node(self.root, data) - + def delete(self, data: Any) -> None: self.root = del_node(self.root, data) - + def __str__(self) -> str: if self.root is None: return "" - + levels = [] queue: list[MyNode | None] = [self.root] - + while queue: current = [] next_level: list[MyNode | None] = [] - + for node in queue: if node: current.append(str(node.data)) @@ -193,7 +191,7 @@ def __str__(self) -> str: current.append("*") next_level.append(None) next_level.append(None) - + if any(node is not None for node in next_level): levels.append(" ".join(current)) queue = next_level @@ -201,20 +199,22 @@ def __str__(self) -> str: if current: levels.append(" ".join(current)) break - - return "\n".join(levels) + "\n" + "*"*36 + + return "\n".join(levels) + "\n" + "*" * 36 + def test_avl_tree() -> None: t = AVLTree() lst = list(range(10)) random.shuffle(lst) - + for i in lst: t.insert(i) - + random.shuffle(lst) for i in lst: t.delete(i) + if __name__ == "__main__": test_avl_tree() From 8381edff1a700794f67376c7c20b93607af3d3a6 Mon Sep 17 00:00:00 2001 From: lighting9999 Date: Sun, 22 Jun 2025 12:01:27 +0800 Subject: [PATCH 048/107] Update stack_with_doubly_linked_list.py --- .../stacks/stack_with_doubly_linked_list.py | 99 ++++++++----------- 1 file changed, 40 insertions(+), 59 deletions(-) diff --git a/data_structures/stacks/stack_with_doubly_linked_list.py b/data_structures/stacks/stack_with_doubly_linked_list.py index 50c5236e073c..c04fa2a410fb 100644 --- a/data_structures/stacks/stack_with_doubly_linked_list.py +++ b/data_structures/stacks/stack_with_doubly_linked_list.py @@ -1,22 +1,17 @@ -# A complete working Python program to demonstrate all -# stack operations using a doubly linked list - +# Complete Python program demonstrating stack operations using a doubly linked list from __future__ import annotations -from typing import Generic, TypeVar - -T = TypeVar("T") - - -class Node(Generic[T]): +class Node[T]: + """Node class for doubly linked list""" def __init__(self, data: T): - self.data = data # Assign data - self.next: Node[T] | None = None # Initialize next as null - self.prev: Node[T] | None = None # Initialize prev as null + self.data = data # Node data + self.next: Node[T] | None = None # Reference to next node + self.prev: Node[T] | None = None # Reference to previous node - -class Stack(Generic[T]): +class Stack[T]: """ + Stack implementation using doubly linked list + >>> stack = Stack() >>> stack.is_empty() True @@ -42,89 +37,75 @@ class Stack(Generic[T]): """ def __init__(self) -> None: - self.head: Node[T] | None = None + self.head: Node[T] | None = None # Top of stack def push(self, data: T) -> None: - """add a Node to the stack""" + """Push element onto stack""" if self.head is None: self.head = Node(data) else: new_node = Node(data) + # Insert new node at head self.head.prev = new_node new_node.next = self.head - new_node.prev = None self.head = new_node def pop(self) -> T | None: - """pop the top element off the stack""" + """Pop element from top of stack""" if self.head is None: return None - else: - assert self.head is not None - temp = self.head.data - self.head = self.head.next - if self.head is not None: - self.head.prev = None - return temp + + # Remove and return head node data + temp = self.head.data + self.head = self.head.next + if self.head is not None: + self.head.prev = None # Clear prev reference for new head + return temp def top(self) -> T | None: - """return the top element of the stack""" + """Peek at top element without removing""" return self.head.data if self.head is not None else None def __len__(self) -> int: - temp = self.head + """Return number of elements in stack""" count = 0 - while temp is not None: + current = self.head + while current: count += 1 - temp = temp.next + current = current.next return count def is_empty(self) -> bool: + """Check if stack is empty""" return self.head is None def print_stack(self) -> None: + """Print all stack elements""" print("stack elements are:") - temp = self.head - while temp is not None: - print(temp.data, end="->") - temp = temp.next - + current = self.head + while current: + print(current.data, end="->") + current = current.next -# Code execution starts here +# Program entry point if __name__ == "__main__": - # Start with the empty stack - stack: Stack[int] = Stack() + stack: Stack[int] = Stack() # Create integer stack - # Insert 4 at the beginning. So stack becomes 4->None print("Stack operations using Doubly LinkedList") + # Push elements onto stack stack.push(4) - - # Insert 5 at the beginning. So stack becomes 4->5->None stack.push(5) - - # Insert 6 at the beginning. So stack becomes 4->5->6->None stack.push(6) - - # Insert 7 at the beginning. So stack becomes 4->5->6->7->None stack.push(7) - # Print the stack - stack.print_stack() + stack.print_stack() # Print current stack - # Print the top element - print("\nTop element is ", stack.top()) + print("\nTop element is", stack.top()) # Show top element + print("Size of stack is", len(stack)) # Show size - # Print the stack size - print("Size of the stack is ", len(stack)) - - # pop the top element + # Pop two elements stack.pop() - - # pop the top element stack.pop() - # two elements have now been popped off - stack.print_stack() - - # Print True if the stack is empty else False - print("\nstack is empty:", stack.is_empty()) + stack.print_stack() # Print modified stack + print("\nStack is empty:", stack.is_empty()) # Check emptiness From 98a5a16de4f6403641b81cd6854f09a2f3cc874f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 22 Jun 2025 04:01:51 +0000 Subject: [PATCH 049/107] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- data_structures/stacks/stack_with_doubly_linked_list.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/data_structures/stacks/stack_with_doubly_linked_list.py b/data_structures/stacks/stack_with_doubly_linked_list.py index c04fa2a410fb..d173d53f7bd2 100644 --- a/data_structures/stacks/stack_with_doubly_linked_list.py +++ b/data_structures/stacks/stack_with_doubly_linked_list.py @@ -1,17 +1,20 @@ # Complete Python program demonstrating stack operations using a doubly linked list from __future__ import annotations + class Node[T]: """Node class for doubly linked list""" + def __init__(self, data: T): self.data = data # Node data self.next: Node[T] | None = None # Reference to next node self.prev: Node[T] | None = None # Reference to previous node + class Stack[T]: """ Stack implementation using doubly linked list - + >>> stack = Stack() >>> stack.is_empty() True @@ -54,7 +57,7 @@ def pop(self) -> T | None: """Pop element from top of stack""" if self.head is None: return None - + # Remove and return head node data temp = self.head.data self.head = self.head.next @@ -87,6 +90,7 @@ def print_stack(self) -> None: print(current.data, end="->") current = current.next + # Program entry point if __name__ == "__main__": stack: Stack[int] = Stack() # Create integer stack From 92b7060041cb40dea5b5cf97438968ea151f7d50 Mon Sep 17 00:00:00 2001 From: lighting9999 Date: Sun, 22 Jun 2025 12:13:02 +0800 Subject: [PATCH 050/107] Update binary_search_tree.py --- data_structures/binary_tree/binary_search_tree.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/data_structures/binary_tree/binary_search_tree.py b/data_structures/binary_tree/binary_search_tree.py index 3f214d0113a4..fdb034ecffc8 100644 --- a/data_structures/binary_tree/binary_search_tree.py +++ b/data_structures/binary_tree/binary_search_tree.py @@ -88,9 +88,8 @@ >>> not t True """ - from __future__ import annotations - +from pprint import pformat # Moved to top-level from collections.abc import Iterable, Iterator from dataclasses import dataclass from typing import Any, Self @@ -115,8 +114,6 @@ def __iter__(self) -> Iterator[int]: yield from self.right or [] def __repr__(self) -> str: - from pprint import pformat - if self.left is None and self.right is None: return str(self.value) return pformat({f"{self.value}": (self.left, self.right)}, indent=1) @@ -196,12 +193,11 @@ def insert(self, *values) -> Self: for value in values: self.__insert(value) return self - - def search(self, value) -> Node | None: + 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)})})})} + {'10': (None, {'20': (None, {'30': (None, {'40': (None, 50)})})} >>> tree.search(20) {'20': (None, {'30': (None, {'40': (None, 50)})})} >>> tree.search(30) @@ -314,8 +310,7 @@ def traversal_tree(self, traversal_function=None) -> Any: return self.preorder_traverse(self.root) else: return traversal_function(self.root) - - def inorder(self, arr: list, node: Node | None) -> None: +def inorder(self, arr: list, node: Node | None) -> None: """Perform an inorder traversal and append values of the nodes to a list named arr""" if node: From 03453507492a4afccc648b10aaf30b667022e7cf Mon Sep 17 00:00:00 2001 From: lighting9999 Date: Sun, 22 Jun 2025 12:29:56 +0800 Subject: [PATCH 051/107] Update binary_search_tree.py --- .../binary_tree/binary_search_tree.py | 324 ++---------------- 1 file changed, 33 insertions(+), 291 deletions(-) diff --git a/data_structures/binary_tree/binary_search_tree.py b/data_structures/binary_tree/binary_search_tree.py index fdb034ecffc8..a0a2a83bca9f 100644 --- a/data_structures/binary_tree/binary_search_tree.py +++ b/data_structures/binary_tree/binary_search_tree.py @@ -1,184 +1,46 @@ -r""" -A binary search Tree - -Example - 8 - / \ - 3 10 - / \ \ - 1 6 14 - / \ / - 4 7 13 - ->>> 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 - ->>> 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) -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) # 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) -{'8': ({'3': (1, {'6': (4, 7)})}, {'10': (None, {'14': (13, None)})})} - -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 ->>> t.search(1).is_right -False - ->>> 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 pprint import pformat # Moved to top-level +from pprint import pformat from collections.abc import Iterable, Iterator from dataclasses import dataclass from typing import Any, Self - @dataclass class Node: 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 [] + parent: Node | None = None def __repr__(self) -> str: if self.left is None and self.right is None: return str(self.value) return pformat({f"{self.value}": (self.left, self.right)}, indent=1) - @property - def is_right(self) -> bool: - return bool(self.parent and self is self.parent.right) - - @dataclass class BinarySearchTree: 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: - """ - Return a string of all the Nodes using in order traversal - """ - return str(self.root) - def __reassign_nodes(self, node: Node, new_children: Node | None) -> None: - if new_children is not None: # reset its kids + if new_children is not None: new_children.parent = node.parent - if node.parent is not None: # reset its parent - if node.is_right: # If it is the right child + if node.parent is not None: + if node.is_right: node.parent.right = new_children else: node.parent.left = new_children else: self.root = new_children - def empty(self) -> bool: - """ - 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: - """ - Insert a new node in Binary Search Tree with value label - """ - new_node = Node(value) # create a new Node - if self.empty(): # if Tree is empty - self.root = new_node # set its root - else: # Tree is not empty - parent_node = self.root # from root - if parent_node is None: - return - while True: # While we don't get to a leaf - if value < parent_node.value: # We go left + new_node = Node(value) + if self.empty(): + self.root = new_node + else: + parent_node = self.root + while True: + if value < parent_node.value: if parent_node.left is None: - parent_node.left = new_node # We insert the new node in a leaf + parent_node.left = new_node break else: parent_node = parent_node.left @@ -189,163 +51,43 @@ def __insert(self, value) -> None: parent_node = parent_node.right new_node.parent = parent_node - def insert(self, *values) -> Self: - for value in values: - self.__insert(value) - 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. - """ - + def search(self, value) -> Node | None: if self.empty(): raise IndexError("Warning: Tree is empty! please use another.") - else: - node = self.root - # use lazy evaluation here to avoid NoneType Attribute error - while node is not None and node.value is not value: - node = node.left if value < node.value else node.right - return node - - 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: - return None - node = self.root - - if not self.empty(): - while node.right is not None: - node = node.right - return node - - 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 - if self.root is None: - return None - if not self.empty(): - node = self.root - while node.left is not None: - node = node.left + node = self.root + while node is not None and node.value != value: + node = node.left if value < node.value else node.right return node def remove(self, value: int) -> None: - # Look for the node with that label node = self.search(value) if node is None: - msg = f"Value {value} not found" - raise ValueError(msg) + raise ValueError(f"Value {value} not found") - if node.left is None and node.right is None: # If it has no children + if node.left is None and node.right is None: self.__reassign_nodes(node, None) - elif node.left is None: # Has only right children + elif node.left is None: self.__reassign_nodes(node, node.right) - elif node.right is None: # Has only left children + elif node.right is None: 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[union-attr] - node.value = ( - 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: - if node is not None: - yield node # Preorder Traversal - yield from self.preorder_traverse(node.left) - yield from self.preorder_traverse(node.right) - - def traversal_tree(self, traversal_function=None) -> Any: - """ - This function traversal the tree. - You can pass a function to traversal the tree as needed by client code - """ - if traversal_function is None: - return self.preorder_traverse(self.root) - else: - return traversal_function(self.root) -def inorder(self, arr: list, node: Node | None) -> None: - """Perform an inorder traversal and append values of the nodes to - a list named arr""" - if node: - self.inorder(arr, node.left) - arr.append(node.value) - self.inorder(arr, node.right) - - def find_kth_smallest(self, k: int, node: Node) -> int: - """Return the kth smallest element in a binary search tree""" - arr: list[int] = [] - self.inorder(arr, node) # append all values to list using inorder traversal - return arr[k - 1] - + predecessor = self.get_max(node.left) + self.remove(predecessor.value) + node.value = predecessor.value +# 修复的递归函数 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 - + """Inorder traversal (left, self, right)""" + if curr_node is None: + return [] + return inorder(curr_node.left) + [curr_node] + inorder(curr_node.right) def postorder(curr_node: Node | None) -> list[Node]: - """ - postOrder (left, right, self) - """ - node_list = [] - if curr_node is not None: - node_list = postorder(curr_node.left) + postorder(curr_node.right) + [curr_node] - return node_list - + """Postorder traversal (left, right, self)""" + if curr_node is None: + return [] + return postorder(curr_node.left) + postorder(curr_node.right) + [curr_node] if __name__ == "__main__": import doctest - doctest.testmod(verbose=True) From 57c44acc79efd37c118e7456d9464b56ad801e8b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 22 Jun 2025 04:30:20 +0000 Subject: [PATCH 052/107] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- data_structures/binary_tree/binary_search_tree.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/data_structures/binary_tree/binary_search_tree.py b/data_structures/binary_tree/binary_search_tree.py index a0a2a83bca9f..3973d2022bd4 100644 --- a/data_structures/binary_tree/binary_search_tree.py +++ b/data_structures/binary_tree/binary_search_tree.py @@ -4,6 +4,7 @@ from dataclasses import dataclass from typing import Any, Self + @dataclass class Node: value: int @@ -16,6 +17,7 @@ def __repr__(self) -> str: return str(self.value) return pformat({f"{self.value}": (self.left, self.right)}, indent=1) + @dataclass class BinarySearchTree: root: Node | None = None @@ -75,6 +77,7 @@ def remove(self, value: int) -> None: self.remove(predecessor.value) node.value = predecessor.value + # 修复的递归函数 def inorder(curr_node: Node | None) -> list[Node]: """Inorder traversal (left, self, right)""" @@ -82,12 +85,15 @@ def inorder(curr_node: Node | None) -> list[Node]: return [] return inorder(curr_node.left) + [curr_node] + inorder(curr_node.right) + def postorder(curr_node: Node | None) -> list[Node]: """Postorder traversal (left, right, self)""" if curr_node is None: return [] return postorder(curr_node.left) + postorder(curr_node.right) + [curr_node] + if __name__ == "__main__": import doctest + doctest.testmod(verbose=True) From 149074847769cfe62aea955e1d735c3665b2b6fb Mon Sep 17 00:00:00 2001 From: lighting9999 Date: Sun, 22 Jun 2025 12:41:22 +0800 Subject: [PATCH 053/107] Update binary_search_tree.py --- .../binary_tree/binary_search_tree.py | 222 +++++++++++++++--- 1 file changed, 195 insertions(+), 27 deletions(-) diff --git a/data_structures/binary_tree/binary_search_tree.py b/data_structures/binary_tree/binary_search_tree.py index 3973d2022bd4..cb52a0aecc89 100644 --- a/data_structures/binary_tree/binary_search_tree.py +++ b/data_structures/binary_tree/binary_search_tree.py @@ -1,8 +1,98 @@ +r""" +A binary search Tree + +Example + 8 + / \ + 3 10 + / \ \ + 1 6 14 + / \ / + 4 7 13 + +>>> 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 + +>>> 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) +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) # 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) +{'8': ({'3': (1, {'6': (4, 7)})}, {'10': (None, {'14': (13, None)})})} + +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 +>>> t.search(1).is_right +False + +>>> 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 pprint import pformat -from collections.abc import Iterable, Iterator + from dataclasses import dataclass -from typing import Any, Self +from pprint import pformat +from typing import Iterator @dataclass @@ -10,7 +100,18 @@ class Node: value: int left: Node | None = None right: Node | None = None - parent: Node | None = None + parent: Node | None = None # For easier deletion + + @property + def is_right(self) -> bool: + return bool(self.parent and self is self.parent.right) + + def __iter__(self) -> Iterator[int]: + if self.left: + yield from self.left + yield self.value + if self.right: + yield from self.right def __repr__(self) -> str: if self.left is None and self.right is None: @@ -22,9 +123,21 @@ def __repr__(self) -> str: class BinarySearchTree: root: Node | None = None + def __bool__(self) -> bool: + return self.root is not None + + def __iter__(self) -> Iterator[int]: + if self.root: + yield from self.root + return iter(()) + + def __str__(self) -> str: + return str(self.root) if self.root else "Empty tree" + def __reassign_nodes(self, node: Node, new_children: Node | None) -> None: if new_children is not None: new_children.parent = node.parent + if node.parent is not None: if node.is_right: node.parent.right = new_children @@ -33,38 +146,71 @@ def __reassign_nodes(self, node: Node, new_children: Node | None) -> None: else: self.root = new_children - def __insert(self, value) -> None: + def empty(self) -> bool: + return self.root is None + + def __insert(self, value: int) -> None: new_node = Node(value) if self.empty(): self.root = new_node - else: - parent_node = self.root - while True: - if value < parent_node.value: - if parent_node.left is None: - parent_node.left = new_node - break - else: - parent_node = parent_node.left - elif parent_node.right is None: + return + + parent_node = self.root + while parent_node: + if value < parent_node.value: + if parent_node.left is None: + parent_node.left = new_node + new_node.parent = parent_node + return + parent_node = parent_node.left + else: + if parent_node.right is None: parent_node.right = new_node - break - else: - parent_node = parent_node.right - new_node.parent = parent_node + new_node.parent = parent_node + return + parent_node = parent_node.right - def search(self, value) -> Node | None: + def insert(self, *values: int) -> BinarySearchTree: + for value in values: + self.__insert(value) + return self + + def search(self, value: int) -> Node | None: if self.empty(): raise IndexError("Warning: Tree is empty! please use another.") + node = self.root while node is not None and node.value != value: - node = node.left if value < node.value else node.right + if value < node.value: + node = node.left + else: + node = node.right + return node + + def get_max(self, node: Node | None = None) -> Node | None: + if node is None: + node = self.root + if node is None: + return None + + while node.right is not None: + node = node.right + return node + def get_min(self, node: Node | None = None) -> Node | None: + if node is None: + node = self.root + if node is None: + return None + + while node.left is not None: + node = node.left return node def remove(self, value: int) -> None: node = self.search(value) if node is None: - raise ValueError(f"Value {value} not found") + error_msg = f"Value {value} not found" + raise ValueError(error_msg) if node.left is None and node.right is None: self.__reassign_nodes(node, None) @@ -74,23 +220,45 @@ def remove(self, value: int) -> None: self.__reassign_nodes(node, node.left) else: predecessor = self.get_max(node.left) - self.remove(predecessor.value) - node.value = predecessor.value + if predecessor is not None: + self.remove(predecessor.value) + node.value = predecessor.value + + def preorder_traverse(self, node: Node | None) -> Iterator[Node]: + if node is not None: + yield node + yield from self.preorder_traverse(node.left) + yield from self.preorder_traverse(node.right) + + def traversal_tree(self, traversal_function=None) -> Iterator[Node]: + if traversal_function is None: + return self.preorder_traverse(self.root) + return traversal_function(self.root) + + def inorder(self, arr: list[int], node: Node | None) -> None: + if node: + self.inorder(arr, node.left) + arr.append(node.value) + self.inorder(arr, node.right) + + def find_kth_smallest(self, k: int, node: Node) -> int: + arr: list[int] = [] + self.inorder(arr, node) + return arr[k - 1] -# 修复的递归函数 def inorder(curr_node: Node | None) -> list[Node]: """Inorder traversal (left, self, right)""" if curr_node is None: return [] - return inorder(curr_node.left) + [curr_node] + inorder(curr_node.right) + return [*inorder(curr_node.left), curr_node, *inorder(curr_node.right)] def postorder(curr_node: Node | None) -> list[Node]: """Postorder traversal (left, right, self)""" if curr_node is None: return [] - return postorder(curr_node.left) + postorder(curr_node.right) + [curr_node] + return [*postorder(curr_node.left), *postorder(curr_node.right), curr_node] if __name__ == "__main__": From 0d7a599157cb512a50b3f7a06ffb9ce65745b948 Mon Sep 17 00:00:00 2001 From: lighting9999 Date: Sun, 22 Jun 2025 12:50:33 +0800 Subject: [PATCH 054/107] Update binary_search_tree.py --- data_structures/binary_tree/binary_search_tree.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/data_structures/binary_tree/binary_search_tree.py b/data_structures/binary_tree/binary_search_tree.py index cb52a0aecc89..f3c8e85e3fa9 100644 --- a/data_structures/binary_tree/binary_search_tree.py +++ b/data_structures/binary_tree/binary_search_tree.py @@ -23,7 +23,7 @@ >>> tuple(t)[3-1] 4 ->>> print(" ".join(repr(i.value) for i in t.traversal_tree(postorder)) +>>> 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): @@ -88,6 +88,7 @@ >>> not t True """ + from __future__ import annotations from dataclasses import dataclass @@ -174,7 +175,6 @@ def insert(self, *values: int) -> BinarySearchTree: for value in values: self.__insert(value) return self - def search(self, value: int) -> Node | None: if self.empty(): raise IndexError("Warning: Tree is empty! please use another.") @@ -196,7 +196,8 @@ def get_max(self, node: Node | None = None) -> Node | None: while node.right is not None: node = node.right return node - def get_min(self, node: Node | None = None) -> Node | None: + + def get_min(self, node: Node | None = None) -> Node | None: if node is None: node = self.root if node is None: From d3e8723c96793ec1a5cd4fc9e5b2e964a2f2b6bd Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 22 Jun 2025 04:50:56 +0000 Subject: [PATCH 055/107] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- data_structures/binary_tree/binary_search_tree.py | 1 + 1 file changed, 1 insertion(+) diff --git a/data_structures/binary_tree/binary_search_tree.py b/data_structures/binary_tree/binary_search_tree.py index f3c8e85e3fa9..3c85720bce34 100644 --- a/data_structures/binary_tree/binary_search_tree.py +++ b/data_structures/binary_tree/binary_search_tree.py @@ -175,6 +175,7 @@ def insert(self, *values: int) -> BinarySearchTree: for value in values: self.__insert(value) return self + def search(self, value: int) -> Node | None: if self.empty(): raise IndexError("Warning: Tree is empty! please use another.") From 4c87cb4beb920c700468e86f95e91e4f7a3d0090 Mon Sep 17 00:00:00 2001 From: lighting9999 Date: Sun, 22 Jun 2025 12:57:45 +0800 Subject: [PATCH 056/107] Update binary_search_tree.py --- data_structures/binary_tree/binary_search_tree.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/data_structures/binary_tree/binary_search_tree.py b/data_structures/binary_tree/binary_search_tree.py index 3c85720bce34..df0a0b18bf1b 100644 --- a/data_structures/binary_tree/binary_search_tree.py +++ b/data_structures/binary_tree/binary_search_tree.py @@ -93,7 +93,7 @@ from dataclasses import dataclass from pprint import pformat -from typing import Iterator +from collections.abc import Iterator # fix UP035 @dataclass @@ -182,10 +182,8 @@ def search(self, value: int) -> Node | None: node = self.root while node is not None and node.value != value: - if value < node.value: - node = node.left - else: - node = node.right + # 修复 SIM108:使用三元表达式替代 if-else 块 + node = node.left if value < node.value else node.right return node def get_max(self, node: Node | None = None) -> Node | None: @@ -225,7 +223,6 @@ def remove(self, value: int) -> None: if predecessor is not None: self.remove(predecessor.value) node.value = predecessor.value - def preorder_traverse(self, node: Node | None) -> Iterator[Node]: if node is not None: yield node From ea99904d9ef73eeb7e228bdb4ed92bb81af05792 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 22 Jun 2025 04:58:07 +0000 Subject: [PATCH 057/107] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- data_structures/binary_tree/binary_search_tree.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/data_structures/binary_tree/binary_search_tree.py b/data_structures/binary_tree/binary_search_tree.py index df0a0b18bf1b..e376f62f7726 100644 --- a/data_structures/binary_tree/binary_search_tree.py +++ b/data_structures/binary_tree/binary_search_tree.py @@ -93,7 +93,7 @@ from dataclasses import dataclass from pprint import pformat -from collections.abc import Iterator # fix UP035 +from collections.abc import Iterator # fix UP035 @dataclass @@ -223,6 +223,7 @@ def remove(self, value: int) -> None: if predecessor is not None: self.remove(predecessor.value) node.value = predecessor.value + def preorder_traverse(self, node: Node | None) -> Iterator[Node]: if node is not None: yield node From ac59df6104b1ff5e7f78ad1ff2be443128814bf1 Mon Sep 17 00:00:00 2001 From: lighting9999 Date: Sun, 22 Jun 2025 13:01:53 +0800 Subject: [PATCH 058/107] Update hill_cipher.py --- ciphers/hill_cipher.py | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/ciphers/hill_cipher.py b/ciphers/hill_cipher.py index b5296d99dd42..7fe211c59c34 100644 --- a/ciphers/hill_cipher.py +++ b/ciphers/hill_cipher.py @@ -1,3 +1,41 @@ +""" + + Hill Cipher: + The 'HillCipher' class below implements the Hill Cipher algorithm which uses + modern linear algebra techniques to encode and decode text using an encryption + key matrix. + + Algorithm: + Let the order of the encryption key be N (as it is a square matrix). + Your text is divided into batches of length N and converted to numerical vectors + by a simple mapping starting with A=0 and so on. + + The key is then multiplied with the newly created batch vector to obtain the + encoded vector. After each multiplication modular 36 calculations are performed + on the vectors so as to bring the numbers between 0 and 36 and then mapped with + their corresponding alphanumerics. + + While decrypting, the decrypting key is found which is the inverse of the + encrypting key modular 36. The same process is repeated for decrypting to get + the original message back. + + Constraints: + The determinant of the encryption key matrix must be relatively prime w.r.t 36. + + Note: + This implementation only considers alphanumerics in the text. If the length of + the text to be encrypted is not a multiple of the break key(the length of one + batch of letters), the last character of the text is added to the text until the + length of the text reaches a multiple of the break_key. So the text after + decrypting might be a little different than the original text. + + References: + https://apprendre-en-ligne.net/crypto/hill/Hillciph.pdf + https://www.youtube.com/watch?v=kfmNeskzs2o + https://www.youtube.com/watch?v=4RhLNDqcjpA + + """ + import string import numpy as np From 3ab69f27637cd094b1c1dacfca4fb02e74e051eb Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 22 Jun 2025 05:02:15 +0000 Subject: [PATCH 059/107] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- ciphers/hill_cipher.py | 74 +++++++++++++++++++++--------------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/ciphers/hill_cipher.py b/ciphers/hill_cipher.py index 7fe211c59c34..e27c4a2f1cbc 100644 --- a/ciphers/hill_cipher.py +++ b/ciphers/hill_cipher.py @@ -1,40 +1,40 @@ -""" - - Hill Cipher: - The 'HillCipher' class below implements the Hill Cipher algorithm which uses - modern linear algebra techniques to encode and decode text using an encryption - key matrix. - - Algorithm: - Let the order of the encryption key be N (as it is a square matrix). - Your text is divided into batches of length N and converted to numerical vectors - by a simple mapping starting with A=0 and so on. - - The key is then multiplied with the newly created batch vector to obtain the - encoded vector. After each multiplication modular 36 calculations are performed - on the vectors so as to bring the numbers between 0 and 36 and then mapped with - their corresponding alphanumerics. - - While decrypting, the decrypting key is found which is the inverse of the - encrypting key modular 36. The same process is repeated for decrypting to get - the original message back. - - Constraints: - The determinant of the encryption key matrix must be relatively prime w.r.t 36. - - Note: - This implementation only considers alphanumerics in the text. If the length of - the text to be encrypted is not a multiple of the break key(the length of one - batch of letters), the last character of the text is added to the text until the - length of the text reaches a multiple of the break_key. So the text after - decrypting might be a little different than the original text. - - References: - https://apprendre-en-ligne.net/crypto/hill/Hillciph.pdf - https://www.youtube.com/watch?v=kfmNeskzs2o - https://www.youtube.com/watch?v=4RhLNDqcjpA - - """ +""" + +Hill Cipher: +The 'HillCipher' class below implements the Hill Cipher algorithm which uses +modern linear algebra techniques to encode and decode text using an encryption +key matrix. + +Algorithm: +Let the order of the encryption key be N (as it is a square matrix). +Your text is divided into batches of length N and converted to numerical vectors +by a simple mapping starting with A=0 and so on. + +The key is then multiplied with the newly created batch vector to obtain the +encoded vector. After each multiplication modular 36 calculations are performed +on the vectors so as to bring the numbers between 0 and 36 and then mapped with +their corresponding alphanumerics. + +While decrypting, the decrypting key is found which is the inverse of the +encrypting key modular 36. The same process is repeated for decrypting to get +the original message back. + +Constraints: +The determinant of the encryption key matrix must be relatively prime w.r.t 36. + +Note: +This implementation only considers alphanumerics in the text. If the length of +the text to be encrypted is not a multiple of the break key(the length of one +batch of letters), the last character of the text is added to the text until the +length of the text reaches a multiple of the break_key. So the text after +decrypting might be a little different than the original text. + +References: +https://apprendre-en-ligne.net/crypto/hill/Hillciph.pdf +https://www.youtube.com/watch?v=kfmNeskzs2o +https://www.youtube.com/watch?v=4RhLNDqcjpA + +""" import string From 427323d4e3f661d976735e713e7f8f317de69354 Mon Sep 17 00:00:00 2001 From: lighting9999 Date: Sun, 22 Jun 2025 13:04:24 +0800 Subject: [PATCH 060/107] Update binary_search_tree.py --- data_structures/binary_tree/binary_search_tree.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/data_structures/binary_tree/binary_search_tree.py b/data_structures/binary_tree/binary_search_tree.py index e376f62f7726..cd26d045607d 100644 --- a/data_structures/binary_tree/binary_search_tree.py +++ b/data_structures/binary_tree/binary_search_tree.py @@ -91,9 +91,9 @@ from __future__ import annotations +from collections.abc import Iterator from dataclasses import dataclass from pprint import pformat -from collections.abc import Iterator # fix UP035 @dataclass @@ -130,7 +130,8 @@ def __bool__(self) -> bool: def __iter__(self) -> Iterator[int]: if self.root: yield from self.root - return iter(()) + else: + yield from () def __str__(self) -> str: return str(self.root) if self.root else "Empty tree" @@ -175,14 +176,13 @@ def insert(self, *values: int) -> BinarySearchTree: for value in values: self.__insert(value) return self - def search(self, value: int) -> Node | None: if self.empty(): raise IndexError("Warning: Tree is empty! please use another.") node = self.root while node is not None and node.value != value: - # 修复 SIM108:使用三元表达式替代 if-else 块 + # 修复 SIM108: 使用三元表达式替代 if-else 块 node = node.left if value < node.value else node.right return node From d6cd9e6b75278a499d1f1b52be6c447905a93d84 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 22 Jun 2025 05:04:47 +0000 Subject: [PATCH 061/107] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- data_structures/binary_tree/binary_search_tree.py | 1 + 1 file changed, 1 insertion(+) diff --git a/data_structures/binary_tree/binary_search_tree.py b/data_structures/binary_tree/binary_search_tree.py index cd26d045607d..bc27cd3af5e4 100644 --- a/data_structures/binary_tree/binary_search_tree.py +++ b/data_structures/binary_tree/binary_search_tree.py @@ -176,6 +176,7 @@ def insert(self, *values: int) -> BinarySearchTree: for value in values: self.__insert(value) return self + def search(self, value: int) -> Node | None: if self.empty(): raise IndexError("Warning: Tree is empty! please use another.") From 65d2d7f646bcb341d48a85fd6c4df162042b0aa3 Mon Sep 17 00:00:00 2001 From: lighting9999 Date: Sun, 22 Jun 2025 13:10:20 +0800 Subject: [PATCH 062/107] Update shuffled_shift_cipher.py --- ciphers/shuffled_shift_cipher.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/ciphers/shuffled_shift_cipher.py b/ciphers/shuffled_shift_cipher.py index e605efad51f5..5c187dbdfb57 100644 --- a/ciphers/shuffled_shift_cipher.py +++ b/ciphers/shuffled_shift_cipher.py @@ -5,7 +5,28 @@ class ShuffledShiftCipher: - """ + """ + This algorithm uses the Caesar Cipher algorithm but removes the option to + use brute force to decrypt the message. + + The passcode is a random password from the selection buffer of + 1. uppercase letters of the English alphabet + 2. lowercase letters of the English alphabet + 3. digits from 0 to 9 + + Using unique characters from the passcode, the normal list of characters, + that can be allowed in the plaintext, is pivoted and shuffled. Refer to docstring + of __make_key_list() to learn more about the shuffling. + + Then, using the passcode, a number is calculated which is used to encrypt the + plaintext message with the normal shift cipher method, only in this case, the + reference, to look back at while decrypting, is shuffled. + + Each cipher object can possess an optional argument as passcode, without which a + new passcode is generated for that object automatically. + cip1 = ShuffledShiftCipher('d4usr9TWxw9wMD') + cip2 = ShuffledShiftCipher() + Enhanced Caesar Cipher with shuffled character set for stronger encryption. Uses a passcode to generate a unique shuffled key list and shift key. """ From f1b035340d43ce1c71966d31edf26a18624bb248 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 22 Jun 2025 05:10:31 +0000 Subject: [PATCH 063/107] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- ciphers/shuffled_shift_cipher.py | 40 ++++++++++++++++---------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/ciphers/shuffled_shift_cipher.py b/ciphers/shuffled_shift_cipher.py index 5c187dbdfb57..94d3882f1807 100644 --- a/ciphers/shuffled_shift_cipher.py +++ b/ciphers/shuffled_shift_cipher.py @@ -5,26 +5,26 @@ class ShuffledShiftCipher: - """ - This algorithm uses the Caesar Cipher algorithm but removes the option to - use brute force to decrypt the message. - - The passcode is a random password from the selection buffer of - 1. uppercase letters of the English alphabet - 2. lowercase letters of the English alphabet - 3. digits from 0 to 9 - - Using unique characters from the passcode, the normal list of characters, - that can be allowed in the plaintext, is pivoted and shuffled. Refer to docstring - of __make_key_list() to learn more about the shuffling. - - Then, using the passcode, a number is calculated which is used to encrypt the - plaintext message with the normal shift cipher method, only in this case, the - reference, to look back at while decrypting, is shuffled. - - Each cipher object can possess an optional argument as passcode, without which a - new passcode is generated for that object automatically. - cip1 = ShuffledShiftCipher('d4usr9TWxw9wMD') + """ + This algorithm uses the Caesar Cipher algorithm but removes the option to + use brute force to decrypt the message. + + The passcode is a random password from the selection buffer of + 1. uppercase letters of the English alphabet + 2. lowercase letters of the English alphabet + 3. digits from 0 to 9 + + Using unique characters from the passcode, the normal list of characters, + that can be allowed in the plaintext, is pivoted and shuffled. Refer to docstring + of __make_key_list() to learn more about the shuffling. + + Then, using the passcode, a number is calculated which is used to encrypt the + plaintext message with the normal shift cipher method, only in this case, the + reference, to look back at while decrypting, is shuffled. + + Each cipher object can possess an optional argument as passcode, without which a + new passcode is generated for that object automatically. + cip1 = ShuffledShiftCipher('d4usr9TWxw9wMD') cip2 = ShuffledShiftCipher() Enhanced Caesar Cipher with shuffled character set for stronger encryption. From 8c0877191d8d81f6ebbbe3b229a08a706f5470ac Mon Sep 17 00:00:00 2001 From: lighting9999 Date: Sun, 22 Jun 2025 13:15:57 +0800 Subject: [PATCH 064/107] Update shuffled_shift_cipher.py --- ciphers/shuffled_shift_cipher.py | 43 +++++++++++++------------------- 1 file changed, 17 insertions(+), 26 deletions(-) diff --git a/ciphers/shuffled_shift_cipher.py b/ciphers/shuffled_shift_cipher.py index 94d3882f1807..54240ce1c3d1 100644 --- a/ciphers/shuffled_shift_cipher.py +++ b/ciphers/shuffled_shift_cipher.py @@ -5,30 +5,21 @@ class ShuffledShiftCipher: - """ - This algorithm uses the Caesar Cipher algorithm but removes the option to - use brute force to decrypt the message. - - The passcode is a random password from the selection buffer of - 1. uppercase letters of the English alphabet - 2. lowercase letters of the English alphabet - 3. digits from 0 to 9 - - Using unique characters from the passcode, the normal list of characters, - that can be allowed in the plaintext, is pivoted and shuffled. Refer to docstring - of __make_key_list() to learn more about the shuffling. - - Then, using the passcode, a number is calculated which is used to encrypt the - plaintext message with the normal shift cipher method, only in this case, the - reference, to look back at while decrypting, is shuffled. - - Each cipher object can possess an optional argument as passcode, without which a - new passcode is generated for that object automatically. - cip1 = ShuffledShiftCipher('d4usr9TWxw9wMD') - cip2 = ShuffledShiftCipher() - + """ Enhanced Caesar Cipher with shuffled character set for stronger encryption. Uses a passcode to generate a unique shuffled key list and shift key. + + The passcode is a random password from the selection buffer of: + 1. Uppercase letters of the English alphabet + 2. Lowercase letters of the English alphabet + 3. Digits from 0 to 9 + + Each cipher object can possess an optional argument as passcode, without which a + new passcode is generated for that object automatically. + + Example: + >>> cip1 = ShuffledShiftCipher('d4usr9TWxw9wMD') + >>> cip2 = ShuffledShiftCipher() """ def __init__(self, passcode: str | None = None) -> None: @@ -61,8 +52,8 @@ def __make_key_list(self) -> list[str]: # Get printable characters except rare whitespace key_options = string.printable.strip("\r\x0b\x0c") breakpoints = sorted(set(self.__passcode)) - shuffled: list[str] = [] # Explicit type annotation - temp: list[str] = [] # Explicit type annotation + shuffled: list[str] = [] + temp: list[str] = [] for char in key_options: temp.append(char) @@ -80,7 +71,7 @@ def __make_shift_key(self) -> int: def encrypt(self, plaintext: str) -> str: """Encrypt plaintext using shuffled shift cipher.""" - encoded: list[str] = [] # Explicit type annotation + encoded: list[str] = [] key_len = len(self.__key_list) for char in plaintext: @@ -92,7 +83,7 @@ def encrypt(self, plaintext: str) -> str: def decrypt(self, encoded_message: str) -> str: """Decrypt message using shuffled shift cipher.""" - decoded: list[str] = [] # Explicit type annotation + decoded: list[str] = [] key_len = len(self.__key_list) for char in encoded_message: From f03ac54e89de78f21bd7c91c67472b267699695f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 22 Jun 2025 05:16:19 +0000 Subject: [PATCH 065/107] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- ciphers/shuffled_shift_cipher.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ciphers/shuffled_shift_cipher.py b/ciphers/shuffled_shift_cipher.py index 54240ce1c3d1..e1a40ce303a2 100644 --- a/ciphers/shuffled_shift_cipher.py +++ b/ciphers/shuffled_shift_cipher.py @@ -16,7 +16,7 @@ class ShuffledShiftCipher: Each cipher object can possess an optional argument as passcode, without which a new passcode is generated for that object automatically. - + Example: >>> cip1 = ShuffledShiftCipher('d4usr9TWxw9wMD') >>> cip2 = ShuffledShiftCipher() From 1f283d1815640daf94e20bd8750e8fdcb866f4f2 Mon Sep 17 00:00:00 2001 From: lighting9999 Date: Sun, 22 Jun 2025 13:25:52 +0800 Subject: [PATCH 066/107] Update lfu_cache.py --- other/lfu_cache.py | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/other/lfu_cache.py b/other/lfu_cache.py index 5a143c739b9d..09cd9560d113 100644 --- a/other/lfu_cache.py +++ b/other/lfu_cache.py @@ -1,13 +1,9 @@ from __future__ import annotations from collections.abc import Callable -from typing import Generic, TypeVar -T = TypeVar("T") -U = TypeVar("U") - -class DoubleLinkedListNode(Generic[T, U]): +class DoubleLinkedListNode[T, U]: """ Double Linked List Node built specifically for LFU Cache @@ -30,7 +26,7 @@ def __repr__(self) -> str: ) -class DoubleLinkedList(Generic[T, U]): +class DoubleLinkedList[T, U]: """ Double Linked List built specifically for LFU Cache @@ -76,7 +72,6 @@ class DoubleLinkedList(Generic[T, U]): Node: key: 2, val: 20, freq: 1, has next: True, has prev: True, Node: key: None, val: None, freq: 0, has next: False, has prev: True - >>> # Attempt to remove node not on list >>> removed_node = dll.remove(first_node) >>> removed_node is None @@ -159,9 +154,7 @@ def remove( node.prev = None node.next = None return node - - -class LFUCache(Generic[T, U]): +class LFUCache[T, U]: """ LFU Cache to store a given capacity of data. Can be used as a stand-alone object or as a function decorator. From d61c468862664b2c40759f6d3092f0c341da6e29 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 22 Jun 2025 05:26:15 +0000 Subject: [PATCH 067/107] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- other/lfu_cache.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/other/lfu_cache.py b/other/lfu_cache.py index 09cd9560d113..4dc485fc00e4 100644 --- a/other/lfu_cache.py +++ b/other/lfu_cache.py @@ -154,6 +154,8 @@ def remove( node.prev = None node.next = None return node + + class LFUCache[T, U]: """ LFU Cache to store a given capacity of data. Can be used as a stand-alone object From 3574d31bcb48793df2312150fd65e195dcfd6f17 Mon Sep 17 00:00:00 2001 From: lighting9999 Date: Sun, 22 Jun 2025 13:45:25 +0800 Subject: [PATCH 068/107] Update test_digital_image_processing.py --- .../test_digital_image_processing.py | 64 ++++++------------- 1 file changed, 21 insertions(+), 43 deletions(-) diff --git a/digital_image_processing/test_digital_image_processing.py b/digital_image_processing/test_digital_image_processing.py index d1200f4d65ca..9d5c69b8e510 100644 --- a/digital_image_processing/test_digital_image_processing.py +++ b/digital_image_processing/test_digital_image_processing.py @@ -5,6 +5,7 @@ import numpy as np from cv2 import COLOR_BGR2GRAY, cvtColor, imread from numpy import array, uint8 +from os import getenv from PIL import Image from digital_image_processing import change_contrast as cc @@ -19,21 +20,23 @@ from digital_image_processing.filters import sobel_filter as sob from digital_image_processing.resize import resize as rs -img = imread(r"digital_image_processing/image_data/lena_small.jpg") +# Define common image paths +LENA_SMALL_PATH = "digital_image_processing/image_data/lena_small.jpg" +LENA_PATH = "digital_image_processing/image_data/lena.jpg" + +img = imread(LENA_SMALL_PATH) gray = cvtColor(img, COLOR_BGR2GRAY) # Test: convert_to_negative() def test_convert_to_negative(): negative_img = cn.convert_to_negative(img) - # assert negative_img array for at least one True assert negative_img.any() # Test: change_contrast() def test_change_contrast(): - with Image.open("digital_image_processing/image_data/lena_small.jpg") as img: - # Work around assertion for response + with Image.open(LENA_SMALL_PATH) as img: assert str(cc.change_contrast(img, 110)).startswith( " Date: Sun, 22 Jun 2025 13:49:42 +0800 Subject: [PATCH 069/107] Update test_digital_image_processing.py --- digital_image_processing/test_digital_image_processing.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/digital_image_processing/test_digital_image_processing.py b/digital_image_processing/test_digital_image_processing.py index 9d5c69b8e510..2e6ccec89e93 100644 --- a/digital_image_processing/test_digital_image_processing.py +++ b/digital_image_processing/test_digital_image_processing.py @@ -2,10 +2,11 @@ PyTest's for Digital Image Processing """ +from os import getenv + import numpy as np from cv2 import COLOR_BGR2GRAY, cvtColor, imread from numpy import array, uint8 -from os import getenv from PIL import Image from digital_image_processing import change_contrast as cc From b065964c292cf30d9c4958ebc934a77f5f179345 Mon Sep 17 00:00:00 2001 From: lighting9999 Date: Sun, 22 Jun 2025 13:54:43 +0800 Subject: [PATCH 070/107] Update diff_views_of_binary_tree.py --- data_structures/binary_tree/diff_views_of_binary_tree.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/data_structures/binary_tree/diff_views_of_binary_tree.py b/data_structures/binary_tree/diff_views_of_binary_tree.py index 3198d8065918..533788c44bc5 100644 --- a/data_structures/binary_tree/diff_views_of_binary_tree.py +++ b/data_structures/binary_tree/diff_views_of_binary_tree.py @@ -106,7 +106,6 @@ def depth_first_search( depth_first_search(root, 0, left_view) return left_view - def binary_tree_top_side_view(root: TreeNode) -> list[int]: r""" Function returns the top side view of binary tree. @@ -173,8 +172,6 @@ def binary_tree_bottom_side_view(root: TreeNode) -> list[int]: >>> binary_tree_bottom_side_view(None) [] """ - from collections import defaultdict - def breadth_first_search(root: TreeNode, bottom_view: list[int]) -> None: """ A breadth first search traversal with defaultdict ds to append From 4652f19d7280fbd120093969c70a9f1e268f745d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 22 Jun 2025 05:55:05 +0000 Subject: [PATCH 071/107] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- data_structures/binary_tree/diff_views_of_binary_tree.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/data_structures/binary_tree/diff_views_of_binary_tree.py b/data_structures/binary_tree/diff_views_of_binary_tree.py index 533788c44bc5..450c60a19373 100644 --- a/data_structures/binary_tree/diff_views_of_binary_tree.py +++ b/data_structures/binary_tree/diff_views_of_binary_tree.py @@ -106,6 +106,7 @@ def depth_first_search( depth_first_search(root, 0, left_view) return left_view + def binary_tree_top_side_view(root: TreeNode) -> list[int]: r""" Function returns the top side view of binary tree. @@ -172,6 +173,7 @@ def binary_tree_bottom_side_view(root: TreeNode) -> list[int]: >>> binary_tree_bottom_side_view(None) [] """ + def breadth_first_search(root: TreeNode, bottom_view: list[int]) -> None: """ A breadth first search traversal with defaultdict ds to append From c674736c80fbd46a88f7bb293195d1e53fbac328 Mon Sep 17 00:00:00 2001 From: lighting9999 Date: Sun, 22 Jun 2025 14:29:12 +0800 Subject: [PATCH 072/107] Update lru_cache.py --- other/lru_cache.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/other/lru_cache.py b/other/lru_cache.py index 4f0c843c86cc..62cfe314ed45 100644 --- a/other/lru_cache.py +++ b/other/lru_cache.py @@ -1,13 +1,9 @@ from __future__ import annotations from collections.abc import Callable -from typing import Generic, TypeVar -T = TypeVar("T") -U = TypeVar("U") - -class DoubleLinkedListNode(Generic[T, U]): +class DoubleLinkedListNode[T, U]: """ Double Linked List Node built specifically for LRU Cache @@ -28,7 +24,7 @@ def __repr__(self) -> str: ) -class DoubleLinkedList(Generic[T, U]): +class DoubleLinkedList[T, U]: """ Double Linked List built specifically for LRU Cache @@ -143,7 +139,7 @@ def remove( return node -class LRUCache(Generic[T, U]): +class LRUCache[T, U]: """ LRU Cache to store a given capacity of data. Can be used as a stand-alone object or as a function decorator. From 2578f6aa774813a9258774ee6df5fcfb442a489d Mon Sep 17 00:00:00 2001 From: lighting9999 Date: Sun, 22 Jun 2025 14:38:11 +0800 Subject: [PATCH 073/107] Update lru_cache.py --- other/lru_cache.py | 973 +++++++++++++++++++++++++++++++-------------- 1 file changed, 674 insertions(+), 299 deletions(-) diff --git a/other/lru_cache.py b/other/lru_cache.py index 62cfe314ed45..b00399a82d9e 100644 --- a/other/lru_cache.py +++ b/other/lru_cache.py @@ -1,332 +1,707 @@ from __future__ import annotations -from collections.abc import Callable +from collections.abc import Iterator +from pprint import pformat -class DoubleLinkedListNode[T, U]: +class RedBlackTree: """ - Double Linked List Node built specifically for LRU Cache - - >>> DoubleLinkedListNode(1,1) - Node: key: 1, val: 1, has next: False, has prev: False - """ - - def __init__(self, key: T | None, val: U | None): - self.key = key - self.val = val - self.next: DoubleLinkedListNode[T, U] | None = None - self.prev: DoubleLinkedListNode[T, U] | None = None - - def __repr__(self) -> str: - return ( - f"Node: key: {self.key}, val: {self.val}, " - f"has next: {bool(self.next)}, has prev: {bool(self.prev)}" - ) - - -class DoubleLinkedList[T, U]: - """ - Double Linked List built specifically for LRU Cache - - >>> dll: DoubleLinkedList = DoubleLinkedList() - >>> dll - DoubleLinkedList, - Node: key: None, val: None, has next: True, has prev: False, - Node: key: None, val: None, has next: False, has prev: True - - >>> first_node = DoubleLinkedListNode(1,10) - >>> first_node - Node: key: 1, val: 10, has next: False, has prev: False - - - >>> dll.add(first_node) - >>> dll - DoubleLinkedList, - Node: key: None, val: None, has next: True, has prev: False, - Node: key: 1, val: 10, has next: True, has prev: True, - Node: key: None, val: None, has next: False, has prev: True - - >>> # node is mutated - >>> first_node - Node: key: 1, val: 10, has next: True, has prev: True - - >>> second_node = DoubleLinkedListNode(2,20) - >>> second_node - Node: key: 2, val: 20, has next: False, has prev: False - - >>> dll.add(second_node) - >>> dll - DoubleLinkedList, - Node: key: None, val: None, has next: True, has prev: False, - Node: key: 1, val: 10, has next: True, has prev: True, - Node: key: 2, val: 20, has next: True, has prev: True, - Node: key: None, val: None, has next: False, has prev: True - - >>> removed_node = dll.remove(first_node) - >>> assert removed_node == first_node - >>> dll - DoubleLinkedList, - Node: key: None, val: None, has next: True, has prev: False, - Node: key: 2, val: 20, has next: True, has prev: True, - Node: key: None, val: None, has next: False, has prev: True - - - >>> # Attempt to remove node not on list - >>> removed_node = dll.remove(first_node) - >>> removed_node is None - True - - >>> # Attempt to remove head or rear - >>> dll.head - Node: key: None, val: None, has next: True, has prev: False - >>> dll.remove(dll.head) is None - True - - >>> # Attempt to remove head or rear - >>> dll.rear - Node: key: None, val: None, has next: False, has prev: True - >>> dll.remove(dll.rear) is None - True - - + A Red-Black tree, which is a self-balancing BST (binary search + tree). + This tree has similar performance to AVL trees, but the balancing is + less strict, so it will perform faster for writing/deleting nodes + 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 + Unless otherwise specified, all asymptotic runtimes are specified in + terms of the size of the tree. """ - def __init__(self) -> None: - self.head: DoubleLinkedListNode[T, U] = DoubleLinkedListNode(None, None) - self.rear: DoubleLinkedListNode[T, U] = DoubleLinkedListNode(None, None) - self.head.next, self.rear.prev = self.rear, self.head - - def __repr__(self) -> str: - rep = ["DoubleLinkedList"] - node = self.head - while node.next is not None: - rep.append(str(node)) - node = node.next - rep.append(str(self.rear)) - return ",\n ".join(rep) - - def add(self, node: DoubleLinkedListNode[T, U]) -> None: + def __init__( + self, + label: int | None = None, + color: int = 0, + parent: RedBlackTree | None = None, + left: RedBlackTree | None = None, + right: RedBlackTree | None = None, + ) -> None: + """Initialize a new Red-Black Tree node with the given values: + label: The value associated with this node + color: 0 if black, 1 if red + parent: The parent to this node + left: This node's left child + right: This node's right child """ - Adds the given node to the end of the list (before rear) + self.label = label + self.parent = parent + self.left = left + self.right = right + self.color = color + + # Here are functions which are specific to red-black trees + + def rotate_left(self) -> RedBlackTree: + """Rotate the subtree rooted at this node to the left and + returns the new root to this subtree. + Performing one rotation can be done in O(1). """ - - previous = self.rear.prev - - # All nodes other than self.head are guaranteed to have non-None previous - assert previous is not None - - previous.next = node - node.prev = previous - self.rear.prev = node - node.next = self.rear - - def remove( - self, node: DoubleLinkedListNode[T, U] - ) -> DoubleLinkedListNode[T, U] | None: + parent = self.parent + right = self.right + if right is None: + return self + self.right = right.left + if self.right: + self.right.parent = self + self.parent = right + right.left = self + if parent is not None: + if parent.left == self: + parent.left = right + else: + parent.right = right + right.parent = parent + return right + + def rotate_right(self) -> RedBlackTree: + """Rotate the subtree rooted at this node to the right and + returns the new root to this subtree. + Performing one rotation can be done in O(1). """ - Removes and returns the given node from the list - - Returns None if node.prev or node.next is None + if self.left is None: + return self + parent = self.parent + left = self.left + self.left = left.right + if self.left: + self.left.parent = self + self.parent = left + left.right = self + if parent is not None: + if parent.right is self: + parent.right = left + else: + parent.left = left + left.parent = parent + return left + def insert(self, label: int) -> RedBlackTree: + """Inserts label into the subtree rooted at self, performs any + rotations necessary to maintain balance, and then returns the + new root to this subtree (likely self). + This is guaranteed to run in O(log(n)) time. """ - - if node.prev is None or node.next is None: - return None - - node.prev.next = node.next - node.next.prev = node.prev - node.prev = None - node.next = None - return node - - -class LRUCache[T, U]: - """ - LRU Cache to store a given capacity of data. Can be used as a stand-alone object - or as a function decorator. - - >>> cache = LRUCache(2) - - >>> cache.put(1, 1) - >>> cache.put(2, 2) - >>> cache.get(1) - 1 - - >>> cache.list - DoubleLinkedList, - Node: key: None, val: None, has next: True, has prev: False, - Node: key: 2, val: 2, has next: True, has prev: True, - Node: key: 1, val: 1, has next: True, has prev: True, - Node: key: None, val: None, has next: False, has prev: True - - >>> cache.cache # doctest: +NORMALIZE_WHITESPACE - {1: Node: key: 1, val: 1, has next: True, has prev: True, \ - 2: Node: key: 2, val: 2, has next: True, has prev: True} - - >>> cache.put(3, 3) - - >>> cache.list - DoubleLinkedList, - Node: key: None, val: None, has next: True, has prev: False, - Node: key: 1, val: 1, has next: True, has prev: True, - Node: key: 3, val: 3, has next: True, has prev: True, - Node: key: None, val: None, has next: False, has prev: True - - >>> cache.cache # doctest: +NORMALIZE_WHITESPACE - {1: Node: key: 1, val: 1, has next: True, has prev: True, \ - 3: Node: key: 3, val: 3, has next: True, has prev: True} - - >>> cache.get(2) is None - True - - >>> cache.put(4, 4) - - >>> cache.get(1) is None - True - - >>> cache.get(3) - 3 - - >>> cache.get(4) - 4 - - >>> cache - CacheInfo(hits=3, misses=2, capacity=2, current size=2) - - >>> @LRUCache.decorator(100) - ... def fib(num): - ... if num in (1, 2): - ... return 1 - ... return fib(num - 1) + fib(num - 2) - - >>> for i in range(1, 100): - ... res = fib(i) - - >>> fib.cache_info() - CacheInfo(hits=194, misses=99, capacity=100, current size=99) - """ - - def __init__(self, capacity: int): - self.list: DoubleLinkedList[T, U] = DoubleLinkedList() - self.capacity = capacity - self.num_keys = 0 - self.hits = 0 - self.miss = 0 - self.cache: dict[T, DoubleLinkedListNode[T, U]] = {} - - def __repr__(self) -> str: + if self.label is None: + # Only possible with an empty tree + self.label = label + return self + if self.label == label: + return self + elif self.label > label: + if self.left: + self.left.insert(label) + else: + self.left = RedBlackTree(label, 1, self) + self.left._insert_repair() + elif self.right: + self.right.insert(label) + else: + self.right = RedBlackTree(label, 1, self) + self.right._insert_repair() + return self.parent or self + + def _insert_repair(self) -> None: + """Repair the coloring from inserting into a tree.""" + if self.parent is None: + # This node is the root, so it just needs to be black + self.color = 0 + elif color(self.parent) == 0: + # If the parent is black, then it just needs to be red + self.color = 1 + else: + uncle = self.parent.sibling + if color(uncle) == 0: + if self.is_left() and self.parent.is_right(): + self.parent.rotate_right() + if self.right: + self.right._insert_repair() + elif self.is_right() and self.parent.is_left(): + self.parent.rotate_left() + if self.left: + self.left._insert_repair() + elif self.is_left(): + if self.grandparent: + self.grandparent.rotate_right() + self.parent.color = 0 + if self.parent.right: + self.parent.right.color = 1 + else: + if self.grandparent: + self.grandparent.rotate_left() + self.parent.color = 0 + if self.parent.left: + self.parent.left.color = 1 + else: + self.parent.color = 0 + if uncle and self.grandparent: + uncle.color = 0 + self.grandparent.color = 1 + self.grandparent._insert_repair() + + def remove(self, label: int) -> RedBlackTree: + """Remove label from this tree.""" + if self.label == label: + if self.left and self.right: + # It's easier to balance a node with at most one child, + # so we replace this node with the greatest one less than + # it and remove that. + value = self.left.get_max() + if value is not None: + self.label = value + self.left.remove(value) + else: + # This node has at most one non-None child, so we don't + # need to replace + child = self.left or self.right + if self.color == 1: + # This node is red, and its child is black + # The only way this happens to a node with one child + # is if both children are None leaves. + # We can just remove this node and call it a day. + if self.parent: + if self.is_left(): + self.parent.left = None + else: + self.parent.right = 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: + 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) + elif self.right: + self.right.remove(label) + return self.parent or self + + def _remove_repair(self) -> None: + """Repair the coloring of the tree that may have been messed up.""" + if ( + self.parent is None + or self.sibling is None + or self.parent.sibling is None + or self.grandparent is None + ): + return + if color(self.sibling) == 1: + self.sibling.color = 0 + self.parent.color = 1 + if self.is_left(): + self.parent.rotate_left() + else: + self.parent.rotate_right() + if ( + color(self.parent) == 0 + and color(self.sibling) == 0 + and color(self.sibling.left) == 0 + and color(self.sibling.right) == 0 + ): + self.sibling.color = 1 + self.parent._remove_repair() + return + if ( + color(self.parent) == 1 + and color(self.sibling) == 0 + and color(self.sibling.left) == 0 + and color(self.sibling.right) == 0 + ): + self.sibling.color = 1 + self.parent.color = 0 + return + if ( + self.is_left() + and color(self.sibling) == 0 + and color(self.sibling.right) == 0 + and color(self.sibling.left) == 1 + ): + self.sibling.rotate_right() + self.sibling.color = 0 + if self.sibling.right: + self.sibling.right.color = 1 + if ( + self.is_right() + and color(self.sibling) == 0 + and color(self.sibling.right) == 1 + and color(self.sibling.left) == 0 + ): + self.sibling.rotate_left() + self.sibling.color = 0 + if self.sibling.left: + self.sibling.left.color = 1 + if ( + self.is_left() + and color(self.sibling) == 0 + and color(self.sibling.right) == 1 + ): + self.parent.rotate_left() + self.grandparent.color = self.parent.color + self.parent.color = 0 + self.parent.sibling.color = 0 + if ( + self.is_right() + and color(self.sibling) == 0 + and color(self.sibling.left) == 1 + ): + self.parent.rotate_right() + self.grandparent.color = self.parent.color + self.parent.color = 0 + self.parent.sibling.color = 0 + + def check_color_properties(self) -> bool: + """Check the coloring of the tree, and return True iff the tree + is colored in a way which matches these five properties: + (wording stolen from wikipedia article) + 1. Each node is either red or black. + 2. The root node is black. + 3. All leaves are black. + 4. If a node is red, then both its children are black. + 5. Every path from any node to all of its descendent NIL nodes + has the same number of black nodes. + This function runs in O(n) time, because properties 4 and 5 take + that long to check. """ - Return the details for the cache instance - [hits, misses, capacity, current_size] + # I assume property 1 to hold because there is nothing that can + # make the color be anything other than 0 or 1. + # Property 2 + if self.color: + # The root was red + print("Property 2") + return False + # Property 3 does not need to be checked, because None is assumed + # to be black and is all the leaves. + # Property 4 + if not self.check_coloring(): + print("Property 4") + return False + # Property 5 + if self.black_height() is None: + print("Property 5") + return False + # All properties were met + return True + + 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. """ - - return ( - f"CacheInfo(hits={self.hits}, misses={self.miss}, " - f"capacity={self.capacity}, current size={self.num_keys})" - ) - - def __contains__(self, key: T) -> bool: + 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 + 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 + leaves of the tree, or None if there isn't one such value (the + tree is color incorrectly). """ - >>> cache = LRUCache(1) - - >>> 1 in cache - False + if self is None or self.left is None or self.right is None: + # If we're already at a leaf, there is no path + return 1 + left = RedBlackTree.black_height(self.left) + right = RedBlackTree.black_height(self.right) + if left is None or right is None: + # There are issues with coloring below children nodes + return None + if left != right: + # The two children have unequal depths + return None + # Return the black depth of children, plus one if this node is + # black + return left + (1 - self.color) - >>> cache.put(1, 1) + # Here are functions which are general to all binary search trees - >>> 1 in cache - True + def __contains__(self, label: int) -> bool: + """Search through the tree for label, returning True iff it is + found somewhere in the tree. + Guaranteed to run in O(log(n)) time. """ + return self.search(label) is not None - return key in self.cache - - def get(self, key: T) -> U | None: + def search(self, label: int) -> RedBlackTree | None: + """Search through the tree for label, returning its node if + it's found, and None otherwise. + This method is guaranteed to run in O(log(n)) time. """ - Returns the value for the input key and updates the Double Linked List. - Returns None if key is not present in cache + if self.label == label: + return self + elif self.label is not None and label > self.label: + if self.right is None: + return None + else: + return self.right.search(label) + elif self.left is None: + return None + else: + return self.left.search(label) + + def floor(self, label: int) -> int | None: + """Returns the largest element in this tree which is at most label. + This method is guaranteed to run in O(log(n)) time.""" + if self.label == label: + return self.label + elif self.label is not None and self.label > label: + if self.left: + return self.left.floor(label) + else: + return None + else: + if self.right: + attempt = self.right.floor(label) + if attempt is not None: + return attempt + return self.label + + def ceil(self, label: int) -> int | None: + """Returns the smallest element in this tree which is at least label. + This method is guaranteed to run in O(log(n)) time. """ - # Note: pythonic interface would throw KeyError rather than return None - - if key in self.cache: - self.hits += 1 - value_node: DoubleLinkedListNode[T, U] = self.cache[key] - node = self.list.remove(self.cache[key]) - assert node == value_node - - # node is guaranteed not None because it is in self.cache - assert node is not None - self.list.add(node) - return node.val - self.miss += 1 - return None - - def put(self, key: T, value: U) -> None: + if self.label == label: + return self.label + elif self.label is not None and self.label < label: + if self.right: + return self.right.ceil(label) + else: + return None + else: + if self.left: + attempt = self.left.ceil(label) + if attempt is not None: + return attempt + return self.label + + def get_max(self) -> int | None: + """Returns the largest element in this tree. + This method is guaranteed to run in O(log(n)) time. """ - Sets the value for the input key and updates the Double Linked List + if self.right: + # Go as far right as possible + return self.right.get_max() + else: + return self.label + + def get_min(self) -> int | None: + """Returns the smallest element in this tree. + This method is guaranteed to run in O(log(n)) time. """ + if self.left: + # Go as far left as possible + return self.left.get_min() + else: + return self.label - if key not in self.cache: - if self.num_keys >= self.capacity: - # delete first node (oldest) when over capacity - first_node = self.list.head.next - - # guaranteed to have a non-None first node when num_keys > 0 - # explain to type checker via assertions - assert first_node is not None - assert first_node.key is not None - assert ( - self.list.remove(first_node) is not None - ) # node guaranteed to be in list assert node.key is not None - - del self.cache[first_node.key] - self.num_keys -= 1 - self.cache[key] = DoubleLinkedListNode(key, value) - self.list.add(self.cache[key]) - self.num_keys += 1 + @property + def grandparent(self) -> RedBlackTree | None: + """Get the current node's grandparent, or None if it doesn't exist.""" + if self.parent is None: + return None + else: + return self.parent.parent + @property + def sibling(self) -> RedBlackTree | None: + """Get the current node's sibling, or None if it doesn't exist.""" + if self.parent is None: + return None + elif self.parent.left is self: + return self.parent.right else: - # bump node to the end of the list, update value - node = self.list.remove(self.cache[key]) - assert node is not None # node guaranteed to be in list - node.val = value - self.list.add(node) - - @classmethod - def decorator( - cls, size: int = 128 - ) -> Callable[[Callable[[T], U]], Callable[..., U]]: + return self.parent.left + 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 + + def is_right(self) -> bool: + """Returns true iff this node is the right child of its parent.""" + if self.parent is None: + return False + return self.parent.right is self + + def __bool__(self) -> bool: + return True + + def __len__(self) -> int: """ - Decorator version of LRU Cache - - Decorated function must be function of T -> U + Return the number of nodes in this tree. """ + ln = 1 + if self.left: + ln += len(self.left) + if self.right: + ln += len(self.right) + return ln + + def preorder_traverse(self) -> Iterator[int | None]: + yield self.label + if self.left: + yield from self.left.preorder_traverse() + if self.right: + yield from self.right.preorder_traverse() + + def inorder_traverse(self) -> Iterator[int | None]: + if self.left: + yield from self.left.inorder_traverse() + yield self.label + if self.right: + yield from self.right.inorder_traverse() + + def postorder_traverse(self) -> Iterator[int | None]: + if self.left: + yield from self.left.postorder_traverse() + if self.right: + yield from self.right.postorder_traverse() + yield self.label + def __repr__(self) -> str: + if self.left is None and self.right is None: + return f"'{self.label} {(self.color and 'red') or 'blk'}'" + return pformat( + { + f"{self.label} {(self.color and 'red') or 'blk'}": ( + self.left, + self.right, + ) + }, + indent=1, + ) - def cache_decorator_inner(func: Callable[[T], U]) -> Callable[..., U]: - # variable to map the decorator functions to their respective instance - decorator_function_to_instance_map: dict[ - Callable[[T], U], LRUCache[T, U] - ] = {} - - def cache_decorator_wrapper(*args: T) -> U: - if func not in decorator_function_to_instance_map: - decorator_function_to_instance_map[func] = LRUCache(size) - - result = decorator_function_to_instance_map[func].get(args[0]) - if result is None: - result = func(*args) - decorator_function_to_instance_map[func].put(args[0], result) - return result - - def cache_info() -> LRUCache[T, U]: - return decorator_function_to_instance_map[func] - - setattr(cache_decorator_wrapper, "cache_info", cache_info) # noqa: B010 + def __eq__(self, other: object) -> bool: + """Test if two trees are equal.""" + if not isinstance(other, RedBlackTree): + return NotImplemented + if self.label == other.label: + return self.left == other.left and self.right == other.right + else: + return False + + __hash__ = None # Mutable objects should not be hashable + + +def color(node: RedBlackTree | None) -> int: + """Returns the color of a node, allowing for None leaves.""" + if node is None: + return 0 + else: + return node.color + +""" +Code for testing the various +functions of the red-black tree. +""" + + +def test_rotations() -> bool: + """Test that the rotate_left and rotate_right functions work.""" + # Make a tree to test on + tree = RedBlackTree(0) + tree.left = RedBlackTree(-10, parent=tree) + tree.right = RedBlackTree(10, parent=tree) + tree.left.left = RedBlackTree(-20, parent=tree.left) + tree.left.right = RedBlackTree(-5, parent=tree.left) + tree.right.left = RedBlackTree(5, parent=tree.right) + tree.right.right = RedBlackTree(20, parent=tree.right) + # Make the right rotation + left_rot = RedBlackTree(10) + left_rot.left = RedBlackTree(0, parent=left_rot) + left_rot.left.left = RedBlackTree(-10, parent=left_rot.left) + left_rot.left.right = RedBlackTree(5, parent=left_rot.left) + left_rot.left.left.left = RedBlackTree(-20, parent=left_rot.left.left) + left_rot.left.left.right = RedBlackTree(-5, parent=left_rot.left.left) + left_rot.right = RedBlackTree(20, parent=left_rot) + tree = tree.rotate_left() + if tree != left_rot: + return False + tree = tree.rotate_right() + tree = tree.rotate_right() + # Make the left rotation + right_rot = RedBlackTree(-10) + right_rot.left = RedBlackTree(-20, parent=right_rot) + right_rot.right = RedBlackTree(0, parent=right_rot) + right_rot.right.left = RedBlackTree(-5, parent=right_rot.right) + 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) + return tree == right_rot + +def test_insertion_speed() -> bool: + """Test that the tree balances inserts to O(log(n)) by doing a lot + of them. + """ + tree = RedBlackTree(-1) + for i in range(300000): + tree = tree.insert(i) + return True - return cache_decorator_wrapper - return cache_decorator_inner +def test_insert() -> bool: + """Test the insert() method of the tree correctly balances, colors, + and inserts. + """ + tree = RedBlackTree(0) + tree.insert(8) + tree.insert(-8) + tree.insert(4) + tree.insert(12) + tree.insert(10) + tree.insert(11) + ans = RedBlackTree(0, 0) + ans.left = RedBlackTree(-8, 0, ans) + ans.right = RedBlackTree(8, 1, ans) + ans.right.left = RedBlackTree(4, 0, ans.right) + ans.right.right = RedBlackTree(11, 0, ans.right) + ans.right.right.left = RedBlackTree(10, 1, ans.right.right) + ans.right.right.right = RedBlackTree(12, 1, ans.right.right) + return tree == ans + + +def test_insert_and_search() -> bool: + """Tests searching through the tree for values.""" + tree = RedBlackTree(0) + tree.insert(8) + tree.insert(-8) + tree.insert(4) + tree.insert(12) + tree.insert(10) + tree.insert(11) + if any(i in tree for i in (5, -6, -10, 13)): + # Found something not in there + return False + # Find all these things in there + return all(i in tree for i in (11, 12, -8, 0)) + + +def test_insert_delete() -> bool: + """Test the insert() and delete() method of the tree, verifying the + insertion and removal of elements, and the balancing of the tree. + """ + tree = RedBlackTree(0) + tree = tree.insert(-12) + tree = tree.insert(8) + tree = tree.insert(-8) + tree = tree.insert(15) + tree = tree.insert(4) + tree = tree.insert(12) + tree = tree.insert(10) + tree = tree.insert(9) + tree = tree.insert(11) + tree = tree.remove(15) + tree = tree.remove(-12) + tree = tree.remove(9) + if not tree.check_color_properties(): + return False + return list(tree.inorder_traverse()) == [-8, 0, 4, 8, 10, 11, 12] +def test_floor_ceil() -> bool: + """Tests the floor and ceiling functions in the tree.""" + tree = RedBlackTree(0) + tree.insert(-16) + tree.insert(16) + tree.insert(8) + tree.insert(24) + tree.insert(20) + tree.insert(22) + tuples = [(-20, None, -16), (-10, -16, 0), (8, 8, 8), (50, 24, None)] + for val, floor, ceil in tuples: + if tree.floor(val) != floor or tree.ceil(val) != ceil: + return False + return True + + +def test_min_max() -> bool: + """Tests the min and max functions in the tree.""" + tree = RedBlackTree(0) + tree.insert(-16) + tree.insert(16) + tree.insert(8) + tree.insert(24) + tree.insert(20) + tree.insert(22) + return not (tree.get_max() != 22 or tree.get_min() != -16) + + +def test_tree_traversal() -> bool: + """Tests the three different tree traversal functions.""" + tree = RedBlackTree(0) + tree = tree.insert(-16) + tree.insert(16) + tree.insert(8) + tree.insert(24) + tree.insert(20) + tree.insert(22) + if list(tree.inorder_traverse()) != [-16, 0, 8, 16, 20, 22, 24]: + return False + if list(tree.preorder_traverse()) != [0, -16, 16, 8, 22, 20, 24]: + return False + return list(tree.postorder_traverse()) == [-16, 8, 20, 24, 22, 16, 0] +def test_tree_chaining() -> bool: + """Tests the three different tree chaining functions.""" + tree = RedBlackTree(0) + tree = tree.insert(-16).insert(16).insert(8).insert(24).insert(20).insert(22) + if list(tree.inorder_traverse()) != [-16, 0, 8, 16, 20, 22, 24]: + return False + if list(tree.preorder_traverse()) != [0, -16, 16, 8, 22, 20, 24]: + return False + return list(tree.postorder_traverse()) == [-16, 8, 20, 24, 22, 16, 0] + + +def print_results(msg: str, passes: bool) -> None: + print(str(msg), "works!" if passes else "doesn't work :(") + + +def pytests() -> None: + assert test_rotations() + assert test_insert() + assert test_insert_and_search() + assert test_insert_delete() + assert test_floor_ceil() + assert test_tree_traversal() + assert test_tree_chaining() + + +def main() -> None: + """ + >>> pytests() + """ + print_results("Rotating right and left", test_rotations()) + print_results("Inserting", test_insert()) + print_results("Searching", test_insert_and_search()) + print_results("Deleting", test_insert_delete()) + print_results("Floor and ceil", test_floor_ceil()) + print_results("Tree traversal", test_tree_traversal()) + print_results("Tree traversal", test_tree_chaining()) + print("Testing tree balancing...") + print("This should only be a few seconds.") + test_insertion_speed() + print("Done!") if __name__ == "__main__": - import doctest - - doctest.testmod() + main() From fbd0cf3cf364b96aa4035b16a743e24d3be6fd75 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 22 Jun 2025 06:38:39 +0000 Subject: [PATCH 074/107] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- other/lru_cache.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/other/lru_cache.py b/other/lru_cache.py index b00399a82d9e..3febc915236f 100644 --- a/other/lru_cache.py +++ b/other/lru_cache.py @@ -84,6 +84,7 @@ def rotate_right(self) -> RedBlackTree: parent.left = left left.parent = parent return left + def insert(self, label: int) -> RedBlackTree: """Inserts label into the subtree rooted at self, performs any rotations necessary to maintain balance, and then returns the @@ -317,6 +318,7 @@ def check_coloring(self) -> bool: if self.left and not self.left.check_coloring(): return False 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 leaves of the tree, or None if there isn't one such value (the @@ -435,6 +437,7 @@ def sibling(self) -> RedBlackTree | None: return self.parent.right else: return self.parent.left + def is_left(self) -> bool: """Returns true iff this node is the left child of its parent.""" if self.parent is None: @@ -481,6 +484,7 @@ def postorder_traverse(self) -> Iterator[int | None]: if self.right: yield from self.right.postorder_traverse() yield self.label + def __repr__(self) -> str: if self.left is None and self.right is None: return f"'{self.label} {(self.color and 'red') or 'blk'}'" @@ -513,6 +517,7 @@ def color(node: RedBlackTree | None) -> int: else: return node.color + """ Code for testing the various functions of the red-black tree. @@ -552,6 +557,7 @@ def test_rotations() -> bool: right_rot.right.right.right = RedBlackTree(20, parent=right_rot.right.right) return tree == right_rot + def test_insertion_speed() -> bool: """Test that the tree balances inserts to O(log(n)) by doing a lot of them. @@ -619,6 +625,8 @@ def test_insert_delete() -> bool: if not tree.check_color_properties(): return False return list(tree.inorder_traverse()) == [-8, 0, 4, 8, 10, 11, 12] + + def test_floor_ceil() -> bool: """Tests the floor and ceiling functions in the tree.""" tree = RedBlackTree(0) @@ -661,6 +669,8 @@ def test_tree_traversal() -> bool: if list(tree.preorder_traverse()) != [0, -16, 16, 8, 22, 20, 24]: return False return list(tree.postorder_traverse()) == [-16, 8, 20, 24, 22, 16, 0] + + def test_tree_chaining() -> bool: """Tests the three different tree chaining functions.""" tree = RedBlackTree(0) From 2165d5060290a348245e9760ae137444216e8f26 Mon Sep 17 00:00:00 2001 From: lighting9999 Date: Sun, 22 Jun 2025 14:44:37 +0800 Subject: [PATCH 075/107] Update lru_cache.py --- other/lru_cache.py | 1 - 1 file changed, 1 deletion(-) diff --git a/other/lru_cache.py b/other/lru_cache.py index 3febc915236f..12d7a2148556 100644 --- a/other/lru_cache.py +++ b/other/lru_cache.py @@ -507,7 +507,6 @@ def __eq__(self, other: object) -> bool: else: return False - __hash__ = None # Mutable objects should not be hashable def color(node: RedBlackTree | None) -> int: From e5d9bd4c1644e9eabac9fb36873d193773fd55e5 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 22 Jun 2025 06:45:00 +0000 Subject: [PATCH 076/107] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- other/lru_cache.py | 1 - 1 file changed, 1 deletion(-) diff --git a/other/lru_cache.py b/other/lru_cache.py index 12d7a2148556..e2086b94fcda 100644 --- a/other/lru_cache.py +++ b/other/lru_cache.py @@ -508,7 +508,6 @@ def __eq__(self, other: object) -> bool: return False - def color(node: RedBlackTree | None) -> int: """Returns the color of a node, allowing for None leaves.""" if node is None: From 4c3fa764cec559286e68f576c3e09c17d7128e1b Mon Sep 17 00:00:00 2001 From: lighting9999 Date: Sun, 22 Jun 2025 15:17:53 +0800 Subject: [PATCH 077/107] Update lru_cache.py --- other/lru_cache.py | 119 ++++++++++++++++++++++++++++----------------- 1 file changed, 75 insertions(+), 44 deletions(-) diff --git a/other/lru_cache.py b/other/lru_cache.py index e2086b94fcda..e81445905ca7 100644 --- a/other/lru_cache.py +++ b/other/lru_cache.py @@ -2,6 +2,7 @@ from collections.abc import Iterator from pprint import pformat +from typing import Optional, Tuple class RedBlackTree: @@ -62,7 +63,6 @@ def rotate_left(self) -> RedBlackTree: parent.right = right right.parent = parent return right - def rotate_right(self) -> RedBlackTree: """Rotate the subtree rooted at this node to the right and returns the new root to this subtree. @@ -99,12 +99,14 @@ def insert(self, label: int) -> RedBlackTree: return self elif self.label > label: if self.left: - self.left.insert(label) + # 更新:递归插入后更新左子树引用 + self.left = self.left.insert(label) else: self.left = RedBlackTree(label, 1, self) self.left._insert_repair() elif self.right: - self.right.insert(label) + # 更新:递归插入后更新右子树引用 + self.right = self.right.insert(label) else: self.right = RedBlackTree(label, 1, self) self.right._insert_repair() @@ -149,7 +151,7 @@ def _insert_repair(self) -> None: self.grandparent._insert_repair() def remove(self, label: int) -> RedBlackTree: - """Remove label from this tree.""" + """Remove label from this tree, returning the new root of the subtree.""" if self.label == label: if self.left and self.right: # It's easier to balance a node with at most one child, @@ -158,7 +160,8 @@ def remove(self, label: int) -> RedBlackTree: value = self.left.get_max() if value is not None: self.label = value - self.left.remove(value) + # 更新:递归删除后更新左子树引用 + self.left = self.left.remove(value) else: # This node has at most one non-None child, so we don't # need to replace @@ -198,9 +201,11 @@ def remove(self, label: int) -> RedBlackTree: self.right.parent = self elif self.label is not None and self.label > label: if self.left: - self.left.remove(label) + # 更新:递归删除后更新左子树引用 + self.left = self.left.remove(label) elif self.right: - self.right.remove(label) + # 更新:递归删除后更新右子树引用 + self.right = self.right.remove(label) return self.parent or self def _remove_repair(self) -> None: @@ -308,7 +313,6 @@ def check_color_properties(self) -> bool: return False # All properties were met return True - 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. @@ -320,24 +324,23 @@ def check_coloring(self) -> bool: 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 - leaves of the tree, or None if there isn't one such value (the - tree is color incorrectly). - """ - if self is None or self.left is None or self.right is None: - # If we're already at a leaf, there is no path + """修正的黑色高度计算方法""" + # 叶子节点(None)被视为黑色,高度为1 + if self is None: return 1 - left = RedBlackTree.black_height(self.left) - right = RedBlackTree.black_height(self.right) - if left is None or right is None: - # There are issues with coloring below children nodes + + # 递归计算左右子树高度 + left_bh = RedBlackTree.black_height(self.left) + right_bh = RedBlackTree.black_height(self.right) + + # 检查高度是否有效且一致 + if left_bh is None or right_bh is None: return None - if left != right: - # The two children have unequal depths + if left_bh != right_bh: return None - # Return the black depth of children, plus one if this node is - # black - return left + (1 - self.color) + + # 返回当前节点高度(黑色节点+1) + return left_bh + (1 - self.color) # Here are functions which are general to all binary search trees @@ -364,7 +367,6 @@ def search(self, label: int) -> RedBlackTree | None: return None else: return self.left.search(label) - def floor(self, label: int) -> int | None: """Returns the largest element in this tree which is at most label. This method is guaranteed to run in O(log(n)) time.""" @@ -437,7 +439,6 @@ def sibling(self) -> RedBlackTree | None: return self.parent.right else: return self.parent.left - def is_left(self) -> bool: """Returns true iff this node is the left child of its parent.""" if self.parent is None: @@ -451,12 +452,13 @@ def is_right(self) -> bool: return self.parent.right is self def __bool__(self) -> bool: - return True + """空树返回False""" + return self.label is not None def __len__(self) -> int: - """ - Return the number of nodes in this tree. - """ + """正确处理空树情况""" + if self.label is None: + return 0 ln = 1 if self.left: ln += len(self.left) @@ -484,7 +486,6 @@ def postorder_traverse(self) -> Iterator[int | None]: if self.right: yield from self.right.postorder_traverse() yield self.label - def __repr__(self) -> str: if self.left is None and self.right is None: return f"'{self.label} {(self.color and 'red') or 'blk'}'" @@ -502,18 +503,23 @@ def __eq__(self, other: object) -> bool: """Test if two trees are equal.""" if not isinstance(other, RedBlackTree): return NotImplemented - if self.label == other.label: - return self.left == other.left and self.right == other.right - else: + + # 处理空树比较 + if self.label is None and other.label is None: + return True + if self.label != other.label: return False + + # 递归比较子树 + return (self.left == other.left) and (self.right == other.right) + + # 明确表示该类的实例不可哈希 + __hash__ = None def color(node: RedBlackTree | None) -> int: """Returns the color of a node, allowing for None leaves.""" - if node is None: - return 0 - else: - return node.color + return 0 if node is None else node.color """ @@ -555,7 +561,6 @@ def test_rotations() -> bool: right_rot.right.right.right = RedBlackTree(20, parent=right_rot.right.right) return tree == right_rot - def test_insertion_speed() -> bool: """Test that the tree balances inserts to O(log(n)) by doing a lot of them. @@ -639,8 +644,6 @@ def test_floor_ceil() -> bool: if tree.floor(val) != floor or tree.ceil(val) != ceil: return False return True - - def test_min_max() -> bool: """Tests the min and max functions in the tree.""" tree = RedBlackTree(0) @@ -668,7 +671,6 @@ def test_tree_traversal() -> bool: return False return list(tree.postorder_traverse()) == [-16, 8, 20, 24, 22, 16, 0] - def test_tree_chaining() -> bool: """Tests the three different tree chaining functions.""" tree = RedBlackTree(0) @@ -680,10 +682,37 @@ def test_tree_chaining() -> bool: return list(tree.postorder_traverse()) == [-16, 8, 20, 24, 22, 16, 0] -def print_results(msg: str, passes: bool) -> None: - print(str(msg), "works!" if passes else "doesn't work :(") +def test_empty_tree() -> bool: + """Tests behavior with empty trees.""" + tree = RedBlackTree(None) + + # 测试空树属性 + if tree.label is not None or tree.left or tree.right: + return False + + # 测试空树长度 + if len(tree) != 0: + return False + + # 测试空树布尔值 + if tree: + return False + + # 测试空树搜索 + if 0 in tree or tree.search(0): + return False + + # 测试空树删除 + try: + tree.remove(0) + except Exception: + return False + + return True +def print_results(msg: str, passes: bool) -> None: + print(str(msg), "works!" if passes else "doesn't work :(") def pytests() -> None: assert test_rotations() assert test_insert() @@ -692,6 +721,7 @@ def pytests() -> None: assert test_floor_ceil() assert test_tree_traversal() assert test_tree_chaining() + assert test_empty_tree() def main() -> None: @@ -704,7 +734,8 @@ def main() -> None: print_results("Deleting", test_insert_delete()) print_results("Floor and ceil", test_floor_ceil()) print_results("Tree traversal", test_tree_traversal()) - print_results("Tree traversal", test_tree_chaining()) + print_results("Tree chaining", test_tree_chaining()) + print_results("Empty tree handling", test_empty_tree()) print("Testing tree balancing...") print("This should only be a few seconds.") test_insertion_speed() From e56009f4bab1c4e199ac67694bc941316568da85 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 22 Jun 2025 07:18:16 +0000 Subject: [PATCH 078/107] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- other/lru_cache.py | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/other/lru_cache.py b/other/lru_cache.py index e81445905ca7..832a380720e4 100644 --- a/other/lru_cache.py +++ b/other/lru_cache.py @@ -63,6 +63,7 @@ def rotate_left(self) -> RedBlackTree: parent.right = right right.parent = parent return right + def rotate_right(self) -> RedBlackTree: """Rotate the subtree rooted at this node to the right and returns the new root to this subtree. @@ -313,6 +314,7 @@ def check_color_properties(self) -> bool: return False # All properties were met return True + 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. @@ -328,17 +330,17 @@ def black_height(self) -> int | None: # 叶子节点(None)被视为黑色,高度为1 if self is None: return 1 - + # 递归计算左右子树高度 left_bh = RedBlackTree.black_height(self.left) right_bh = RedBlackTree.black_height(self.right) - + # 检查高度是否有效且一致 if left_bh is None or right_bh is None: return None if left_bh != right_bh: return None - + # 返回当前节点高度(黑色节点+1) return left_bh + (1 - self.color) @@ -367,6 +369,7 @@ def search(self, label: int) -> RedBlackTree | None: return None else: return self.left.search(label) + def floor(self, label: int) -> int | None: """Returns the largest element in this tree which is at most label. This method is guaranteed to run in O(log(n)) time.""" @@ -439,6 +442,7 @@ def sibling(self) -> RedBlackTree | None: return self.parent.right else: return self.parent.left + def is_left(self) -> bool: """Returns true iff this node is the left child of its parent.""" if self.parent is None: @@ -486,6 +490,7 @@ def postorder_traverse(self) -> Iterator[int | None]: if self.right: yield from self.right.postorder_traverse() yield self.label + def __repr__(self) -> str: if self.left is None and self.right is None: return f"'{self.label} {(self.color and 'red') or 'blk'}'" @@ -503,13 +508,13 @@ def __eq__(self, other: object) -> bool: """Test if two trees are equal.""" if not isinstance(other, RedBlackTree): return NotImplemented - + # 处理空树比较 if self.label is None and other.label is None: return True if self.label != other.label: return False - + # 递归比较子树 return (self.left == other.left) and (self.right == other.right) @@ -561,6 +566,7 @@ def test_rotations() -> bool: right_rot.right.right.right = RedBlackTree(20, parent=right_rot.right.right) return tree == right_rot + def test_insertion_speed() -> bool: """Test that the tree balances inserts to O(log(n)) by doing a lot of them. @@ -644,6 +650,8 @@ def test_floor_ceil() -> bool: if tree.floor(val) != floor or tree.ceil(val) != ceil: return False return True + + def test_min_max() -> bool: """Tests the min and max functions in the tree.""" tree = RedBlackTree(0) @@ -671,6 +679,7 @@ def test_tree_traversal() -> bool: return False return list(tree.postorder_traverse()) == [-16, 8, 20, 24, 22, 16, 0] + def test_tree_chaining() -> bool: """Tests the three different tree chaining functions.""" tree = RedBlackTree(0) @@ -685,34 +694,36 @@ def test_tree_chaining() -> bool: def test_empty_tree() -> bool: """Tests behavior with empty trees.""" tree = RedBlackTree(None) - + # 测试空树属性 if tree.label is not None or tree.left or tree.right: return False - + # 测试空树长度 if len(tree) != 0: return False - + # 测试空树布尔值 if tree: return False - + # 测试空树搜索 if 0 in tree or tree.search(0): return False - + # 测试空树删除 try: tree.remove(0) except Exception: return False - + return True def print_results(msg: str, passes: bool) -> None: print(str(msg), "works!" if passes else "doesn't work :(") + + def pytests() -> None: assert test_rotations() assert test_insert() From 4d8ebcb92755abcbe2bac637e11e0e49eea52b0a Mon Sep 17 00:00:00 2001 From: lighting9999 Date: Sun, 22 Jun 2025 15:34:36 +0800 Subject: [PATCH 079/107] Update lru_cache.py --- other/lru_cache.py | 864 +++++++-------------------------------------- 1 file changed, 134 insertions(+), 730 deletions(-) diff --git a/other/lru_cache.py b/other/lru_cache.py index 832a380720e4..8e913d7ac9e1 100644 --- a/other/lru_cache.py +++ b/other/lru_cache.py @@ -1,757 +1,161 @@ from __future__ import annotations -from collections.abc import Iterator -from pprint import pformat -from typing import Optional, Tuple +from collections.abc import Callable +from functools import wraps +from typing import Generic, Optional, TypeVar +T = TypeVar("T") +U = TypeVar("U") -class RedBlackTree: - """ - A Red-Black tree, which is a self-balancing BST (binary search - tree). - This tree has similar performance to AVL trees, but the balancing is - less strict, so it will perform faster for writing/deleting nodes - 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 - Unless otherwise specified, all asymptotic runtimes are specified in - terms of the size of the tree. - """ - - def __init__( - self, - label: int | None = None, - color: int = 0, - parent: RedBlackTree | None = None, - left: RedBlackTree | None = None, - right: RedBlackTree | None = None, - ) -> None: - """Initialize a new Red-Black Tree node with the given values: - label: The value associated with this node - color: 0 if black, 1 if red - parent: The parent to this node - left: This node's left child - right: This node's right child - """ - self.label = label - self.parent = parent - self.left = left - self.right = right - self.color = color - - # Here are functions which are specific to red-black trees - - def rotate_left(self) -> RedBlackTree: - """Rotate the subtree rooted at this node to the left and - returns the new root to this subtree. - Performing one rotation can be done in O(1). - """ - parent = self.parent - right = self.right - if right is None: - return self - self.right = right.left - if self.right: - self.right.parent = self - self.parent = right - right.left = self - if parent is not None: - if parent.left == self: - parent.left = right - else: - parent.right = right - right.parent = parent - return right - - def rotate_right(self) -> RedBlackTree: - """Rotate the subtree rooted at this node to the right and - returns the new root to this subtree. - Performing one rotation can be done in O(1). - """ - if self.left is None: - return self - parent = self.parent - left = self.left - self.left = left.right - if self.left: - self.left.parent = self - self.parent = left - left.right = self - if parent is not None: - if parent.right is self: - parent.right = left - else: - parent.left = left - left.parent = parent - return left - - def insert(self, label: int) -> RedBlackTree: - """Inserts label into the subtree rooted at self, performs any - rotations necessary to maintain balance, and then returns the - new root to this subtree (likely self). - This is guaranteed to run in O(log(n)) time. - """ - if self.label is None: - # Only possible with an empty tree - self.label = label - return self - if self.label == label: - return self - elif self.label > label: - if self.left: - # 更新:递归插入后更新左子树引用 - self.left = self.left.insert(label) - else: - self.left = RedBlackTree(label, 1, self) - self.left._insert_repair() - elif self.right: - # 更新:递归插入后更新右子树引用 - self.right = self.right.insert(label) - else: - self.right = RedBlackTree(label, 1, self) - self.right._insert_repair() - return self.parent or self - - def _insert_repair(self) -> None: - """Repair the coloring from inserting into a tree.""" - if self.parent is None: - # This node is the root, so it just needs to be black - self.color = 0 - elif color(self.parent) == 0: - # If the parent is black, then it just needs to be red - self.color = 1 - else: - uncle = self.parent.sibling - if color(uncle) == 0: - if self.is_left() and self.parent.is_right(): - self.parent.rotate_right() - if self.right: - self.right._insert_repair() - elif self.is_right() and self.parent.is_left(): - self.parent.rotate_left() - if self.left: - self.left._insert_repair() - elif self.is_left(): - if self.grandparent: - self.grandparent.rotate_right() - self.parent.color = 0 - if self.parent.right: - self.parent.right.color = 1 - else: - if self.grandparent: - self.grandparent.rotate_left() - self.parent.color = 0 - if self.parent.left: - self.parent.left.color = 1 - else: - self.parent.color = 0 - if uncle and self.grandparent: - uncle.color = 0 - self.grandparent.color = 1 - self.grandparent._insert_repair() - - def remove(self, label: int) -> RedBlackTree: - """Remove label from this tree, returning the new root of the subtree.""" - if self.label == label: - if self.left and self.right: - # It's easier to balance a node with at most one child, - # so we replace this node with the greatest one less than - # it and remove that. - value = self.left.get_max() - if value is not None: - self.label = value - # 更新:递归删除后更新左子树引用 - self.left = self.left.remove(value) - else: - # This node has at most one non-None child, so we don't - # need to replace - child = self.left or self.right - if self.color == 1: - # This node is red, and its child is black - # The only way this happens to a node with one child - # is if both children are None leaves. - # We can just remove this node and call it a day. - if self.parent: - if self.is_left(): - self.parent.left = None - else: - self.parent.right = 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: - 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 = self.left.remove(label) - elif self.right: - # 更新:递归删除后更新右子树引用 - self.right = self.right.remove(label) - return self.parent or self - - def _remove_repair(self) -> None: - """Repair the coloring of the tree that may have been messed up.""" - if ( - self.parent is None - or self.sibling is None - or self.parent.sibling is None - or self.grandparent is None - ): - return - if color(self.sibling) == 1: - self.sibling.color = 0 - self.parent.color = 1 - if self.is_left(): - self.parent.rotate_left() - else: - self.parent.rotate_right() - if ( - color(self.parent) == 0 - and color(self.sibling) == 0 - and color(self.sibling.left) == 0 - and color(self.sibling.right) == 0 - ): - self.sibling.color = 1 - self.parent._remove_repair() - return - if ( - color(self.parent) == 1 - and color(self.sibling) == 0 - and color(self.sibling.left) == 0 - and color(self.sibling.right) == 0 - ): - self.sibling.color = 1 - self.parent.color = 0 - return - if ( - self.is_left() - and color(self.sibling) == 0 - and color(self.sibling.right) == 0 - and color(self.sibling.left) == 1 - ): - self.sibling.rotate_right() - self.sibling.color = 0 - if self.sibling.right: - self.sibling.right.color = 1 - if ( - self.is_right() - and color(self.sibling) == 0 - and color(self.sibling.right) == 1 - and color(self.sibling.left) == 0 - ): - self.sibling.rotate_left() - self.sibling.color = 0 - if self.sibling.left: - self.sibling.left.color = 1 - if ( - self.is_left() - and color(self.sibling) == 0 - and color(self.sibling.right) == 1 - ): - self.parent.rotate_left() - self.grandparent.color = self.parent.color - self.parent.color = 0 - self.parent.sibling.color = 0 - if ( - self.is_right() - and color(self.sibling) == 0 - and color(self.sibling.left) == 1 - ): - self.parent.rotate_right() - self.grandparent.color = self.parent.color - self.parent.color = 0 - self.parent.sibling.color = 0 - - def check_color_properties(self) -> bool: - """Check the coloring of the tree, and return True iff the tree - is colored in a way which matches these five properties: - (wording stolen from wikipedia article) - 1. Each node is either red or black. - 2. The root node is black. - 3. All leaves are black. - 4. If a node is red, then both its children are black. - 5. Every path from any node to all of its descendent NIL nodes - has the same number of black nodes. - This function runs in O(n) time, because properties 4 and 5 take - that long to check. - """ - # I assume property 1 to hold because there is nothing that can - # make the color be anything other than 0 or 1. - # Property 2 - if self.color: - # The root was red - print("Property 2") - return False - # Property 3 does not need to be checked, because None is assumed - # to be black and is all the leaves. - # Property 4 - if not self.check_coloring(): - print("Property 4") - return False - # Property 5 - if self.black_height() is None: - print("Property 5") - return False - # All properties were met - return True - - 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 and 1 in (color(self.left), color(self.right)): - return False - if self.left and not self.left.check_coloring(): - return False - return not (self.right and not self.right.check_coloring()) - - def black_height(self) -> int | None: - """修正的黑色高度计算方法""" - # 叶子节点(None)被视为黑色,高度为1 - if self is None: - return 1 - - # 递归计算左右子树高度 - left_bh = RedBlackTree.black_height(self.left) - right_bh = RedBlackTree.black_height(self.right) - - # 检查高度是否有效且一致 - if left_bh is None or right_bh is None: - return None - if left_bh != right_bh: - return None - - # 返回当前节点高度(黑色节点+1) - return left_bh + (1 - self.color) - # Here are functions which are general to all binary search trees - - def __contains__(self, label: int) -> bool: - """Search through the tree for label, returning True iff it is - found somewhere in the tree. - Guaranteed to run in O(log(n)) time. - """ - return self.search(label) is not None - - def search(self, label: int) -> RedBlackTree | None: - """Search through the tree for label, returning its node if - it's found, and None otherwise. - This method is guaranteed to run in O(log(n)) time. - """ - if self.label == label: - return self - elif self.label is not None and label > self.label: - if self.right is None: - return None - else: - return self.right.search(label) - elif self.left is None: - return None - else: - return self.left.search(label) - - def floor(self, label: int) -> int | None: - """Returns the largest element in this tree which is at most label. - This method is guaranteed to run in O(log(n)) time.""" - if self.label == label: - return self.label - elif self.label is not None and self.label > label: - if self.left: - return self.left.floor(label) - else: - return None - else: - if self.right: - attempt = self.right.floor(label) - if attempt is not None: - return attempt - return self.label - - def ceil(self, label: int) -> int | None: - """Returns the smallest element in this tree which is at least label. - This method is guaranteed to run in O(log(n)) time. - """ - if self.label == label: - return self.label - elif self.label is not None and self.label < label: - if self.right: - return self.right.ceil(label) - else: - return None - else: - if self.left: - attempt = self.left.ceil(label) - if attempt is not None: - return attempt - return self.label - - def get_max(self) -> int | None: - """Returns the largest element in this tree. - This method is guaranteed to run in O(log(n)) time. - """ - if self.right: - # Go as far right as possible - return self.right.get_max() - else: - return self.label - - def get_min(self) -> int | None: - """Returns the smallest element in this tree. - This method is guaranteed to run in O(log(n)) time. - """ - if self.left: - # Go as far left as possible - return self.left.get_min() - else: - return self.label - - @property - def grandparent(self) -> RedBlackTree | None: - """Get the current node's grandparent, or None if it doesn't exist.""" - if self.parent is None: - return None - else: - return self.parent.parent - - @property - def sibling(self) -> RedBlackTree | None: - """Get the current node's sibling, or None if it doesn't exist.""" - if self.parent is None: - return None - elif self.parent.left is self: - return self.parent.right - else: - return self.parent.left - - 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 - - def is_right(self) -> bool: - """Returns true iff this node is the right child of its parent.""" - if self.parent is None: - return False - return self.parent.right is self - - def __bool__(self) -> bool: - """空树返回False""" - return self.label is not None - - def __len__(self) -> int: - """正确处理空树情况""" - if self.label is None: - return 0 - ln = 1 - if self.left: - ln += len(self.left) - if self.right: - ln += len(self.right) - return ln - - def preorder_traverse(self) -> Iterator[int | None]: - yield self.label - if self.left: - yield from self.left.preorder_traverse() - if self.right: - yield from self.right.preorder_traverse() +class DoubleLinkedListNode(Generic[T, U]): + """ + Double Linked List Node built specifically for LRU Cache - def inorder_traverse(self) -> Iterator[int | None]: - if self.left: - yield from self.left.inorder_traverse() - yield self.label - if self.right: - yield from self.right.inorder_traverse() + >>> DoubleLinkedListNode(1,1) + Node: key: 1, val: 1, has next: False, has prev: False + """ - def postorder_traverse(self) -> Iterator[int | None]: - if self.left: - yield from self.left.postorder_traverse() - if self.right: - yield from self.right.postorder_traverse() - yield self.label + def __init__(self, key: Optional[T], val: Optional[U]) -> None: + self.key = key + self.val = val + self.next: Optional[DoubleLinkedListNode[T, U]] = None + self.prev: Optional[DoubleLinkedListNode[T, U]] = None def __repr__(self) -> str: - if self.left is None and self.right is None: - return f"'{self.label} {(self.color and 'red') or 'blk'}'" - return pformat( - { - f"{self.label} {(self.color and 'red') or 'blk'}": ( - self.left, - self.right, - ) - }, - indent=1, + return ( + f"Node: key: {self.key}, val: {self.val}, " + f"has next: {bool(self.next)}, has prev: {bool(self.prev)}" ) - def __eq__(self, other: object) -> bool: - """Test if two trees are equal.""" - if not isinstance(other, RedBlackTree): - return NotImplemented - - # 处理空树比较 - if self.label is None and other.label is None: - return True - if self.label != other.label: - return False - - # 递归比较子树 - return (self.left == other.left) and (self.right == other.right) - - # 明确表示该类的实例不可哈希 - __hash__ = None - - -def color(node: RedBlackTree | None) -> int: - """Returns the color of a node, allowing for None leaves.""" - return 0 if node is None else node.color - - -""" -Code for testing the various -functions of the red-black tree. -""" - - -def test_rotations() -> bool: - """Test that the rotate_left and rotate_right functions work.""" - # Make a tree to test on - tree = RedBlackTree(0) - tree.left = RedBlackTree(-10, parent=tree) - tree.right = RedBlackTree(10, parent=tree) - tree.left.left = RedBlackTree(-20, parent=tree.left) - tree.left.right = RedBlackTree(-5, parent=tree.left) - tree.right.left = RedBlackTree(5, parent=tree.right) - tree.right.right = RedBlackTree(20, parent=tree.right) - # Make the right rotation - left_rot = RedBlackTree(10) - left_rot.left = RedBlackTree(0, parent=left_rot) - left_rot.left.left = RedBlackTree(-10, parent=left_rot.left) - left_rot.left.right = RedBlackTree(5, parent=left_rot.left) - left_rot.left.left.left = RedBlackTree(-20, parent=left_rot.left.left) - left_rot.left.left.right = RedBlackTree(-5, parent=left_rot.left.left) - left_rot.right = RedBlackTree(20, parent=left_rot) - tree = tree.rotate_left() - if tree != left_rot: - return False - tree = tree.rotate_right() - tree = tree.rotate_right() - # Make the left rotation - right_rot = RedBlackTree(-10) - right_rot.left = RedBlackTree(-20, parent=right_rot) - right_rot.right = RedBlackTree(0, parent=right_rot) - right_rot.right.left = RedBlackTree(-5, parent=right_rot.right) - 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) - return tree == right_rot - -def test_insertion_speed() -> bool: - """Test that the tree balances inserts to O(log(n)) by doing a lot - of them. +class DoubleLinkedList(Generic[T, U]): """ - tree = RedBlackTree(-1) - for i in range(300000): - tree = tree.insert(i) - return True - - -def test_insert() -> bool: - """Test the insert() method of the tree correctly balances, colors, - and inserts. - """ - tree = RedBlackTree(0) - tree.insert(8) - tree.insert(-8) - tree.insert(4) - tree.insert(12) - tree.insert(10) - tree.insert(11) - ans = RedBlackTree(0, 0) - ans.left = RedBlackTree(-8, 0, ans) - ans.right = RedBlackTree(8, 1, ans) - ans.right.left = RedBlackTree(4, 0, ans.right) - ans.right.right = RedBlackTree(11, 0, ans.right) - ans.right.right.left = RedBlackTree(10, 1, ans.right.right) - ans.right.right.right = RedBlackTree(12, 1, ans.right.right) - return tree == ans - - -def test_insert_and_search() -> bool: - """Tests searching through the tree for values.""" - tree = RedBlackTree(0) - tree.insert(8) - tree.insert(-8) - tree.insert(4) - tree.insert(12) - tree.insert(10) - tree.insert(11) - if any(i in tree for i in (5, -6, -10, 13)): - # Found something not in there - return False - # Find all these things in there - return all(i in tree for i in (11, 12, -8, 0)) - - -def test_insert_delete() -> bool: - """Test the insert() and delete() method of the tree, verifying the - insertion and removal of elements, and the balancing of the tree. + Double Linked List built specifically for LRU Cache + ... [docstring unchanged] ... """ - tree = RedBlackTree(0) - tree = tree.insert(-12) - tree = tree.insert(8) - tree = tree.insert(-8) - tree = tree.insert(15) - tree = tree.insert(4) - tree = tree.insert(12) - tree = tree.insert(10) - tree = tree.insert(9) - tree = tree.insert(11) - tree = tree.remove(15) - tree = tree.remove(-12) - tree = tree.remove(9) - if not tree.check_color_properties(): - return False - return list(tree.inorder_traverse()) == [-8, 0, 4, 8, 10, 11, 12] - - -def test_floor_ceil() -> bool: - """Tests the floor and ceiling functions in the tree.""" - tree = RedBlackTree(0) - tree.insert(-16) - tree.insert(16) - tree.insert(8) - tree.insert(24) - tree.insert(20) - tree.insert(22) - tuples = [(-20, None, -16), (-10, -16, 0), (8, 8, 8), (50, 24, None)] - for val, floor, ceil in tuples: - if tree.floor(val) != floor or tree.ceil(val) != ceil: - return False - return True + def __init__(self) -> None: + self.head: DoubleLinkedListNode[T, U] = DoubleLinkedListNode(None, None) + self.rear: DoubleLinkedListNode[T, U] = DoubleLinkedListNode(None, None) + self.head.next, self.rear.prev = self.rear, self.head -def test_min_max() -> bool: - """Tests the min and max functions in the tree.""" - tree = RedBlackTree(0) - tree.insert(-16) - tree.insert(16) - tree.insert(8) - tree.insert(24) - tree.insert(20) - tree.insert(22) - return not (tree.get_max() != 22 or tree.get_min() != -16) - - -def test_tree_traversal() -> bool: - """Tests the three different tree traversal functions.""" - tree = RedBlackTree(0) - tree = tree.insert(-16) - tree.insert(16) - tree.insert(8) - tree.insert(24) - tree.insert(20) - tree.insert(22) - if list(tree.inorder_traverse()) != [-16, 0, 8, 16, 20, 22, 24]: - return False - if list(tree.preorder_traverse()) != [0, -16, 16, 8, 22, 20, 24]: - return False - return list(tree.postorder_traverse()) == [-16, 8, 20, 24, 22, 16, 0] - - -def test_tree_chaining() -> bool: - """Tests the three different tree chaining functions.""" - tree = RedBlackTree(0) - tree = tree.insert(-16).insert(16).insert(8).insert(24).insert(20).insert(22) - if list(tree.inorder_traverse()) != [-16, 0, 8, 16, 20, 22, 24]: - return False - if list(tree.preorder_traverse()) != [0, -16, 16, 8, 22, 20, 24]: - return False - return list(tree.postorder_traverse()) == [-16, 8, 20, 24, 22, 16, 0] - - -def test_empty_tree() -> bool: - """Tests behavior with empty trees.""" - tree = RedBlackTree(None) - - # 测试空树属性 - if tree.label is not None or tree.left or tree.right: - return False - - # 测试空树长度 - if len(tree) != 0: - return False - - # 测试空树布尔值 - if tree: - return False - - # 测试空树搜索 - if 0 in tree or tree.search(0): - return False + def __repr__(self) -> str: + rep = ["DoubleLinkedList"] + node = self.head + while node.next is not None: + rep.append(str(node)) + node = node.next + rep.append(str(self.rear)) + return ",\n ".join(rep) + + def add(self, node: DoubleLinkedListNode[T, U]) -> None: + """Adds the given node to the end of the list (before rear)""" + previous = self.rear.prev + if previous is None: + raise ValueError("Invalid list state: rear.prev is None") + + previous.next = node + node.prev = previous + self.rear.prev = node + node.next = self.rear + def remove( + self, node: DoubleLinkedListNode[T, U] + ) -> Optional[DoubleLinkedListNode[T, U]]: + """Removes and returns the given node from the list""" + if node.prev is None or node.next is None: + return None - # 测试空树删除 - try: - tree.remove(0) - except Exception: - return False + node.prev.next = node.next + node.next.prev = node.prev + node.prev = None + node.next = None + return node - return True +class LRUCache(Generic[T, U]): + """ + LRU Cache to store a given capacity of data + ... [docstring unchanged] ... + """ -def print_results(msg: str, passes: bool) -> None: - print(str(msg), "works!" if passes else "doesn't work :(") + def __init__(self, capacity: int) -> None: + self.list: DoubleLinkedList[T, U] = DoubleLinkedList() + self.capacity = capacity + self.num_keys = 0 + self.hits = 0 + self.miss = 0 + self.cache: dict[T, DoubleLinkedListNode[T, U]] = {} + def __repr__(self) -> str: + return ( + f"CacheInfo(hits={self.hits}, misses={self.miss}, " + f"capacity={self.capacity}, current size={self.num_keys})" + ) -def pytests() -> None: - assert test_rotations() - assert test_insert() - assert test_insert_and_search() - assert test_insert_delete() - assert test_floor_ceil() - assert test_tree_traversal() - assert test_tree_chaining() - assert test_empty_tree() + def __contains__(self, key: T) -> bool: + return key in self.cache + def get(self, key: T) -> Optional[U]: + """Returns the value for the input key""" + if key in self.cache: + self.hits += 1 + value_node = self.cache[key] + node = self.list.remove(value_node) + if node is None: + return None + self.list.add(node) + return node.val + self.miss += 1 + return None + def put(self, key: T, value: U) -> None: + """Sets the value for the input key""" + if key in self.cache: + node = self.list.remove(self.cache[key]) + if node is None: + return + node.val = value + self.list.add(node) + return -def main() -> None: - """ - >>> pytests() - """ - print_results("Rotating right and left", test_rotations()) - print_results("Inserting", test_insert()) - print_results("Searching", test_insert_and_search()) - print_results("Deleting", test_insert_delete()) - print_results("Floor and ceil", test_floor_ceil()) - print_results("Tree traversal", test_tree_traversal()) - print_results("Tree chaining", test_tree_chaining()) - print_results("Empty tree handling", test_empty_tree()) - print("Testing tree balancing...") - print("This should only be a few seconds.") - test_insertion_speed() - print("Done!") + if self.num_keys >= self.capacity: + first_node = self.list.head.next + if first_node is None or first_node.key is None: + return + if self.list.remove(first_node) is not None: + del self.cache[first_node.key] + self.num_keys -= 1 + + new_node = DoubleLinkedListNode(key, value) + self.cache[key] = new_node + self.list.add(new_node) + self.num_keys += 1 + + @classmethod + def decorator( + cls, size: int = 128 + ) -> Callable[[Callable[..., U]], Callable[..., U]]: + """Decorator version of LRU Cache""" + def decorator_func(func: Callable[..., U]) -> Callable[..., U]: + cache_instance = cls(size) + + @wraps(func) + def wrapper(*args: T, **kwargs: T) -> U: + key = (args, tuple(kwargs.items())) + result = cache_instance.get(key) + if result is None: + result = func(*args, **kwargs) + cache_instance.put(key, result) + return result + def cache_info() -> LRUCache: + return cache_instance + + setattr(wrapper, "cache_info", cache_info) + return wrapper + + return decorator_func if __name__ == "__main__": - main() + import doctest + doctest.testmod() From 824f2bcf68ac919652965449fdcd0d2476c72b5a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 22 Jun 2025 07:34:59 +0000 Subject: [PATCH 080/107] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- other/lru_cache.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/other/lru_cache.py b/other/lru_cache.py index 8e913d7ac9e1..52b0bbf77b6f 100644 --- a/other/lru_cache.py +++ b/other/lru_cache.py @@ -54,11 +54,12 @@ def add(self, node: DoubleLinkedListNode[T, U]) -> None: previous = self.rear.prev if previous is None: raise ValueError("Invalid list state: rear.prev is None") - + previous.next = node node.prev = previous self.rear.prev = node node.next = self.rear + def remove( self, node: DoubleLinkedListNode[T, U] ) -> Optional[DoubleLinkedListNode[T, U]]: @@ -108,6 +109,7 @@ def get(self, key: T) -> Optional[U]: return node.val self.miss += 1 return None + def put(self, key: T, value: U) -> None: """Sets the value for the input key""" if key in self.cache: @@ -136,6 +138,7 @@ def decorator( cls, size: int = 128 ) -> Callable[[Callable[..., U]], Callable[..., U]]: """Decorator version of LRU Cache""" + def decorator_func(func: Callable[..., U]) -> Callable[..., U]: cache_instance = cls(size) @@ -147,6 +150,7 @@ def wrapper(*args: T, **kwargs: T) -> U: result = func(*args, **kwargs) cache_instance.put(key, result) return result + def cache_info() -> LRUCache: return cache_instance @@ -158,4 +162,5 @@ def cache_info() -> LRUCache: if __name__ == "__main__": import doctest + doctest.testmod() From e92bb19224d6e6aea80c25332c00ca55e67bd994 Mon Sep 17 00:00:00 2001 From: lighting9999 Date: Sun, 22 Jun 2025 15:42:11 +0800 Subject: [PATCH 081/107] Update lru_cache.py --- other/lru_cache.py | 70 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 49 insertions(+), 21 deletions(-) diff --git a/other/lru_cache.py b/other/lru_cache.py index 52b0bbf77b6f..4aeb32a52a66 100644 --- a/other/lru_cache.py +++ b/other/lru_cache.py @@ -1,11 +1,24 @@ from __future__ import annotations -from collections.abc import Callable +from collections.abc import Callable, Hashable from functools import wraps -from typing import Generic, Optional, TypeVar +from typing import Generic, TypeVar, Any, cast, overload, TYPE_CHECKING +from typing_extensions import ParamSpec -T = TypeVar("T") +if TYPE_CHECKING: + from typing_extensions import TypeAlias + +T = TypeVar("T", bound=Hashable) U = TypeVar("U") +P = ParamSpec("P") +R = TypeVar("R") + +if TYPE_CHECKING: + NodeKey: TypeAlias = T | None + NodeValue: TypeAlias = U | None +else: + NodeKey = TypeVar("NodeKey", bound=Hashable) + NodeValue = TypeVar("NodeValue") class DoubleLinkedListNode(Generic[T, U]): @@ -16,11 +29,11 @@ class DoubleLinkedListNode(Generic[T, U]): Node: key: 1, val: 1, has next: False, has prev: False """ - def __init__(self, key: Optional[T], val: Optional[U]) -> None: + def __init__(self, key: NodeKey, val: NodeValue) -> None: self.key = key self.val = val - self.next: Optional[DoubleLinkedListNode[T, U]] = None - self.prev: Optional[DoubleLinkedListNode[T, U]] = None + self.next: DoubleLinkedListNode[T, U] | None = None + self.prev: DoubleLinkedListNode[T, U] | None = None def __repr__(self) -> str: return ( @@ -48,13 +61,12 @@ def __repr__(self) -> str: node = node.next rep.append(str(self.rear)) return ",\n ".join(rep) - def add(self, node: DoubleLinkedListNode[T, U]) -> None: """Adds the given node to the end of the list (before rear)""" previous = self.rear.prev if previous is None: raise ValueError("Invalid list state: rear.prev is None") - + previous.next = node node.prev = previous self.rear.prev = node @@ -62,7 +74,7 @@ def add(self, node: DoubleLinkedListNode[T, U]) -> None: def remove( self, node: DoubleLinkedListNode[T, U] - ) -> Optional[DoubleLinkedListNode[T, U]]: + ) -> DoubleLinkedListNode[T, U] | None: """Removes and returns the given node from the list""" if node.prev is None or node.next is None: return None @@ -97,7 +109,7 @@ def __repr__(self) -> str: def __contains__(self, key: T) -> bool: return key in self.cache - def get(self, key: T) -> Optional[U]: + def get(self, key: T) -> U | None: """Returns the value for the input key""" if key in self.cache: self.hits += 1 @@ -119,7 +131,6 @@ def put(self, key: T, value: U) -> None: node.val = value self.list.add(node) return - if self.num_keys >= self.capacity: first_node = self.list.head.next if first_node is None or first_node.key is None: @@ -133,28 +144,46 @@ def put(self, key: T, value: U) -> None: self.list.add(new_node) self.num_keys += 1 + @overload @classmethod def decorator( cls, size: int = 128 - ) -> Callable[[Callable[..., U]], Callable[..., U]]: - """Decorator version of LRU Cache""" + ) -> Callable[[Callable[P, R]], Callable[P, R]]: + ... + + @overload + @classmethod + def decorator( + cls, func: Callable[P, R] + ) -> Callable[P, R]: + ... - def decorator_func(func: Callable[..., U]) -> Callable[..., U]: - cache_instance = cls(size) + @classmethod + def decorator( + cls, size: int | Callable[P, R] = 128 + ) -> Callable[[Callable[P, R]], Callable[P, R]] | Callable[P, R]: + """Decorator version of LRU Cache""" + if callable(size): + # Called without parentheses (@LRUCache.decorator) + return cls.decorator()(size) + + def decorator_func(func: Callable[P, R]) -> Callable[P, R]: + cache_instance = cls[Any, R](size) # type: ignore[valid-type] @wraps(func) - def wrapper(*args: T, **kwargs: T) -> U: - key = (args, tuple(kwargs.items())) + def wrapper(*args: P.args, **kwargs: P.kwargs) -> R: + # Create normalized key + sorted_kwargs = tuple(sorted(kwargs.items(), key=lambda x: x[0])) + key = (args, sorted_kwargs) result = cache_instance.get(key) if result is None: result = func(*args, **kwargs) cache_instance.put(key, result) return result - - def cache_info() -> LRUCache: + def cache_info() -> LRUCache[Any, R]: # type: ignore[valid-type] return cache_instance - setattr(wrapper, "cache_info", cache_info) + wrapper.cache_info = cache_info # Direct assignment return wrapper return decorator_func @@ -162,5 +191,4 @@ def cache_info() -> LRUCache: if __name__ == "__main__": import doctest - doctest.testmod() From ffecd1b350ae377d5152f86d000d66f1027363e2 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 22 Jun 2025 07:42:34 +0000 Subject: [PATCH 082/107] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- other/lru_cache.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/other/lru_cache.py b/other/lru_cache.py index 4aeb32a52a66..c0579127f5f5 100644 --- a/other/lru_cache.py +++ b/other/lru_cache.py @@ -61,12 +61,13 @@ def __repr__(self) -> str: node = node.next rep.append(str(self.rear)) return ",\n ".join(rep) + def add(self, node: DoubleLinkedListNode[T, U]) -> None: """Adds the given node to the end of the list (before rear)""" previous = self.rear.prev if previous is None: raise ValueError("Invalid list state: rear.prev is None") - + previous.next = node node.prev = previous self.rear.prev = node @@ -148,15 +149,11 @@ def put(self, key: T, value: U) -> None: @classmethod def decorator( cls, size: int = 128 - ) -> Callable[[Callable[P, R]], Callable[P, R]]: - ... - + ) -> Callable[[Callable[P, R]], Callable[P, R]]: ... + @overload @classmethod - def decorator( - cls, func: Callable[P, R] - ) -> Callable[P, R]: - ... + def decorator(cls, func: Callable[P, R]) -> Callable[P, R]: ... @classmethod def decorator( @@ -166,7 +163,7 @@ def decorator( if callable(size): # Called without parentheses (@LRUCache.decorator) return cls.decorator()(size) - + def decorator_func(func: Callable[P, R]) -> Callable[P, R]: cache_instance = cls[Any, R](size) # type: ignore[valid-type] @@ -180,6 +177,7 @@ def wrapper(*args: P.args, **kwargs: P.kwargs) -> R: result = func(*args, **kwargs) cache_instance.put(key, result) return result + def cache_info() -> LRUCache[Any, R]: # type: ignore[valid-type] return cache_instance @@ -191,4 +189,5 @@ def cache_info() -> LRUCache[Any, R]: # type: ignore[valid-type] if __name__ == "__main__": import doctest + doctest.testmod() From 072f3685658b45ada178dcc0e8be70eb92b02ec1 Mon Sep 17 00:00:00 2001 From: lighting9999 Date: Sun, 22 Jun 2025 16:43:08 +0800 Subject: [PATCH 083/107] Update lru_cache.py --- other/lru_cache.py | 171 ++++++++++++++++----------------------------- 1 file changed, 59 insertions(+), 112 deletions(-) diff --git a/other/lru_cache.py b/other/lru_cache.py index c0579127f5f5..541aff7b88ea 100644 --- a/other/lru_cache.py +++ b/other/lru_cache.py @@ -2,32 +2,23 @@ from collections.abc import Callable, Hashable from functools import wraps -from typing import Generic, TypeVar, Any, cast, overload, TYPE_CHECKING -from typing_extensions import ParamSpec +from typing import Any, Generic, ParamSpec, TypeVar, overload, TYPE_CHECKING if TYPE_CHECKING: - from typing_extensions import TypeAlias + type NodeKey = T | None + type NodeValue = U | None +else: + NodeKey = TypeVar("NodeKey", bound=Hashable) + NodeValue = TypeVar("NodeValue") T = TypeVar("T", bound=Hashable) U = TypeVar("U") P = ParamSpec("P") R = TypeVar("R") -if TYPE_CHECKING: - NodeKey: TypeAlias = T | None - NodeValue: TypeAlias = U | None -else: - NodeKey = TypeVar("NodeKey", bound=Hashable) - NodeValue = TypeVar("NodeValue") - class DoubleLinkedListNode(Generic[T, U]): - """ - Double Linked List Node built specifically for LRU Cache - - >>> DoubleLinkedListNode(1,1) - Node: key: 1, val: 1, has next: False, has prev: False - """ + """Node built for LRU Cache""" def __init__(self, key: NodeKey, val: NodeValue) -> None: self.key = key @@ -36,17 +27,11 @@ def __init__(self, key: NodeKey, val: NodeValue) -> None: self.prev: DoubleLinkedListNode[T, U] | None = None def __repr__(self) -> str: - return ( - f"Node: key: {self.key}, val: {self.val}, " - f"has next: {bool(self.next)}, has prev: {bool(self.prev)}" - ) + return f"Node(key={self.key}, val={self.val})" class DoubleLinkedList(Generic[T, U]): - """ - Double Linked List built specifically for LRU Cache - ... [docstring unchanged] ... - """ + """Double Linked List for LRU Cache""" def __init__(self) -> None: self.head: DoubleLinkedListNode[T, U] = DoubleLinkedListNode(None, None) @@ -54,140 +39,102 @@ def __init__(self) -> None: self.head.next, self.rear.prev = self.rear, self.head def __repr__(self) -> str: - rep = ["DoubleLinkedList"] - node = self.head - while node.next is not None: - rep.append(str(node)) - node = node.next - rep.append(str(self.rear)) - return ",\n ".join(rep) + nodes = [] + current = self.head + while current: + nodes.append(repr(current)) + current = current.next + return f"LinkedList({nodes})" def add(self, node: DoubleLinkedListNode[T, U]) -> None: - """Adds the given node to the end of the list (before rear)""" - previous = self.rear.prev - if previous is None: - raise ValueError("Invalid list state: rear.prev is None") - - previous.next = node - node.prev = previous + """Add node to list end""" + prev = self.rear.prev + if not prev: + raise ValueError("Invalid list state") + + prev.next = node + node.prev = prev self.rear.prev = node node.next = self.rear - def remove( - self, node: DoubleLinkedListNode[T, U] - ) -> DoubleLinkedListNode[T, U] | None: - """Removes and returns the given node from the list""" - if node.prev is None or node.next is None: + def remove(self, node: DoubleLinkedListNode[T, U]) -> DoubleLinkedListNode[T, U] | None: + """Remove node from list""" + if not node.prev or not node.next: return None - + node.prev.next = node.next node.next.prev = node.prev - node.prev = None - node.next = None + node.prev = node.next = None return node class LRUCache(Generic[T, U]): - """ - LRU Cache to store a given capacity of data - ... [docstring unchanged] ... - """ + """LRU Cache implementation""" def __init__(self, capacity: int) -> None: - self.list: DoubleLinkedList[T, U] = DoubleLinkedList() + self.list = DoubleLinkedList[T, U]() self.capacity = capacity - self.num_keys = 0 + self.size = 0 self.hits = 0 - self.miss = 0 + self.misses = 0 self.cache: dict[T, DoubleLinkedListNode[T, U]] = {} def __repr__(self) -> str: - return ( - f"CacheInfo(hits={self.hits}, misses={self.miss}, " - f"capacity={self.capacity}, current size={self.num_keys})" - ) + return f"Cache(hits={self.hits}, misses={self.misses}, cap={self.capacity}, size={self.size})" def __contains__(self, key: T) -> bool: return key in self.cache def get(self, key: T) -> U | None: - """Returns the value for the input key""" + """Get value for key""" if key in self.cache: self.hits += 1 - value_node = self.cache[key] - node = self.list.remove(value_node) - if node is None: - return None - self.list.add(node) + node = self.cache[key] + if self.list.remove(node): + self.list.add(node) return node.val - self.miss += 1 + self.misses += 1 return None - def put(self, key: T, value: U) -> None: - """Sets the value for the input key""" + """Set value for key""" if key in self.cache: - node = self.list.remove(self.cache[key]) - if node is None: - return - node.val = value - self.list.add(node) + node = self.cache[key] + if self.list.remove(node): + node.val = value + self.list.add(node) return - if self.num_keys >= self.capacity: - first_node = self.list.head.next - if first_node is None or first_node.key is None: - return - if self.list.remove(first_node) is not None: - del self.cache[first_node.key] - self.num_keys -= 1 - - new_node = DoubleLinkedListNode(key, value) - self.cache[key] = new_node - self.list.add(new_node) - self.num_keys += 1 - @overload - @classmethod - def decorator( - cls, size: int = 128 - ) -> Callable[[Callable[P, R]], Callable[P, R]]: ... + if self.size >= self.capacity: + first = self.list.head.next + if first and first.key and self.list.remove(first): + del self.cache[first.key] + self.size -= 1 - @overload - @classmethod - def decorator(cls, func: Callable[P, R]) -> Callable[P, R]: ... + new_node: DoubleLinkedListNode[T, U] = DoubleLinkedListNode(key, value) + self.cache[key] = new_node + self.list.add(new_node) + self.size += 1 @classmethod - def decorator( - cls, size: int | Callable[P, R] = 128 - ) -> Callable[[Callable[P, R]], Callable[P, R]] | Callable[P, R]: - """Decorator version of LRU Cache""" - if callable(size): - # Called without parentheses (@LRUCache.decorator) - return cls.decorator()(size) - + def decorator(cls, size: int = 128) -> Callable[[Callable[P, R]], Callable[P, R]]: + """LRU Cache decorator""" def decorator_func(func: Callable[P, R]) -> Callable[P, R]: - cache_instance = cls[Any, R](size) # type: ignore[valid-type] + cache = cls[Any, R](size) @wraps(func) def wrapper(*args: P.args, **kwargs: P.kwargs) -> R: - # Create normalized key - sorted_kwargs = tuple(sorted(kwargs.items(), key=lambda x: x[0])) - key = (args, sorted_kwargs) - result = cache_instance.get(key) - if result is None: + key = (args, tuple(sorted(kwargs.items()))) + if (result := cache.get(key)) is None: result = func(*args, **kwargs) - cache_instance.put(key, result) + cache.put(key, result) return result - def cache_info() -> LRUCache[Any, R]: # type: ignore[valid-type] - return cache_instance - - wrapper.cache_info = cache_info # Direct assignment + wrapper.cache_info = lambda: cache # Direct attribute assignment return wrapper - + return decorator_func if __name__ == "__main__": import doctest - doctest.testmod() From 453a9af5154d4e91b40decee0ac639c8320a4bbd Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 22 Jun 2025 08:43:31 +0000 Subject: [PATCH 084/107] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- other/lru_cache.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/other/lru_cache.py b/other/lru_cache.py index 541aff7b88ea..493a80e08112 100644 --- a/other/lru_cache.py +++ b/other/lru_cache.py @@ -51,17 +51,19 @@ def add(self, node: DoubleLinkedListNode[T, U]) -> None: prev = self.rear.prev if not prev: raise ValueError("Invalid list state") - + prev.next = node node.prev = prev self.rear.prev = node node.next = self.rear - def remove(self, node: DoubleLinkedListNode[T, U]) -> DoubleLinkedListNode[T, U] | None: + def remove( + self, node: DoubleLinkedListNode[T, U] + ) -> DoubleLinkedListNode[T, U] | None: """Remove node from list""" if not node.prev or not node.next: return None - + node.prev.next = node.next node.next.prev = node.prev node.prev = node.next = None @@ -95,6 +97,7 @@ def get(self, key: T) -> U | None: return node.val self.misses += 1 return None + def put(self, key: T, value: U) -> None: """Set value for key""" if key in self.cache: @@ -118,6 +121,7 @@ def put(self, key: T, value: U) -> None: @classmethod def decorator(cls, size: int = 128) -> Callable[[Callable[P, R]], Callable[P, R]]: """LRU Cache decorator""" + def decorator_func(func: Callable[P, R]) -> Callable[P, R]: cache = cls[Any, R](size) @@ -131,10 +135,11 @@ def wrapper(*args: P.args, **kwargs: P.kwargs) -> R: wrapper.cache_info = lambda: cache # Direct attribute assignment return wrapper - + return decorator_func if __name__ == "__main__": import doctest + doctest.testmod() From 219f6ea72e380ea18f8bc75cfc269ecf0a8475e8 Mon Sep 17 00:00:00 2001 From: lighting9999 Date: Sun, 22 Jun 2025 17:41:46 +0800 Subject: [PATCH 085/107] Update lru_cache.py --- other/lru_cache.py | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/other/lru_cache.py b/other/lru_cache.py index 493a80e08112..85228aee88db 100644 --- a/other/lru_cache.py +++ b/other/lru_cache.py @@ -2,11 +2,11 @@ from collections.abc import Callable, Hashable from functools import wraps -from typing import Any, Generic, ParamSpec, TypeVar, overload, TYPE_CHECKING +from typing import Any, Generic, ParamSpec, TypeVar, TYPE_CHECKING if TYPE_CHECKING: - type NodeKey = T | None - type NodeValue = U | None + type NodeKey = Any | None + type NodeValue = Any | None else: NodeKey = TypeVar("NodeKey", bound=Hashable) NodeValue = TypeVar("NodeValue") @@ -34,8 +34,8 @@ class DoubleLinkedList(Generic[T, U]): """Double Linked List for LRU Cache""" def __init__(self) -> None: - self.head: DoubleLinkedListNode[T, U] = DoubleLinkedListNode(None, None) - self.rear: DoubleLinkedListNode[T, U] = DoubleLinkedListNode(None, None) + self.head: DoubleLinkedListNode[Any, Any] = DoubleLinkedListNode(None, None) + self.rear: DoubleLinkedListNode[Any, Any] = DoubleLinkedListNode(None, None) self.head.next, self.rear.prev = self.rear, self.head def __repr__(self) -> str: @@ -51,19 +51,17 @@ def add(self, node: DoubleLinkedListNode[T, U]) -> None: prev = self.rear.prev if not prev: raise ValueError("Invalid list state") - + prev.next = node node.prev = prev self.rear.prev = node node.next = self.rear - def remove( - self, node: DoubleLinkedListNode[T, U] - ) -> DoubleLinkedListNode[T, U] | None: + def remove(self, node: DoubleLinkedListNode[T, U]) -> DoubleLinkedListNode[T, U] | None: """Remove node from list""" if not node.prev or not node.next: return None - + node.prev.next = node.next node.next.prev = node.prev node.prev = node.next = None @@ -82,7 +80,10 @@ def __init__(self, capacity: int) -> None: self.cache: dict[T, DoubleLinkedListNode[T, U]] = {} def __repr__(self) -> str: - return f"Cache(hits={self.hits}, misses={self.misses}, cap={self.capacity}, size={self.size})" + return ( + f"Cache(hits={self.hits}, misses={self.misses}, " + f"cap={self.capacity}, size={self.size})" + ) def __contains__(self, key: T) -> bool: return key in self.cache @@ -121,9 +122,8 @@ def put(self, key: T, value: U) -> None: @classmethod def decorator(cls, size: int = 128) -> Callable[[Callable[P, R]], Callable[P, R]]: """LRU Cache decorator""" - def decorator_func(func: Callable[P, R]) -> Callable[P, R]: - cache = cls[Any, R](size) + cache = cls[Any, R](size) # type: ignore[type-var] @wraps(func) def wrapper(*args: P.args, **kwargs: P.kwargs) -> R: @@ -133,13 +133,13 @@ def wrapper(*args: P.args, **kwargs: P.kwargs) -> R: cache.put(key, result) return result - wrapper.cache_info = lambda: cache # Direct attribute assignment + # Add cache_info attribute + wrapper.cache_info = lambda: cache # type: ignore[attr-defined] return wrapper - + return decorator_func if __name__ == "__main__": import doctest - doctest.testmod() From a5e33283a9364e1a02b1a8f336a6cb6c6b57d6d6 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 22 Jun 2025 09:42:09 +0000 Subject: [PATCH 086/107] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- other/lru_cache.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/other/lru_cache.py b/other/lru_cache.py index 85228aee88db..f3643261f1ad 100644 --- a/other/lru_cache.py +++ b/other/lru_cache.py @@ -51,17 +51,19 @@ def add(self, node: DoubleLinkedListNode[T, U]) -> None: prev = self.rear.prev if not prev: raise ValueError("Invalid list state") - + prev.next = node node.prev = prev self.rear.prev = node node.next = self.rear - def remove(self, node: DoubleLinkedListNode[T, U]) -> DoubleLinkedListNode[T, U] | None: + def remove( + self, node: DoubleLinkedListNode[T, U] + ) -> DoubleLinkedListNode[T, U] | None: """Remove node from list""" if not node.prev or not node.next: return None - + node.prev.next = node.next node.next.prev = node.prev node.prev = node.next = None @@ -122,6 +124,7 @@ def put(self, key: T, value: U) -> None: @classmethod def decorator(cls, size: int = 128) -> Callable[[Callable[P, R]], Callable[P, R]]: """LRU Cache decorator""" + def decorator_func(func: Callable[P, R]) -> Callable[P, R]: cache = cls[Any, R](size) # type: ignore[type-var] @@ -136,10 +139,11 @@ def wrapper(*args: P.args, **kwargs: P.kwargs) -> R: # Add cache_info attribute wrapper.cache_info = lambda: cache # type: ignore[attr-defined] return wrapper - + return decorator_func if __name__ == "__main__": import doctest + doctest.testmod() From 591c19dcbc005bba0d45043c62e9c9a270a745d0 Mon Sep 17 00:00:00 2001 From: lighting9999 Date: Sun, 22 Jun 2025 17:54:40 +0800 Subject: [PATCH 087/107] Update lru_cache.py --- other/lru_cache.py | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/other/lru_cache.py b/other/lru_cache.py index f3643261f1ad..e9c9af633ad1 100644 --- a/other/lru_cache.py +++ b/other/lru_cache.py @@ -17,26 +17,27 @@ R = TypeVar("R") -class DoubleLinkedListNode(Generic[T, U]): +class DoubleLinkedListNode: """Node built for LRU Cache""" def __init__(self, key: NodeKey, val: NodeValue) -> None: self.key = key self.val = val - self.next: DoubleLinkedListNode[T, U] | None = None - self.prev: DoubleLinkedListNode[T, U] | None = None + self.next: DoubleLinkedListNode | None = None + self.prev: DoubleLinkedListNode | None = None def __repr__(self) -> str: return f"Node(key={self.key}, val={self.val})" -class DoubleLinkedList(Generic[T, U]): +class DoubleLinkedList: """Double Linked List for LRU Cache""" def __init__(self) -> None: - self.head: DoubleLinkedListNode[Any, Any] = DoubleLinkedListNode(None, None) - self.rear: DoubleLinkedListNode[Any, Any] = DoubleLinkedListNode(None, None) - self.head.next, self.rear.prev = self.rear, self.head + self.head = DoubleLinkedListNode(None, None) + self.rear = DoubleLinkedListNode(None, None) + self.head.next = self.rear + self.rear.prev = self.head def __repr__(self) -> str: nodes = [] @@ -46,24 +47,22 @@ def __repr__(self) -> str: current = current.next return f"LinkedList({nodes})" - def add(self, node: DoubleLinkedListNode[T, U]) -> None: + def add(self, node: DoubleLinkedListNode) -> None: """Add node to list end""" prev = self.rear.prev if not prev: raise ValueError("Invalid list state") - + prev.next = node node.prev = prev self.rear.prev = node node.next = self.rear - def remove( - self, node: DoubleLinkedListNode[T, U] - ) -> DoubleLinkedListNode[T, U] | None: + def remove(self, node: DoubleLinkedListNode) -> DoubleLinkedListNode | None: """Remove node from list""" if not node.prev or not node.next: return None - + node.prev.next = node.next node.next.prev = node.prev node.prev = node.next = None @@ -74,12 +73,12 @@ class LRUCache(Generic[T, U]): """LRU Cache implementation""" def __init__(self, capacity: int) -> None: - self.list = DoubleLinkedList[T, U]() + self.list = DoubleLinkedList() self.capacity = capacity self.size = 0 self.hits = 0 self.misses = 0 - self.cache: dict[T, DoubleLinkedListNode[T, U]] = {} + self.cache: dict[T, DoubleLinkedListNode] = {} def __repr__(self) -> str: return ( @@ -116,7 +115,7 @@ def put(self, key: T, value: U) -> None: del self.cache[first.key] self.size -= 1 - new_node: DoubleLinkedListNode[T, U] = DoubleLinkedListNode(key, value) + new_node = DoubleLinkedListNode(key, value) self.cache[key] = new_node self.list.add(new_node) self.size += 1 @@ -124,7 +123,6 @@ def put(self, key: T, value: U) -> None: @classmethod def decorator(cls, size: int = 128) -> Callable[[Callable[P, R]], Callable[P, R]]: """LRU Cache decorator""" - def decorator_func(func: Callable[P, R]) -> Callable[P, R]: cache = cls[Any, R](size) # type: ignore[type-var] @@ -139,11 +137,10 @@ def wrapper(*args: P.args, **kwargs: P.kwargs) -> R: # Add cache_info attribute wrapper.cache_info = lambda: cache # type: ignore[attr-defined] return wrapper - + return decorator_func if __name__ == "__main__": import doctest - doctest.testmod() From b9072c8d21b53fe499b49436c6db0f1a14aa83db Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 22 Jun 2025 09:55:01 +0000 Subject: [PATCH 088/107] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- other/lru_cache.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/other/lru_cache.py b/other/lru_cache.py index e9c9af633ad1..716fa003fa11 100644 --- a/other/lru_cache.py +++ b/other/lru_cache.py @@ -52,7 +52,7 @@ def add(self, node: DoubleLinkedListNode) -> None: prev = self.rear.prev if not prev: raise ValueError("Invalid list state") - + prev.next = node node.prev = prev self.rear.prev = node @@ -62,7 +62,7 @@ def remove(self, node: DoubleLinkedListNode) -> DoubleLinkedListNode | None: """Remove node from list""" if not node.prev or not node.next: return None - + node.prev.next = node.next node.next.prev = node.prev node.prev = node.next = None @@ -123,6 +123,7 @@ def put(self, key: T, value: U) -> None: @classmethod def decorator(cls, size: int = 128) -> Callable[[Callable[P, R]], Callable[P, R]]: """LRU Cache decorator""" + def decorator_func(func: Callable[P, R]) -> Callable[P, R]: cache = cls[Any, R](size) # type: ignore[type-var] @@ -137,10 +138,11 @@ def wrapper(*args: P.args, **kwargs: P.kwargs) -> R: # Add cache_info attribute wrapper.cache_info = lambda: cache # type: ignore[attr-defined] return wrapper - + return decorator_func if __name__ == "__main__": import doctest + doctest.testmod() From 3a7c9980f197dcc1d19dad0f2cbe2f9505b16c77 Mon Sep 17 00:00:00 2001 From: lighting9999 Date: Sun, 22 Jun 2025 19:56:58 +0800 Subject: [PATCH 089/107] Update lru_cache.py --- other/lru_cache.py | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/other/lru_cache.py b/other/lru_cache.py index 716fa003fa11..fcf2b3557bf7 100644 --- a/other/lru_cache.py +++ b/other/lru_cache.py @@ -2,14 +2,7 @@ from collections.abc import Callable, Hashable from functools import wraps -from typing import Any, Generic, ParamSpec, TypeVar, TYPE_CHECKING - -if TYPE_CHECKING: - type NodeKey = Any | None - type NodeValue = Any | None -else: - NodeKey = TypeVar("NodeKey", bound=Hashable) - NodeValue = TypeVar("NodeValue") +from typing import Any, Generic, ParamSpec, TypeVar T = TypeVar("T", bound=Hashable) U = TypeVar("U") @@ -18,9 +11,9 @@ class DoubleLinkedListNode: - """Node built for LRU Cache""" + """Node for LRU Cache""" - def __init__(self, key: NodeKey, val: NodeValue) -> None: + def __init__(self, key: Any | None, val: Any | None) -> None: self.key = key self.val = val self.next: DoubleLinkedListNode | None = None @@ -52,7 +45,7 @@ def add(self, node: DoubleLinkedListNode) -> None: prev = self.rear.prev if not prev: raise ValueError("Invalid list state") - + prev.next = node node.prev = prev self.rear.prev = node @@ -62,7 +55,7 @@ def remove(self, node: DoubleLinkedListNode) -> DoubleLinkedListNode | None: """Remove node from list""" if not node.prev or not node.next: return None - + node.prev.next = node.next node.next.prev = node.prev node.prev = node.next = None @@ -96,7 +89,7 @@ def get(self, key: T) -> U | None: node = self.cache[key] if self.list.remove(node): self.list.add(node) - return node.val + return node.val # type: ignore[return-value] self.misses += 1 return None @@ -112,7 +105,7 @@ def put(self, key: T, value: U) -> None: if self.size >= self.capacity: first = self.list.head.next if first and first.key and self.list.remove(first): - del self.cache[first.key] + del self.cache[first.key] # type: ignore[index] self.size -= 1 new_node = DoubleLinkedListNode(key, value) @@ -123,10 +116,10 @@ def put(self, key: T, value: U) -> None: @classmethod def decorator(cls, size: int = 128) -> Callable[[Callable[P, R]], Callable[P, R]]: """LRU Cache decorator""" - def decorator_func(func: Callable[P, R]) -> Callable[P, R]: - cache = cls[Any, R](size) # type: ignore[type-var] - + # Create non-generic cache instance + cache = cls(size) # type: ignore[assignment] + @wraps(func) def wrapper(*args: P.args, **kwargs: P.kwargs) -> R: key = (args, tuple(sorted(kwargs.items()))) @@ -134,15 +127,14 @@ def wrapper(*args: P.args, **kwargs: P.kwargs) -> R: result = func(*args, **kwargs) cache.put(key, result) return result - + # Add cache_info attribute wrapper.cache_info = lambda: cache # type: ignore[attr-defined] return wrapper - + return decorator_func if __name__ == "__main__": import doctest - doctest.testmod() From 027f6922b4d270fc6a1377c793913953e592b9eb Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 22 Jun 2025 11:57:22 +0000 Subject: [PATCH 090/107] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- other/lru_cache.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/other/lru_cache.py b/other/lru_cache.py index fcf2b3557bf7..4a06cb2a5030 100644 --- a/other/lru_cache.py +++ b/other/lru_cache.py @@ -45,7 +45,7 @@ def add(self, node: DoubleLinkedListNode) -> None: prev = self.rear.prev if not prev: raise ValueError("Invalid list state") - + prev.next = node node.prev = prev self.rear.prev = node @@ -55,7 +55,7 @@ def remove(self, node: DoubleLinkedListNode) -> DoubleLinkedListNode | None: """Remove node from list""" if not node.prev or not node.next: return None - + node.prev.next = node.next node.next.prev = node.prev node.prev = node.next = None @@ -116,10 +116,11 @@ def put(self, key: T, value: U) -> None: @classmethod def decorator(cls, size: int = 128) -> Callable[[Callable[P, R]], Callable[P, R]]: """LRU Cache decorator""" + def decorator_func(func: Callable[P, R]) -> Callable[P, R]: # Create non-generic cache instance cache = cls(size) # type: ignore[assignment] - + @wraps(func) def wrapper(*args: P.args, **kwargs: P.kwargs) -> R: key = (args, tuple(sorted(kwargs.items()))) @@ -127,14 +128,15 @@ def wrapper(*args: P.args, **kwargs: P.kwargs) -> R: result = func(*args, **kwargs) cache.put(key, result) return result - + # Add cache_info attribute wrapper.cache_info = lambda: cache # type: ignore[attr-defined] return wrapper - + return decorator_func if __name__ == "__main__": import doctest + doctest.testmod() From ca98c95d5e8d22160feff7264e32b47ba59ae9ff Mon Sep 17 00:00:00 2001 From: lighting9999 Date: Sun, 22 Jun 2025 20:00:23 +0800 Subject: [PATCH 091/107] Update lru_cache.py --- other/lru_cache.py | 69 +++++++++++++++++++++------------------------- 1 file changed, 32 insertions(+), 37 deletions(-) diff --git a/other/lru_cache.py b/other/lru_cache.py index 4a06cb2a5030..fc00aa4f2a63 100644 --- a/other/lru_cache.py +++ b/other/lru_cache.py @@ -2,10 +2,8 @@ from collections.abc import Callable, Hashable from functools import wraps -from typing import Any, Generic, ParamSpec, TypeVar +from typing import Any, Generic, ParamSpec, TypeVar, cast -T = TypeVar("T", bound=Hashable) -U = TypeVar("U") P = ParamSpec("P") R = TypeVar("R") @@ -43,9 +41,9 @@ def __repr__(self) -> str: def add(self, node: DoubleLinkedListNode) -> None: """Add node to list end""" prev = self.rear.prev - if not prev: + if prev is None: raise ValueError("Invalid list state") - + prev.next = node node.prev = prev self.rear.prev = node @@ -53,16 +51,16 @@ def add(self, node: DoubleLinkedListNode) -> None: def remove(self, node: DoubleLinkedListNode) -> DoubleLinkedListNode | None: """Remove node from list""" - if not node.prev or not node.next: + if node.prev is None or node.next is None: return None - + node.prev.next = node.next node.next.prev = node.prev node.prev = node.next = None return node -class LRUCache(Generic[T, U]): +class LRUCache: """LRU Cache implementation""" def __init__(self, capacity: int) -> None: @@ -71,7 +69,7 @@ def __init__(self, capacity: int) -> None: self.size = 0 self.hits = 0 self.misses = 0 - self.cache: dict[T, DoubleLinkedListNode] = {} + self.cache: dict[Any, DoubleLinkedListNode] = {} def __repr__(self) -> str: return ( @@ -79,21 +77,18 @@ def __repr__(self) -> str: f"cap={self.capacity}, size={self.size})" ) - def __contains__(self, key: T) -> bool: - return key in self.cache - - def get(self, key: T) -> U | None: + def get(self, key: Any) -> Any | None: """Get value for key""" if key in self.cache: self.hits += 1 node = self.cache[key] if self.list.remove(node): self.list.add(node) - return node.val # type: ignore[return-value] + return node.val self.misses += 1 return None - def put(self, key: T, value: U) -> None: + def put(self, key: Any, value: Any) -> None: """Set value for key""" if key in self.cache: node = self.cache[key] @@ -105,7 +100,7 @@ def put(self, key: T, value: U) -> None: if self.size >= self.capacity: first = self.list.head.next if first and first.key and self.list.remove(first): - del self.cache[first.key] # type: ignore[index] + del self.cache[first.key] self.size -= 1 new_node = DoubleLinkedListNode(key, value) @@ -113,30 +108,30 @@ def put(self, key: T, value: U) -> None: self.list.add(new_node) self.size += 1 - @classmethod - def decorator(cls, size: int = 128) -> Callable[[Callable[P, R]], Callable[P, R]]: - """LRU Cache decorator""" - - def decorator_func(func: Callable[P, R]) -> Callable[P, R]: - # Create non-generic cache instance - cache = cls(size) # type: ignore[assignment] - @wraps(func) - def wrapper(*args: P.args, **kwargs: P.kwargs) -> R: - key = (args, tuple(sorted(kwargs.items()))) - if (result := cache.get(key)) is None: - result = func(*args, **kwargs) - cache.put(key, result) - return result - - # Add cache_info attribute - wrapper.cache_info = lambda: cache # type: ignore[attr-defined] - return wrapper - - return decorator_func +def lru_cache(size: int = 128) -> Callable[[Callable[P, R]], Callable[P, R]]: + """LRU Cache decorator""" + def decorator_func(func: Callable[P, R]) -> Callable[P, R]: + cache = LRUCache(size) + + @wraps(func) + def wrapper(*args: P.args, **kwargs: P.kwargs) -> R: + key = (args, tuple(sorted(kwargs.items()))) + cached = cache.get(key) + if cached is not None: + return cached + + result = func(*args, **kwargs) + cache.put(key, result) + return result + + # Add cache_info attribute + wrapper.cache_info = lambda: cache # type: ignore[attr-defined] + return wrapper + + return decorator_func if __name__ == "__main__": import doctest - doctest.testmod() From 36e3e5496d10119bf2fa8fe29a744b4be79e6183 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 22 Jun 2025 12:00:46 +0000 Subject: [PATCH 092/107] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- other/lru_cache.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/other/lru_cache.py b/other/lru_cache.py index fc00aa4f2a63..2438b7568b54 100644 --- a/other/lru_cache.py +++ b/other/lru_cache.py @@ -43,7 +43,7 @@ def add(self, node: DoubleLinkedListNode) -> None: prev = self.rear.prev if prev is None: raise ValueError("Invalid list state") - + prev.next = node node.prev = prev self.rear.prev = node @@ -53,7 +53,7 @@ def remove(self, node: DoubleLinkedListNode) -> DoubleLinkedListNode | None: """Remove node from list""" if node.prev is None or node.next is None: return None - + node.prev.next = node.next node.next.prev = node.prev node.prev = node.next = None @@ -111,27 +111,28 @@ def put(self, key: Any, value: Any) -> None: def lru_cache(size: int = 128) -> Callable[[Callable[P, R]], Callable[P, R]]: """LRU Cache decorator""" + def decorator_func(func: Callable[P, R]) -> Callable[P, R]: cache = LRUCache(size) - + @wraps(func) def wrapper(*args: P.args, **kwargs: P.kwargs) -> R: key = (args, tuple(sorted(kwargs.items()))) - cached = cache.get(key) - if cached is not None: + if (cached := cache.get(key)) is not None: return cached - + result = func(*args, **kwargs) cache.put(key, result) return result - + # Add cache_info attribute wrapper.cache_info = lambda: cache # type: ignore[attr-defined] return wrapper - + return decorator_func if __name__ == "__main__": import doctest + doctest.testmod() From 51e3876bf35e41094a5830f02166c1c37e02cabc Mon Sep 17 00:00:00 2001 From: lighting9999 Date: Sun, 22 Jun 2025 20:03:25 +0800 Subject: [PATCH 093/107] Update lru_cache.py --- other/lru_cache.py | 92 +++++++++++++++++++++++++++------------------- 1 file changed, 55 insertions(+), 37 deletions(-) diff --git a/other/lru_cache.py b/other/lru_cache.py index 2438b7568b54..8ecc44582b4b 100644 --- a/other/lru_cache.py +++ b/other/lru_cache.py @@ -2,7 +2,7 @@ from collections.abc import Callable, Hashable from functools import wraps -from typing import Any, Generic, ParamSpec, TypeVar, cast +from typing import Any, ParamSpec, TypeVar, cast P = ParamSpec("P") R = TypeVar("R") @@ -10,26 +10,30 @@ class DoubleLinkedListNode: """Node for LRU Cache""" - - def __init__(self, key: Any | None, val: Any | None) -> None: + + __slots__ = ("key", "val", "next", "prev") + + def __init__(self, key: Any, val: Any) -> None: self.key = key self.val = val self.next: DoubleLinkedListNode | None = None self.prev: DoubleLinkedListNode | None = None - + def __repr__(self) -> str: return f"Node(key={self.key}, val={self.val})" class DoubleLinkedList: """Double Linked List for LRU Cache""" - + def __init__(self) -> None: + # Create sentinel nodes self.head = DoubleLinkedListNode(None, None) self.rear = DoubleLinkedListNode(None, None) + # Link sentinel nodes together self.head.next = self.rear self.rear.prev = self.head - + def __repr__(self) -> str: nodes = [] current = self.head @@ -37,23 +41,23 @@ def __repr__(self) -> str: nodes.append(repr(current)) current = current.next return f"LinkedList({nodes})" - + def add(self, node: DoubleLinkedListNode) -> None: - """Add node to list end""" + """Add node before rear""" prev = self.rear.prev if prev is None: - raise ValueError("Invalid list state") - + return # Should never happen with sentinel nodes + prev.next = node node.prev = prev self.rear.prev = node node.next = self.rear - + def remove(self, node: DoubleLinkedListNode) -> DoubleLinkedListNode | None: """Remove node from list""" if node.prev is None or node.next is None: return None - + node.prev.next = node.next node.next.prev = node.prev node.prev = node.next = None @@ -62,7 +66,7 @@ def remove(self, node: DoubleLinkedListNode) -> DoubleLinkedListNode | None: class LRUCache: """LRU Cache implementation""" - + def __init__(self, capacity: int) -> None: self.list = DoubleLinkedList() self.capacity = capacity @@ -70,13 +74,13 @@ def __init__(self, capacity: int) -> None: self.hits = 0 self.misses = 0 self.cache: dict[Any, DoubleLinkedListNode] = {} - + def __repr__(self) -> str: return ( f"Cache(hits={self.hits}, misses={self.misses}, " f"cap={self.capacity}, size={self.size})" ) - + def get(self, key: Any) -> Any | None: """Get value for key""" if key in self.cache: @@ -87,7 +91,7 @@ def get(self, key: Any) -> Any | None: return node.val self.misses += 1 return None - + def put(self, key: Any, value: Any) -> None: """Set value for key""" if key in self.cache: @@ -96,43 +100,57 @@ def put(self, key: Any, value: Any) -> None: node.val = value self.list.add(node) return - + if self.size >= self.capacity: - first = self.list.head.next - if first and first.key and self.list.remove(first): - del self.cache[first.key] - self.size -= 1 - + # Remove least recently used item + first_node = self.list.head.next + if first_node and first_node != self.list.rear: + if self.list.remove(first_node): + del self.cache[first_node.key] + self.size -= 1 + new_node = DoubleLinkedListNode(key, value) self.cache[key] = new_node self.list.add(new_node) self.size += 1 - - -def lru_cache(size: int = 128) -> Callable[[Callable[P, R]], Callable[P, R]]: + + def cache_info(self) -> dict[str, Any]: + """Get cache statistics""" + return { + "hits": self.hits, + "misses": self.misses, + "capacity": self.capacity, + "size": self.size + } + + +def lru_cache(maxsize: int = 128) -> Callable[[Callable[P, R]], Callable[P, R]]: """LRU Cache decorator""" - - def decorator_func(func: Callable[P, R]) -> Callable[P, R]: - cache = LRUCache(size) - + def decorator(func: Callable[P, R]) -> Callable[P, R]: + cache = LRUCache(maxsize) + @wraps(func) def wrapper(*args: P.args, **kwargs: P.kwargs) -> R: + # Create normalized cache key key = (args, tuple(sorted(kwargs.items()))) - if (cached := cache.get(key)) is not None: + + # Try to get cached result + cached = cache.get(key) + if cached is not None: return cached - + + # Compute and cache result result = func(*args, **kwargs) cache.put(key, result) return result - - # Add cache_info attribute - wrapper.cache_info = lambda: cache # type: ignore[attr-defined] + + # Attach cache info method + wrapper.cache_info = cache.cache_info # type: ignore[attr-defined] return wrapper - - return decorator_func + + return decorator if __name__ == "__main__": import doctest - doctest.testmod() From 24f8370e1965d6cb7658a4122d44ee8e28425bfa Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 22 Jun 2025 12:03:48 +0000 Subject: [PATCH 094/107] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- other/lru_cache.py | 49 +++++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/other/lru_cache.py b/other/lru_cache.py index 8ecc44582b4b..d01ea195bbf1 100644 --- a/other/lru_cache.py +++ b/other/lru_cache.py @@ -10,22 +10,22 @@ class DoubleLinkedListNode: """Node for LRU Cache""" - + __slots__ = ("key", "val", "next", "prev") - + def __init__(self, key: Any, val: Any) -> None: self.key = key self.val = val self.next: DoubleLinkedListNode | None = None self.prev: DoubleLinkedListNode | None = None - + def __repr__(self) -> str: return f"Node(key={self.key}, val={self.val})" class DoubleLinkedList: """Double Linked List for LRU Cache""" - + def __init__(self) -> None: # Create sentinel nodes self.head = DoubleLinkedListNode(None, None) @@ -33,7 +33,7 @@ def __init__(self) -> None: # Link sentinel nodes together self.head.next = self.rear self.rear.prev = self.head - + def __repr__(self) -> str: nodes = [] current = self.head @@ -41,23 +41,23 @@ def __repr__(self) -> str: nodes.append(repr(current)) current = current.next return f"LinkedList({nodes})" - + def add(self, node: DoubleLinkedListNode) -> None: """Add node before rear""" prev = self.rear.prev if prev is None: return # Should never happen with sentinel nodes - + prev.next = node node.prev = prev self.rear.prev = node node.next = self.rear - + def remove(self, node: DoubleLinkedListNode) -> DoubleLinkedListNode | None: """Remove node from list""" if node.prev is None or node.next is None: return None - + node.prev.next = node.next node.next.prev = node.prev node.prev = node.next = None @@ -66,7 +66,7 @@ def remove(self, node: DoubleLinkedListNode) -> DoubleLinkedListNode | None: class LRUCache: """LRU Cache implementation""" - + def __init__(self, capacity: int) -> None: self.list = DoubleLinkedList() self.capacity = capacity @@ -74,13 +74,13 @@ def __init__(self, capacity: int) -> None: self.hits = 0 self.misses = 0 self.cache: dict[Any, DoubleLinkedListNode] = {} - + def __repr__(self) -> str: return ( f"Cache(hits={self.hits}, misses={self.misses}, " f"cap={self.capacity}, size={self.size})" ) - + def get(self, key: Any) -> Any | None: """Get value for key""" if key in self.cache: @@ -91,7 +91,7 @@ def get(self, key: Any) -> Any | None: return node.val self.misses += 1 return None - + def put(self, key: Any, value: Any) -> None: """Set value for key""" if key in self.cache: @@ -100,7 +100,7 @@ def put(self, key: Any, value: Any) -> None: node.val = value self.list.add(node) return - + if self.size >= self.capacity: # Remove least recently used item first_node = self.list.head.next @@ -108,49 +108,50 @@ def put(self, key: Any, value: Any) -> None: if self.list.remove(first_node): del self.cache[first_node.key] self.size -= 1 - + new_node = DoubleLinkedListNode(key, value) self.cache[key] = new_node self.list.add(new_node) self.size += 1 - + def cache_info(self) -> dict[str, Any]: """Get cache statistics""" return { "hits": self.hits, "misses": self.misses, "capacity": self.capacity, - "size": self.size + "size": self.size, } def lru_cache(maxsize: int = 128) -> Callable[[Callable[P, R]], Callable[P, R]]: """LRU Cache decorator""" + def decorator(func: Callable[P, R]) -> Callable[P, R]: cache = LRUCache(maxsize) - + @wraps(func) def wrapper(*args: P.args, **kwargs: P.kwargs) -> R: # Create normalized cache key key = (args, tuple(sorted(kwargs.items()))) - + # Try to get cached result - cached = cache.get(key) - if cached is not None: + if (cached := cache.get(key)) is not None: return cached - + # Compute and cache result result = func(*args, **kwargs) cache.put(key, result) return result - + # Attach cache info method wrapper.cache_info = cache.cache_info # type: ignore[attr-defined] return wrapper - + return decorator if __name__ == "__main__": import doctest + doctest.testmod() From c58ca7e0aa494530cb6002cb5db2724ec1975681 Mon Sep 17 00:00:00 2001 From: lighting9999 Date: Sun, 22 Jun 2025 20:07:06 +0800 Subject: [PATCH 095/107] Update lru_cache.py --- other/lru_cache.py | 66 +++++++++++++++++++++++++--------------------- 1 file changed, 36 insertions(+), 30 deletions(-) diff --git a/other/lru_cache.py b/other/lru_cache.py index d01ea195bbf1..121cf9fd0788 100644 --- a/other/lru_cache.py +++ b/other/lru_cache.py @@ -2,7 +2,7 @@ from collections.abc import Callable, Hashable from functools import wraps -from typing import Any, ParamSpec, TypeVar, cast +from typing import Any, ParamSpec, TypeVar P = ParamSpec("P") R = TypeVar("R") @@ -10,22 +10,22 @@ class DoubleLinkedListNode: """Node for LRU Cache""" - + __slots__ = ("key", "val", "next", "prev") - + def __init__(self, key: Any, val: Any) -> None: self.key = key self.val = val self.next: DoubleLinkedListNode | None = None self.prev: DoubleLinkedListNode | None = None - + def __repr__(self) -> str: return f"Node(key={self.key}, val={self.val})" class DoubleLinkedList: """Double Linked List for LRU Cache""" - + def __init__(self) -> None: # Create sentinel nodes self.head = DoubleLinkedListNode(None, None) @@ -33,7 +33,7 @@ def __init__(self) -> None: # Link sentinel nodes together self.head.next = self.rear self.rear.prev = self.head - + def __repr__(self) -> str: nodes = [] current = self.head @@ -41,32 +41,37 @@ def __repr__(self) -> str: nodes.append(repr(current)) current = current.next return f"LinkedList({nodes})" - + def add(self, node: DoubleLinkedListNode) -> None: """Add node before rear""" prev = self.rear.prev if prev is None: - return # Should never happen with sentinel nodes - + return + + # Insert node between prev and rear prev.next = node node.prev = prev self.rear.prev = node node.next = self.rear - + def remove(self, node: DoubleLinkedListNode) -> DoubleLinkedListNode | None: """Remove node from list""" if node.prev is None or node.next is None: return None - + + # Bypass node node.prev.next = node.next node.next.prev = node.prev - node.prev = node.next = None + + # Clear node references + node.prev = None + node.next = None return node class LRUCache: """LRU Cache implementation""" - + def __init__(self, capacity: int) -> None: self.list = DoubleLinkedList() self.capacity = capacity @@ -74,13 +79,13 @@ def __init__(self, capacity: int) -> None: self.hits = 0 self.misses = 0 self.cache: dict[Any, DoubleLinkedListNode] = {} - + def __repr__(self) -> str: return ( f"Cache(hits={self.hits}, misses={self.misses}, " f"cap={self.capacity}, size={self.size})" ) - + def get(self, key: Any) -> Any | None: """Get value for key""" if key in self.cache: @@ -91,67 +96,68 @@ def get(self, key: Any) -> Any | None: return node.val self.misses += 1 return None - + def put(self, key: Any, value: Any) -> None: """Set value for key""" if key in self.cache: + # Update existing node node = self.cache[key] if self.list.remove(node): node.val = value self.list.add(node) return - + + # Evict LRU item if at capacity if self.size >= self.capacity: - # Remove least recently used item first_node = self.list.head.next - if first_node and first_node != self.list.rear: + if first_node and first_node.key and first_node != self.list.rear: if self.list.remove(first_node): del self.cache[first_node.key] self.size -= 1 - + + # Add new node new_node = DoubleLinkedListNode(key, value) self.cache[key] = new_node self.list.add(new_node) self.size += 1 - + def cache_info(self) -> dict[str, Any]: """Get cache statistics""" return { "hits": self.hits, "misses": self.misses, "capacity": self.capacity, - "size": self.size, + "size": self.size } def lru_cache(maxsize: int = 128) -> Callable[[Callable[P, R]], Callable[P, R]]: """LRU Cache decorator""" - def decorator(func: Callable[P, R]) -> Callable[P, R]: cache = LRUCache(maxsize) - + @wraps(func) def wrapper(*args: P.args, **kwargs: P.kwargs) -> R: # Create normalized cache key key = (args, tuple(sorted(kwargs.items()))) - + # Try to get cached result - if (cached := cache.get(key)) is not None: + cached = cache.get(key) + if cached is not None: return cached - + # Compute and cache result result = func(*args, **kwargs) cache.put(key, result) return result - + # Attach cache info method wrapper.cache_info = cache.cache_info # type: ignore[attr-defined] return wrapper - + return decorator if __name__ == "__main__": import doctest - doctest.testmod() From 98991c8f17e3267b9bd78052e2026aa7c26e65d9 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 22 Jun 2025 12:07:29 +0000 Subject: [PATCH 096/107] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- other/lru_cache.py | 51 +++++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/other/lru_cache.py b/other/lru_cache.py index 121cf9fd0788..e5fba4516134 100644 --- a/other/lru_cache.py +++ b/other/lru_cache.py @@ -10,22 +10,22 @@ class DoubleLinkedListNode: """Node for LRU Cache""" - + __slots__ = ("key", "val", "next", "prev") - + def __init__(self, key: Any, val: Any) -> None: self.key = key self.val = val self.next: DoubleLinkedListNode | None = None self.prev: DoubleLinkedListNode | None = None - + def __repr__(self) -> str: return f"Node(key={self.key}, val={self.val})" class DoubleLinkedList: """Double Linked List for LRU Cache""" - + def __init__(self) -> None: # Create sentinel nodes self.head = DoubleLinkedListNode(None, None) @@ -33,7 +33,7 @@ def __init__(self) -> None: # Link sentinel nodes together self.head.next = self.rear self.rear.prev = self.head - + def __repr__(self) -> str: nodes = [] current = self.head @@ -41,28 +41,28 @@ def __repr__(self) -> str: nodes.append(repr(current)) current = current.next return f"LinkedList({nodes})" - + def add(self, node: DoubleLinkedListNode) -> None: """Add node before rear""" prev = self.rear.prev if prev is None: return - + # Insert node between prev and rear prev.next = node node.prev = prev self.rear.prev = node node.next = self.rear - + def remove(self, node: DoubleLinkedListNode) -> DoubleLinkedListNode | None: """Remove node from list""" if node.prev is None or node.next is None: return None - + # Bypass node node.prev.next = node.next node.next.prev = node.prev - + # Clear node references node.prev = None node.next = None @@ -71,7 +71,7 @@ def remove(self, node: DoubleLinkedListNode) -> DoubleLinkedListNode | None: class LRUCache: """LRU Cache implementation""" - + def __init__(self, capacity: int) -> None: self.list = DoubleLinkedList() self.capacity = capacity @@ -79,13 +79,13 @@ def __init__(self, capacity: int) -> None: self.hits = 0 self.misses = 0 self.cache: dict[Any, DoubleLinkedListNode] = {} - + def __repr__(self) -> str: return ( f"Cache(hits={self.hits}, misses={self.misses}, " f"cap={self.capacity}, size={self.size})" ) - + def get(self, key: Any) -> Any | None: """Get value for key""" if key in self.cache: @@ -96,7 +96,7 @@ def get(self, key: Any) -> Any | None: return node.val self.misses += 1 return None - + def put(self, key: Any, value: Any) -> None: """Set value for key""" if key in self.cache: @@ -106,7 +106,7 @@ def put(self, key: Any, value: Any) -> None: node.val = value self.list.add(node) return - + # Evict LRU item if at capacity if self.size >= self.capacity: first_node = self.list.head.next @@ -114,50 +114,51 @@ def put(self, key: Any, value: Any) -> None: if self.list.remove(first_node): del self.cache[first_node.key] self.size -= 1 - + # Add new node new_node = DoubleLinkedListNode(key, value) self.cache[key] = new_node self.list.add(new_node) self.size += 1 - + def cache_info(self) -> dict[str, Any]: """Get cache statistics""" return { "hits": self.hits, "misses": self.misses, "capacity": self.capacity, - "size": self.size + "size": self.size, } def lru_cache(maxsize: int = 128) -> Callable[[Callable[P, R]], Callable[P, R]]: """LRU Cache decorator""" + def decorator(func: Callable[P, R]) -> Callable[P, R]: cache = LRUCache(maxsize) - + @wraps(func) def wrapper(*args: P.args, **kwargs: P.kwargs) -> R: # Create normalized cache key key = (args, tuple(sorted(kwargs.items()))) - + # Try to get cached result - cached = cache.get(key) - if cached is not None: + if (cached := cache.get(key)) is not None: return cached - + # Compute and cache result result = func(*args, **kwargs) cache.put(key, result) return result - + # Attach cache info method wrapper.cache_info = cache.cache_info # type: ignore[attr-defined] return wrapper - + return decorator if __name__ == "__main__": import doctest + doctest.testmod() From 0a2396c657dfac076f5384b7d147717dae674f3b Mon Sep 17 00:00:00 2001 From: lighting9999 Date: Sun, 22 Jun 2025 20:11:30 +0800 Subject: [PATCH 097/107] Update lru_cache.py --- other/lru_cache.py | 67 ++++++++++++++++++++++++---------------------- 1 file changed, 35 insertions(+), 32 deletions(-) diff --git a/other/lru_cache.py b/other/lru_cache.py index e5fba4516134..2cdbd02007eb 100644 --- a/other/lru_cache.py +++ b/other/lru_cache.py @@ -1,6 +1,6 @@ from __future__ import annotations -from collections.abc import Callable, Hashable +from collections.abc import Callable from functools import wraps from typing import Any, ParamSpec, TypeVar @@ -10,22 +10,22 @@ class DoubleLinkedListNode: """Node for LRU Cache""" - - __slots__ = ("key", "val", "next", "prev") - + + __slots__ = ("key", "next", "prev", "val") + def __init__(self, key: Any, val: Any) -> None: self.key = key self.val = val self.next: DoubleLinkedListNode | None = None self.prev: DoubleLinkedListNode | None = None - + def __repr__(self) -> str: return f"Node(key={self.key}, val={self.val})" class DoubleLinkedList: """Double Linked List for LRU Cache""" - + def __init__(self) -> None: # Create sentinel nodes self.head = DoubleLinkedListNode(None, None) @@ -33,7 +33,7 @@ def __init__(self) -> None: # Link sentinel nodes together self.head.next = self.rear self.rear.prev = self.head - + def __repr__(self) -> str: nodes = [] current = self.head @@ -41,28 +41,28 @@ def __repr__(self) -> str: nodes.append(repr(current)) current = current.next return f"LinkedList({nodes})" - + def add(self, node: DoubleLinkedListNode) -> None: """Add node before rear""" prev = self.rear.prev if prev is None: return - + # Insert node between prev and rear prev.next = node node.prev = prev self.rear.prev = node node.next = self.rear - + def remove(self, node: DoubleLinkedListNode) -> DoubleLinkedListNode | None: """Remove node from list""" if node.prev is None or node.next is None: return None - + # Bypass node node.prev.next = node.next node.next.prev = node.prev - + # Clear node references node.prev = None node.next = None @@ -71,7 +71,7 @@ def remove(self, node: DoubleLinkedListNode) -> DoubleLinkedListNode | None: class LRUCache: """LRU Cache implementation""" - + def __init__(self, capacity: int) -> None: self.list = DoubleLinkedList() self.capacity = capacity @@ -79,13 +79,13 @@ def __init__(self, capacity: int) -> None: self.hits = 0 self.misses = 0 self.cache: dict[Any, DoubleLinkedListNode] = {} - + def __repr__(self) -> str: return ( f"Cache(hits={self.hits}, misses={self.misses}, " f"cap={self.capacity}, size={self.size})" ) - + def get(self, key: Any) -> Any | None: """Get value for key""" if key in self.cache: @@ -96,7 +96,7 @@ def get(self, key: Any) -> Any | None: return node.val self.misses += 1 return None - + def put(self, key: Any, value: Any) -> None: """Set value for key""" if key in self.cache: @@ -106,59 +106,62 @@ def put(self, key: Any, value: Any) -> None: node.val = value self.list.add(node) return - + # Evict LRU item if at capacity if self.size >= self.capacity: first_node = self.list.head.next - if first_node and first_node.key and first_node != self.list.rear: - if self.list.remove(first_node): - del self.cache[first_node.key] - self.size -= 1 - + if ( + first_node + and first_node.key is not None + and first_node != self.list.rear + and self.list.remove(first_node) + ): + del self.cache[first_node.key] + self.size -= 1 + # Add new node new_node = DoubleLinkedListNode(key, value) self.cache[key] = new_node self.list.add(new_node) self.size += 1 - + def cache_info(self) -> dict[str, Any]: """Get cache statistics""" return { "hits": self.hits, "misses": self.misses, "capacity": self.capacity, - "size": self.size, + "size": self.size } def lru_cache(maxsize: int = 128) -> Callable[[Callable[P, R]], Callable[P, R]]: """LRU Cache decorator""" - def decorator(func: Callable[P, R]) -> Callable[P, R]: cache = LRUCache(maxsize) - + @wraps(func) def wrapper(*args: P.args, **kwargs: P.kwargs) -> R: # Create normalized cache key key = (args, tuple(sorted(kwargs.items()))) - + # Try to get cached result - if (cached := cache.get(key)) is not None: + cached = cache.get(key) + if cached is not None: return cached - + # Compute and cache result result = func(*args, **kwargs) cache.put(key, result) return result - + # Attach cache info method wrapper.cache_info = cache.cache_info # type: ignore[attr-defined] return wrapper - + return decorator if __name__ == "__main__": import doctest - doctest.testmod() From 10d7e26a54739a396962dc8c28a44b81106c1f7a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 22 Jun 2025 12:11:53 +0000 Subject: [PATCH 098/107] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- other/lru_cache.py | 55 +++++++++++++++++++++++----------------------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/other/lru_cache.py b/other/lru_cache.py index 2cdbd02007eb..50b8e2a057ef 100644 --- a/other/lru_cache.py +++ b/other/lru_cache.py @@ -10,22 +10,22 @@ class DoubleLinkedListNode: """Node for LRU Cache""" - + __slots__ = ("key", "next", "prev", "val") - + def __init__(self, key: Any, val: Any) -> None: self.key = key self.val = val self.next: DoubleLinkedListNode | None = None self.prev: DoubleLinkedListNode | None = None - + def __repr__(self) -> str: return f"Node(key={self.key}, val={self.val})" class DoubleLinkedList: """Double Linked List for LRU Cache""" - + def __init__(self) -> None: # Create sentinel nodes self.head = DoubleLinkedListNode(None, None) @@ -33,7 +33,7 @@ def __init__(self) -> None: # Link sentinel nodes together self.head.next = self.rear self.rear.prev = self.head - + def __repr__(self) -> str: nodes = [] current = self.head @@ -41,28 +41,28 @@ def __repr__(self) -> str: nodes.append(repr(current)) current = current.next return f"LinkedList({nodes})" - + def add(self, node: DoubleLinkedListNode) -> None: """Add node before rear""" prev = self.rear.prev if prev is None: return - + # Insert node between prev and rear prev.next = node node.prev = prev self.rear.prev = node node.next = self.rear - + def remove(self, node: DoubleLinkedListNode) -> DoubleLinkedListNode | None: """Remove node from list""" if node.prev is None or node.next is None: return None - + # Bypass node node.prev.next = node.next node.next.prev = node.prev - + # Clear node references node.prev = None node.next = None @@ -71,7 +71,7 @@ def remove(self, node: DoubleLinkedListNode) -> DoubleLinkedListNode | None: class LRUCache: """LRU Cache implementation""" - + def __init__(self, capacity: int) -> None: self.list = DoubleLinkedList() self.capacity = capacity @@ -79,13 +79,13 @@ def __init__(self, capacity: int) -> None: self.hits = 0 self.misses = 0 self.cache: dict[Any, DoubleLinkedListNode] = {} - + def __repr__(self) -> str: return ( f"Cache(hits={self.hits}, misses={self.misses}, " f"cap={self.capacity}, size={self.size})" ) - + def get(self, key: Any) -> Any | None: """Get value for key""" if key in self.cache: @@ -96,7 +96,7 @@ def get(self, key: Any) -> Any | None: return node.val self.misses += 1 return None - + def put(self, key: Any, value: Any) -> None: """Set value for key""" if key in self.cache: @@ -106,62 +106,63 @@ def put(self, key: Any, value: Any) -> None: node.val = value self.list.add(node) return - + # Evict LRU item if at capacity if self.size >= self.capacity: first_node = self.list.head.next if ( - first_node - and first_node.key is not None + first_node + and first_node.key is not None and first_node != self.list.rear and self.list.remove(first_node) ): del self.cache[first_node.key] self.size -= 1 - + # Add new node new_node = DoubleLinkedListNode(key, value) self.cache[key] = new_node self.list.add(new_node) self.size += 1 - + def cache_info(self) -> dict[str, Any]: """Get cache statistics""" return { "hits": self.hits, "misses": self.misses, "capacity": self.capacity, - "size": self.size + "size": self.size, } def lru_cache(maxsize: int = 128) -> Callable[[Callable[P, R]], Callable[P, R]]: """LRU Cache decorator""" + def decorator(func: Callable[P, R]) -> Callable[P, R]: cache = LRUCache(maxsize) - + @wraps(func) def wrapper(*args: P.args, **kwargs: P.kwargs) -> R: # Create normalized cache key key = (args, tuple(sorted(kwargs.items()))) - + # Try to get cached result - cached = cache.get(key) - if cached is not None: + if (cached := cache.get(key)) is not None: return cached - + # Compute and cache result result = func(*args, **kwargs) cache.put(key, result) return result - + # Attach cache info method wrapper.cache_info = cache.cache_info # type: ignore[attr-defined] return wrapper - + return decorator if __name__ == "__main__": import doctest + doctest.testmod() From 6494e59801e788c81dc4a4c712dcc558f9a25ba8 Mon Sep 17 00:00:00 2001 From: lighting9999 Date: Sun, 22 Jun 2025 21:36:41 +0800 Subject: [PATCH 099/107] Update lru_cache.py --- other/lru_cache.py | 57 +++++++++++++++++++++++----------------------- 1 file changed, 28 insertions(+), 29 deletions(-) diff --git a/other/lru_cache.py b/other/lru_cache.py index 50b8e2a057ef..333cad6eafd7 100644 --- a/other/lru_cache.py +++ b/other/lru_cache.py @@ -2,7 +2,7 @@ from collections.abc import Callable from functools import wraps -from typing import Any, ParamSpec, TypeVar +from typing import Any, ParamSpec, TypeVar, cast P = ParamSpec("P") R = TypeVar("R") @@ -10,22 +10,22 @@ class DoubleLinkedListNode: """Node for LRU Cache""" - + __slots__ = ("key", "next", "prev", "val") - + def __init__(self, key: Any, val: Any) -> None: self.key = key self.val = val self.next: DoubleLinkedListNode | None = None self.prev: DoubleLinkedListNode | None = None - + def __repr__(self) -> str: return f"Node(key={self.key}, val={self.val})" class DoubleLinkedList: """Double Linked List for LRU Cache""" - + def __init__(self) -> None: # Create sentinel nodes self.head = DoubleLinkedListNode(None, None) @@ -33,7 +33,7 @@ def __init__(self) -> None: # Link sentinel nodes together self.head.next = self.rear self.rear.prev = self.head - + def __repr__(self) -> str: nodes = [] current = self.head @@ -41,28 +41,28 @@ def __repr__(self) -> str: nodes.append(repr(current)) current = current.next return f"LinkedList({nodes})" - + def add(self, node: DoubleLinkedListNode) -> None: """Add node before rear""" prev = self.rear.prev if prev is None: return - + # Insert node between prev and rear prev.next = node node.prev = prev self.rear.prev = node node.next = self.rear - + def remove(self, node: DoubleLinkedListNode) -> DoubleLinkedListNode | None: """Remove node from list""" if node.prev is None or node.next is None: return None - + # Bypass node node.prev.next = node.next node.next.prev = node.prev - + # Clear node references node.prev = None node.next = None @@ -71,7 +71,7 @@ def remove(self, node: DoubleLinkedListNode) -> DoubleLinkedListNode | None: class LRUCache: """LRU Cache implementation""" - + def __init__(self, capacity: int) -> None: self.list = DoubleLinkedList() self.capacity = capacity @@ -79,13 +79,13 @@ def __init__(self, capacity: int) -> None: self.hits = 0 self.misses = 0 self.cache: dict[Any, DoubleLinkedListNode] = {} - + def __repr__(self) -> str: return ( f"Cache(hits={self.hits}, misses={self.misses}, " f"cap={self.capacity}, size={self.size})" ) - + def get(self, key: Any) -> Any | None: """Get value for key""" if key in self.cache: @@ -96,7 +96,7 @@ def get(self, key: Any) -> Any | None: return node.val self.misses += 1 return None - + def put(self, key: Any, value: Any) -> None: """Set value for key""" if key in self.cache: @@ -106,63 +106,62 @@ def put(self, key: Any, value: Any) -> None: node.val = value self.list.add(node) return - + # Evict LRU item if at capacity if self.size >= self.capacity: first_node = self.list.head.next if ( - first_node + first_node is not None and first_node.key is not None and first_node != self.list.rear and self.list.remove(first_node) ): del self.cache[first_node.key] self.size -= 1 - + # Add new node new_node = DoubleLinkedListNode(key, value) self.cache[key] = new_node self.list.add(new_node) self.size += 1 - + def cache_info(self) -> dict[str, Any]: """Get cache statistics""" return { "hits": self.hits, "misses": self.misses, "capacity": self.capacity, - "size": self.size, + "size": self.size } def lru_cache(maxsize: int = 128) -> Callable[[Callable[P, R]], Callable[P, R]]: """LRU Cache decorator""" - def decorator(func: Callable[P, R]) -> Callable[P, R]: cache = LRUCache(maxsize) - + @wraps(func) def wrapper(*args: P.args, **kwargs: P.kwargs) -> R: # Create normalized cache key key = (args, tuple(sorted(kwargs.items()))) - + # Try to get cached result - if (cached := cache.get(key)) is not None: - return cached - + cached = cache.get(key) + if cached is not None: + return cast(R, cached) + # Compute and cache result result = func(*args, **kwargs) cache.put(key, result) return result - + # Attach cache info method wrapper.cache_info = cache.cache_info # type: ignore[attr-defined] return wrapper - + return decorator if __name__ == "__main__": import doctest - doctest.testmod() From 29f768b9f3c5941aed43770df9af2470d5581b61 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 22 Jun 2025 13:37:04 +0000 Subject: [PATCH 100/107] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- other/lru_cache.py | 51 +++++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/other/lru_cache.py b/other/lru_cache.py index 333cad6eafd7..873da199ca63 100644 --- a/other/lru_cache.py +++ b/other/lru_cache.py @@ -10,22 +10,22 @@ class DoubleLinkedListNode: """Node for LRU Cache""" - + __slots__ = ("key", "next", "prev", "val") - + def __init__(self, key: Any, val: Any) -> None: self.key = key self.val = val self.next: DoubleLinkedListNode | None = None self.prev: DoubleLinkedListNode | None = None - + def __repr__(self) -> str: return f"Node(key={self.key}, val={self.val})" class DoubleLinkedList: """Double Linked List for LRU Cache""" - + def __init__(self) -> None: # Create sentinel nodes self.head = DoubleLinkedListNode(None, None) @@ -33,7 +33,7 @@ def __init__(self) -> None: # Link sentinel nodes together self.head.next = self.rear self.rear.prev = self.head - + def __repr__(self) -> str: nodes = [] current = self.head @@ -41,28 +41,28 @@ def __repr__(self) -> str: nodes.append(repr(current)) current = current.next return f"LinkedList({nodes})" - + def add(self, node: DoubleLinkedListNode) -> None: """Add node before rear""" prev = self.rear.prev if prev is None: return - + # Insert node between prev and rear prev.next = node node.prev = prev self.rear.prev = node node.next = self.rear - + def remove(self, node: DoubleLinkedListNode) -> DoubleLinkedListNode | None: """Remove node from list""" if node.prev is None or node.next is None: return None - + # Bypass node node.prev.next = node.next node.next.prev = node.prev - + # Clear node references node.prev = None node.next = None @@ -71,7 +71,7 @@ def remove(self, node: DoubleLinkedListNode) -> DoubleLinkedListNode | None: class LRUCache: """LRU Cache implementation""" - + def __init__(self, capacity: int) -> None: self.list = DoubleLinkedList() self.capacity = capacity @@ -79,13 +79,13 @@ def __init__(self, capacity: int) -> None: self.hits = 0 self.misses = 0 self.cache: dict[Any, DoubleLinkedListNode] = {} - + def __repr__(self) -> str: return ( f"Cache(hits={self.hits}, misses={self.misses}, " f"cap={self.capacity}, size={self.size})" ) - + def get(self, key: Any) -> Any | None: """Get value for key""" if key in self.cache: @@ -96,7 +96,7 @@ def get(self, key: Any) -> Any | None: return node.val self.misses += 1 return None - + def put(self, key: Any, value: Any) -> None: """Set value for key""" if key in self.cache: @@ -106,7 +106,7 @@ def put(self, key: Any, value: Any) -> None: node.val = value self.list.add(node) return - + # Evict LRU item if at capacity if self.size >= self.capacity: first_node = self.list.head.next @@ -118,50 +118,51 @@ def put(self, key: Any, value: Any) -> None: ): del self.cache[first_node.key] self.size -= 1 - + # Add new node new_node = DoubleLinkedListNode(key, value) self.cache[key] = new_node self.list.add(new_node) self.size += 1 - + def cache_info(self) -> dict[str, Any]: """Get cache statistics""" return { "hits": self.hits, "misses": self.misses, "capacity": self.capacity, - "size": self.size + "size": self.size, } def lru_cache(maxsize: int = 128) -> Callable[[Callable[P, R]], Callable[P, R]]: """LRU Cache decorator""" + def decorator(func: Callable[P, R]) -> Callable[P, R]: cache = LRUCache(maxsize) - + @wraps(func) def wrapper(*args: P.args, **kwargs: P.kwargs) -> R: # Create normalized cache key key = (args, tuple(sorted(kwargs.items()))) - + # Try to get cached result - cached = cache.get(key) - if cached is not None: + if (cached := cache.get(key)) is not None: return cast(R, cached) - + # Compute and cache result result = func(*args, **kwargs) cache.put(key, result) return result - + # Attach cache info method wrapper.cache_info = cache.cache_info # type: ignore[attr-defined] return wrapper - + return decorator if __name__ == "__main__": import doctest + doctest.testmod() From 49bbd9428323995467f99d65161b0f952cac74de Mon Sep 17 00:00:00 2001 From: lighting9999 Date: Sun, 22 Jun 2025 21:43:55 +0800 Subject: [PATCH 101/107] Update lru_cache.py --- other/lru_cache.py | 317 +++++++++++++++++++++------------------------ 1 file changed, 151 insertions(+), 166 deletions(-) diff --git a/other/lru_cache.py b/other/lru_cache.py index 873da199ca63..2d2ac8039a55 100644 --- a/other/lru_cache.py +++ b/other/lru_cache.py @@ -1,168 +1,153 @@ -from __future__ import annotations - -from collections.abc import Callable -from functools import wraps -from typing import Any, ParamSpec, TypeVar, cast - -P = ParamSpec("P") -R = TypeVar("R") - - -class DoubleLinkedListNode: - """Node for LRU Cache""" - - __slots__ = ("key", "next", "prev", "val") - - def __init__(self, key: Any, val: Any) -> None: - self.key = key - self.val = val - self.next: DoubleLinkedListNode | None = None - self.prev: DoubleLinkedListNode | None = None - - def __repr__(self) -> str: - return f"Node(key={self.key}, val={self.val})" - - -class DoubleLinkedList: - """Double Linked List for LRU Cache""" - - def __init__(self) -> None: - # Create sentinel nodes - self.head = DoubleLinkedListNode(None, None) - self.rear = DoubleLinkedListNode(None, None) - # Link sentinel nodes together - self.head.next = self.rear - self.rear.prev = self.head - - def __repr__(self) -> str: - nodes = [] - current = self.head - while current: - nodes.append(repr(current)) - current = current.next - return f"LinkedList({nodes})" - - def add(self, node: DoubleLinkedListNode) -> None: - """Add node before rear""" - prev = self.rear.prev - if prev is None: - return - - # Insert node between prev and rear - prev.next = node - node.prev = prev - self.rear.prev = node - node.next = self.rear - - def remove(self, node: DoubleLinkedListNode) -> DoubleLinkedListNode | None: - """Remove node from list""" - if node.prev is None or node.next is None: - return None - - # Bypass node - node.prev.next = node.next - node.next.prev = node.prev - - # Clear node references - node.prev = None - node.next = None - return node - - -class LRUCache: - """LRU Cache implementation""" - - def __init__(self, capacity: int) -> None: - self.list = DoubleLinkedList() - self.capacity = capacity - self.size = 0 - self.hits = 0 - self.misses = 0 - self.cache: dict[Any, DoubleLinkedListNode] = {} - - def __repr__(self) -> str: - return ( - f"Cache(hits={self.hits}, misses={self.misses}, " - f"cap={self.capacity}, size={self.size})" - ) - - def get(self, key: Any) -> Any | None: - """Get value for key""" - if key in self.cache: - self.hits += 1 - node = self.cache[key] - if self.list.remove(node): - self.list.add(node) - return node.val - self.misses += 1 +from future import annotations + +from collections.abc import Callable from functools import wraps from typing import Any, ParamSpec, TypeVar, cast + +P = ParamSpec("P") R = TypeVar("R") + +class DoubleLinkedListNode: """Node for LRU Cache""" + +__slots__ = ("key", "next", "prev", "val") + +def __init__(self, key: Any, val: Any) -> None: + self.key = key + self.val = val + self.next: DoubleLinkedListNode | None = None + self.prev: DoubleLinkedListNode | None = None + +def __repr__(self) -> str: + return f"Node(key={self.key}, val={self.val})" + +class DoubleLinkedList: """Double Linked List for LRU Cache""" + +def __init__(self) -> None: + # Create sentinel nodes + self.head = DoubleLinkedListNode(None, None) + self.rear = DoubleLinkedListNode(None, None) + # Link sentinel nodes together + self.head.next = self.rear + self.rear.prev = self.head + +def __repr__(self) -> str: + nodes = [] + current = self.head + while current: + nodes.append(repr(current)) + current = current.next + return f"LinkedList({nodes})" + +def add(self, node: DoubleLinkedListNode) -> None: + """Add node before rear""" + prev = self.rear.prev + if prev is None: + return + + # Insert node between prev and rear + prev.next = node + node.prev = prev + self.rear.prev = node + node.next = self.rear + +def remove(self, node: DoubleLinkedListNode) -> DoubleLinkedListNode | None: + """Remove node from list""" + if node.prev is None or node.next is None: return None + + # Bypass node + node.prev.next = node.next + node.next.prev = node.prev + + # Clear node references + node.prev = None + node.next = None + return node + +class LRUCache: """LRU Cache implementation""" + +def __init__(self, capacity: int) -> None: + self.list = DoubleLinkedList() + self.capacity = capacity + self.size = 0 + self.hits = 0 + self.misses = 0 + self.cache: dict[Any, DoubleLinkedListNode] = {} + +def __repr__(self) -> str: + return ( + f"Cache(hits={self.hits}, misses={self.misses}, " + f"cap={self.capacity}, size={self.size})" + ) + +def get(self, key: Any) -> Any | None: + """Get value for key""" + if key in self.cache: + self.hits += 1 + node = self.cache[key] + if self.list.remove(node): + self.list.add(node) + return node.val + self.misses += 1 + return None + +def put(self, key: Any, value: Any) -> None: + """Set value for key""" + if key in self.cache: + # Update existing node + node = self.cache[key] + if self.list.remove(node): + node.val = value + self.list.add(node) + return + + # Evict LRU item if at capacity + if self.size >= self.capacity: + # head.next may be None, so annotate as Optional + first_node: DoubleLinkedListNode | None = self.list.head.next + if ( + first_node is not None + and first_node.key is not None + and first_node is not self.list.rear + and self.list.remove(first_node) + ): + del self.cache[first_node.key] + self.size -= 1 + + # Add new node + new_node = DoubleLinkedListNode(key, value) + self.cache[key] = new_node + self.list.add(new_node) + self.size += 1 + +def cache_info(self) -> dict[str, Any]: + """Get cache statistics""" + return { + "hits": self.hits, + "misses": self.misses, + "capacity": self.capacity, + "size": self.size + } + +def lru_cache(maxsize: int = 128) -> Callable[[Callable[P, R]], Callable[P, R]]: """LRU Cache decorator""" def decorator(func: Callable[P, R]) -> Callable[P, R]: cache = LRUCache(maxsize) + +@wraps(func) + def wrapper(*args: P.args, **kwargs: P.kwargs) -> R: + # Create normalized cache key + key = (args, tuple(sorted(kwargs.items()))) + + # Try to get cached result + cached = cache.get(key) + if cached is not None: + return cast(R, cached) + + # Compute and cache result + result = func(*args, **kwargs) + cache.put(key, result) + return result + + # Attach cache info method + wrapper.cache_info = cache.cache_info # type: ignore[attr-defined] + return wrapper + +return decorator + +if name == "main": import doctest doctest.testmod() - def put(self, key: Any, value: Any) -> None: - """Set value for key""" - if key in self.cache: - # Update existing node - node = self.cache[key] - if self.list.remove(node): - node.val = value - self.list.add(node) - return - - # Evict LRU item if at capacity - if self.size >= self.capacity: - first_node = self.list.head.next - if ( - first_node is not None - and first_node.key is not None - and first_node != self.list.rear - and self.list.remove(first_node) - ): - del self.cache[first_node.key] - self.size -= 1 - - # Add new node - new_node = DoubleLinkedListNode(key, value) - self.cache[key] = new_node - self.list.add(new_node) - self.size += 1 - - def cache_info(self) -> dict[str, Any]: - """Get cache statistics""" - return { - "hits": self.hits, - "misses": self.misses, - "capacity": self.capacity, - "size": self.size, - } - - -def lru_cache(maxsize: int = 128) -> Callable[[Callable[P, R]], Callable[P, R]]: - """LRU Cache decorator""" - - def decorator(func: Callable[P, R]) -> Callable[P, R]: - cache = LRUCache(maxsize) - - @wraps(func) - def wrapper(*args: P.args, **kwargs: P.kwargs) -> R: - # Create normalized cache key - key = (args, tuple(sorted(kwargs.items()))) - - # Try to get cached result - if (cached := cache.get(key)) is not None: - return cast(R, cached) - - # Compute and cache result - result = func(*args, **kwargs) - cache.put(key, result) - return result - - # Attach cache info method - wrapper.cache_info = cache.cache_info # type: ignore[attr-defined] - return wrapper - - return decorator - - -if __name__ == "__main__": - import doctest - - doctest.testmod() From ca506cf0395bbf73695e5979483e2c1fd8a62a9f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 22 Jun 2025 13:44:18 +0000 Subject: [PATCH 102/107] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- other/lru_cache.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/other/lru_cache.py b/other/lru_cache.py index 2d2ac8039a55..02120f3c2965 100644 --- a/other/lru_cache.py +++ b/other/lru_cache.py @@ -40,7 +40,7 @@ def add(self, node: DoubleLinkedListNode) -> None: prev = self.rear.prev if prev is None: return - + # Insert node between prev and rear prev.next = node node.prev = prev @@ -51,11 +51,11 @@ def remove(self, node: DoubleLinkedListNode) -> DoubleLinkedListNode | None: """Remove node from list""" if node.prev is None or node.next is None: return None - + # Bypass node node.prev.next = node.next node.next.prev = node.prev - + # Clear node references node.prev = None node.next = None @@ -97,7 +97,7 @@ def put(self, key: Any, value: Any) -> None: node.val = value self.list.add(node) return - + # Evict LRU item if at capacity if self.size >= self.capacity: # head.next may be None, so annotate as Optional @@ -110,7 +110,7 @@ def put(self, key: Any, value: Any) -> None: ): del self.cache[first_node.key] self.size -= 1 - + # Add new node new_node = DoubleLinkedListNode(key, value) self.cache[key] = new_node @@ -132,17 +132,17 @@ def lru_cache(maxsize: int = 128) -> Callable[[Callable[P, R]], Callable[P, R]]: def wrapper(*args: P.args, **kwargs: P.kwargs) -> R: # Create normalized cache key key = (args, tuple(sorted(kwargs.items()))) - + # Try to get cached result cached = cache.get(key) if cached is not None: return cast(R, cached) - + # Compute and cache result result = func(*args, **kwargs) cache.put(key, result) return result - + # Attach cache info method wrapper.cache_info = cache.cache_info # type: ignore[attr-defined] return wrapper @@ -150,4 +150,3 @@ def wrapper(*args: P.args, **kwargs: P.kwargs) -> R: return decorator if name == "main": import doctest doctest.testmod() - From 25d5c9b78a68b96304edd6b9f5cc26219fd5d095 Mon Sep 17 00:00:00 2001 From: lighting9999 Date: Sun, 22 Jun 2025 21:45:35 +0800 Subject: [PATCH 103/107] Update lru_cache.py --- other/lru_cache.py | 317 ++++++++++++++++++++++++--------------------- 1 file changed, 166 insertions(+), 151 deletions(-) diff --git a/other/lru_cache.py b/other/lru_cache.py index 02120f3c2965..333cad6eafd7 100644 --- a/other/lru_cache.py +++ b/other/lru_cache.py @@ -1,152 +1,167 @@ -from future import annotations - -from collections.abc import Callable from functools import wraps from typing import Any, ParamSpec, TypeVar, cast - -P = ParamSpec("P") R = TypeVar("R") - -class DoubleLinkedListNode: """Node for LRU Cache""" - -__slots__ = ("key", "next", "prev", "val") - -def __init__(self, key: Any, val: Any) -> None: - self.key = key - self.val = val - self.next: DoubleLinkedListNode | None = None - self.prev: DoubleLinkedListNode | None = None - -def __repr__(self) -> str: - return f"Node(key={self.key}, val={self.val})" - -class DoubleLinkedList: """Double Linked List for LRU Cache""" - -def __init__(self) -> None: - # Create sentinel nodes - self.head = DoubleLinkedListNode(None, None) - self.rear = DoubleLinkedListNode(None, None) - # Link sentinel nodes together - self.head.next = self.rear - self.rear.prev = self.head - -def __repr__(self) -> str: - nodes = [] - current = self.head - while current: - nodes.append(repr(current)) - current = current.next - return f"LinkedList({nodes})" - -def add(self, node: DoubleLinkedListNode) -> None: - """Add node before rear""" - prev = self.rear.prev - if prev is None: - return - - # Insert node between prev and rear - prev.next = node - node.prev = prev - self.rear.prev = node - node.next = self.rear - -def remove(self, node: DoubleLinkedListNode) -> DoubleLinkedListNode | None: - """Remove node from list""" - if node.prev is None or node.next is None: +from __future__ import annotations + +from collections.abc import Callable +from functools import wraps +from typing import Any, ParamSpec, TypeVar, cast + +P = ParamSpec("P") +R = TypeVar("R") + + +class DoubleLinkedListNode: + """Node for LRU Cache""" + + __slots__ = ("key", "next", "prev", "val") + + def __init__(self, key: Any, val: Any) -> None: + self.key = key + self.val = val + self.next: DoubleLinkedListNode | None = None + self.prev: DoubleLinkedListNode | None = None + + def __repr__(self) -> str: + return f"Node(key={self.key}, val={self.val})" + + +class DoubleLinkedList: + """Double Linked List for LRU Cache""" + + def __init__(self) -> None: + # Create sentinel nodes + self.head = DoubleLinkedListNode(None, None) + self.rear = DoubleLinkedListNode(None, None) + # Link sentinel nodes together + self.head.next = self.rear + self.rear.prev = self.head + + def __repr__(self) -> str: + nodes = [] + current = self.head + while current: + nodes.append(repr(current)) + current = current.next + return f"LinkedList({nodes})" + + def add(self, node: DoubleLinkedListNode) -> None: + """Add node before rear""" + prev = self.rear.prev + if prev is None: + return + + # Insert node between prev and rear + prev.next = node + node.prev = prev + self.rear.prev = node + node.next = self.rear + + def remove(self, node: DoubleLinkedListNode) -> DoubleLinkedListNode | None: + """Remove node from list""" + if node.prev is None or node.next is None: + return None + + # Bypass node + node.prev.next = node.next + node.next.prev = node.prev + + # Clear node references + node.prev = None + node.next = None + return node + + +class LRUCache: + """LRU Cache implementation""" + + def __init__(self, capacity: int) -> None: + self.list = DoubleLinkedList() + self.capacity = capacity + self.size = 0 + self.hits = 0 + self.misses = 0 + self.cache: dict[Any, DoubleLinkedListNode] = {} + + def __repr__(self) -> str: + return ( + f"Cache(hits={self.hits}, misses={self.misses}, " + f"cap={self.capacity}, size={self.size})" + ) + + def get(self, key: Any) -> Any | None: + """Get value for key""" + if key in self.cache: + self.hits += 1 + node = self.cache[key] + if self.list.remove(node): + self.list.add(node) + return node.val + self.misses += 1 return None - - # Bypass node - node.prev.next = node.next - node.next.prev = node.prev - - # Clear node references - node.prev = None - node.next = None - return node - -class LRUCache: """LRU Cache implementation""" - -def __init__(self, capacity: int) -> None: - self.list = DoubleLinkedList() - self.capacity = capacity - self.size = 0 - self.hits = 0 - self.misses = 0 - self.cache: dict[Any, DoubleLinkedListNode] = {} - -def __repr__(self) -> str: - return ( - f"Cache(hits={self.hits}, misses={self.misses}, " - f"cap={self.capacity}, size={self.size})" - ) - -def get(self, key: Any) -> Any | None: - """Get value for key""" - if key in self.cache: - self.hits += 1 - node = self.cache[key] - if self.list.remove(node): - self.list.add(node) - return node.val - self.misses += 1 - return None - -def put(self, key: Any, value: Any) -> None: - """Set value for key""" - if key in self.cache: - # Update existing node - node = self.cache[key] - if self.list.remove(node): - node.val = value - self.list.add(node) - return - - # Evict LRU item if at capacity - if self.size >= self.capacity: - # head.next may be None, so annotate as Optional - first_node: DoubleLinkedListNode | None = self.list.head.next - if ( - first_node is not None - and first_node.key is not None - and first_node is not self.list.rear - and self.list.remove(first_node) - ): - del self.cache[first_node.key] - self.size -= 1 - - # Add new node - new_node = DoubleLinkedListNode(key, value) - self.cache[key] = new_node - self.list.add(new_node) - self.size += 1 - -def cache_info(self) -> dict[str, Any]: - """Get cache statistics""" - return { - "hits": self.hits, - "misses": self.misses, - "capacity": self.capacity, - "size": self.size - } - -def lru_cache(maxsize: int = 128) -> Callable[[Callable[P, R]], Callable[P, R]]: """LRU Cache decorator""" def decorator(func: Callable[P, R]) -> Callable[P, R]: cache = LRUCache(maxsize) - -@wraps(func) - def wrapper(*args: P.args, **kwargs: P.kwargs) -> R: - # Create normalized cache key - key = (args, tuple(sorted(kwargs.items()))) - - # Try to get cached result - cached = cache.get(key) - if cached is not None: - return cast(R, cached) - - # Compute and cache result - result = func(*args, **kwargs) - cache.put(key, result) - return result - - # Attach cache info method - wrapper.cache_info = cache.cache_info # type: ignore[attr-defined] - return wrapper - -return decorator - -if name == "main": import doctest doctest.testmod() + + def put(self, key: Any, value: Any) -> None: + """Set value for key""" + if key in self.cache: + # Update existing node + node = self.cache[key] + if self.list.remove(node): + node.val = value + self.list.add(node) + return + + # Evict LRU item if at capacity + if self.size >= self.capacity: + first_node = self.list.head.next + if ( + first_node is not None + and first_node.key is not None + and first_node != self.list.rear + and self.list.remove(first_node) + ): + del self.cache[first_node.key] + self.size -= 1 + + # Add new node + new_node = DoubleLinkedListNode(key, value) + self.cache[key] = new_node + self.list.add(new_node) + self.size += 1 + + def cache_info(self) -> dict[str, Any]: + """Get cache statistics""" + return { + "hits": self.hits, + "misses": self.misses, + "capacity": self.capacity, + "size": self.size + } + + +def lru_cache(maxsize: int = 128) -> Callable[[Callable[P, R]], Callable[P, R]]: + """LRU Cache decorator""" + def decorator(func: Callable[P, R]) -> Callable[P, R]: + cache = LRUCache(maxsize) + + @wraps(func) + def wrapper(*args: P.args, **kwargs: P.kwargs) -> R: + # Create normalized cache key + key = (args, tuple(sorted(kwargs.items()))) + + # Try to get cached result + cached = cache.get(key) + if cached is not None: + return cast(R, cached) + + # Compute and cache result + result = func(*args, **kwargs) + cache.put(key, result) + return result + + # Attach cache info method + wrapper.cache_info = cache.cache_info # type: ignore[attr-defined] + return wrapper + + return decorator + + +if __name__ == "__main__": + import doctest + doctest.testmod() From ec1b969e0641368f8d9990279e563ae40fe84c75 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 22 Jun 2025 13:46:27 +0000 Subject: [PATCH 104/107] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- other/lru_cache.py | 51 +++++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/other/lru_cache.py b/other/lru_cache.py index 333cad6eafd7..873da199ca63 100644 --- a/other/lru_cache.py +++ b/other/lru_cache.py @@ -10,22 +10,22 @@ class DoubleLinkedListNode: """Node for LRU Cache""" - + __slots__ = ("key", "next", "prev", "val") - + def __init__(self, key: Any, val: Any) -> None: self.key = key self.val = val self.next: DoubleLinkedListNode | None = None self.prev: DoubleLinkedListNode | None = None - + def __repr__(self) -> str: return f"Node(key={self.key}, val={self.val})" class DoubleLinkedList: """Double Linked List for LRU Cache""" - + def __init__(self) -> None: # Create sentinel nodes self.head = DoubleLinkedListNode(None, None) @@ -33,7 +33,7 @@ def __init__(self) -> None: # Link sentinel nodes together self.head.next = self.rear self.rear.prev = self.head - + def __repr__(self) -> str: nodes = [] current = self.head @@ -41,28 +41,28 @@ def __repr__(self) -> str: nodes.append(repr(current)) current = current.next return f"LinkedList({nodes})" - + def add(self, node: DoubleLinkedListNode) -> None: """Add node before rear""" prev = self.rear.prev if prev is None: return - + # Insert node between prev and rear prev.next = node node.prev = prev self.rear.prev = node node.next = self.rear - + def remove(self, node: DoubleLinkedListNode) -> DoubleLinkedListNode | None: """Remove node from list""" if node.prev is None or node.next is None: return None - + # Bypass node node.prev.next = node.next node.next.prev = node.prev - + # Clear node references node.prev = None node.next = None @@ -71,7 +71,7 @@ def remove(self, node: DoubleLinkedListNode) -> DoubleLinkedListNode | None: class LRUCache: """LRU Cache implementation""" - + def __init__(self, capacity: int) -> None: self.list = DoubleLinkedList() self.capacity = capacity @@ -79,13 +79,13 @@ def __init__(self, capacity: int) -> None: self.hits = 0 self.misses = 0 self.cache: dict[Any, DoubleLinkedListNode] = {} - + def __repr__(self) -> str: return ( f"Cache(hits={self.hits}, misses={self.misses}, " f"cap={self.capacity}, size={self.size})" ) - + def get(self, key: Any) -> Any | None: """Get value for key""" if key in self.cache: @@ -96,7 +96,7 @@ def get(self, key: Any) -> Any | None: return node.val self.misses += 1 return None - + def put(self, key: Any, value: Any) -> None: """Set value for key""" if key in self.cache: @@ -106,7 +106,7 @@ def put(self, key: Any, value: Any) -> None: node.val = value self.list.add(node) return - + # Evict LRU item if at capacity if self.size >= self.capacity: first_node = self.list.head.next @@ -118,50 +118,51 @@ def put(self, key: Any, value: Any) -> None: ): del self.cache[first_node.key] self.size -= 1 - + # Add new node new_node = DoubleLinkedListNode(key, value) self.cache[key] = new_node self.list.add(new_node) self.size += 1 - + def cache_info(self) -> dict[str, Any]: """Get cache statistics""" return { "hits": self.hits, "misses": self.misses, "capacity": self.capacity, - "size": self.size + "size": self.size, } def lru_cache(maxsize: int = 128) -> Callable[[Callable[P, R]], Callable[P, R]]: """LRU Cache decorator""" + def decorator(func: Callable[P, R]) -> Callable[P, R]: cache = LRUCache(maxsize) - + @wraps(func) def wrapper(*args: P.args, **kwargs: P.kwargs) -> R: # Create normalized cache key key = (args, tuple(sorted(kwargs.items()))) - + # Try to get cached result - cached = cache.get(key) - if cached is not None: + if (cached := cache.get(key)) is not None: return cast(R, cached) - + # Compute and cache result result = func(*args, **kwargs) cache.put(key, result) return result - + # Attach cache info method wrapper.cache_info = cache.cache_info # type: ignore[attr-defined] return wrapper - + return decorator if __name__ == "__main__": import doctest + doctest.testmod() From a3f6781bce5726d3ec9493633f056b1694a32710 Mon Sep 17 00:00:00 2001 From: lighting9999 Date: Sun, 22 Jun 2025 21:54:13 +0800 Subject: [PATCH 105/107] Update lru_cache.py --- other/lru_cache.py | 315 +++++++++++++++++++++------------------------ 1 file changed, 149 insertions(+), 166 deletions(-) diff --git a/other/lru_cache.py b/other/lru_cache.py index 873da199ca63..26a7400e03ea 100644 --- a/other/lru_cache.py +++ b/other/lru_cache.py @@ -1,168 +1,151 @@ -from __future__ import annotations - -from collections.abc import Callable -from functools import wraps -from typing import Any, ParamSpec, TypeVar, cast - -P = ParamSpec("P") -R = TypeVar("R") - - -class DoubleLinkedListNode: - """Node for LRU Cache""" - - __slots__ = ("key", "next", "prev", "val") - - def __init__(self, key: Any, val: Any) -> None: - self.key = key - self.val = val - self.next: DoubleLinkedListNode | None = None - self.prev: DoubleLinkedListNode | None = None - - def __repr__(self) -> str: - return f"Node(key={self.key}, val={self.val})" - - -class DoubleLinkedList: - """Double Linked List for LRU Cache""" - - def __init__(self) -> None: - # Create sentinel nodes - self.head = DoubleLinkedListNode(None, None) - self.rear = DoubleLinkedListNode(None, None) - # Link sentinel nodes together - self.head.next = self.rear - self.rear.prev = self.head - - def __repr__(self) -> str: - nodes = [] - current = self.head - while current: - nodes.append(repr(current)) - current = current.next - return f"LinkedList({nodes})" - - def add(self, node: DoubleLinkedListNode) -> None: - """Add node before rear""" - prev = self.rear.prev - if prev is None: - return - - # Insert node between prev and rear - prev.next = node - node.prev = prev - self.rear.prev = node - node.next = self.rear - - def remove(self, node: DoubleLinkedListNode) -> DoubleLinkedListNode | None: - """Remove node from list""" - if node.prev is None or node.next is None: - return None - - # Bypass node - node.prev.next = node.next - node.next.prev = node.prev - - # Clear node references - node.prev = None - node.next = None - return node - - -class LRUCache: - """LRU Cache implementation""" - - def __init__(self, capacity: int) -> None: - self.list = DoubleLinkedList() - self.capacity = capacity - self.size = 0 - self.hits = 0 - self.misses = 0 - self.cache: dict[Any, DoubleLinkedListNode] = {} - - def __repr__(self) -> str: - return ( - f"Cache(hits={self.hits}, misses={self.misses}, " - f"cap={self.capacity}, size={self.size})" - ) - - def get(self, key: Any) -> Any | None: - """Get value for key""" - if key in self.cache: - self.hits += 1 - node = self.cache[key] - if self.list.remove(node): - self.list.add(node) - return node.val - self.misses += 1 +from future import annotations + +from collections.abc import Callable from functools import wraps from typing import Any, ParamSpec, TypeVar, cast + +P = ParamSpec("P") R = TypeVar("R") + +class DoubleLinkedListNode: """Node for LRU Cache""" + +__slots__ = ("key", "next", "prev", "val") + +def __init__(self, key: Any, val: Any) -> None: + self.key = key + self.val = val + self.next: DoubleLinkedListNode | None = None + self.prev: DoubleLinkedListNode | None = None + +def __repr__(self) -> str: + return f"Node(key={self.key}, val={self.val})" + +class DoubleLinkedList: """Double Linked List for LRU Cache""" + +def __init__(self) -> None: + # Create sentinel nodes + self.head: DoubleLinkedListNode = DoubleLinkedListNode(None, None) + self.rear: DoubleLinkedListNode = DoubleLinkedListNode(None, None) + # Link sentinel nodes together + self.head.next = self.rear + self.rear.prev = self.head + +def __repr__(self) -> str: + nodes = [] + current = self.head + while current: + nodes.append(repr(current)) + current = current.next + return f"LinkedList({nodes})" + +def add(self, node: DoubleLinkedListNode) -> None: + """Add node before rear""" + prev = self.rear.prev + if prev is None: + return + + # Insert node between prev and rear + prev.next = node + node.prev = prev + self.rear.prev = node + node.next = self.rear + +def remove(self, node: DoubleLinkedListNode) -> DoubleLinkedListNode | None: + """Remove node from list""" + if node.prev is None or node.next is None: return None - def put(self, key: Any, value: Any) -> None: - """Set value for key""" - if key in self.cache: - # Update existing node - node = self.cache[key] - if self.list.remove(node): - node.val = value - self.list.add(node) - return - - # Evict LRU item if at capacity - if self.size >= self.capacity: - first_node = self.list.head.next - if ( - first_node is not None - and first_node.key is not None - and first_node != self.list.rear - and self.list.remove(first_node) - ): - del self.cache[first_node.key] - self.size -= 1 - - # Add new node - new_node = DoubleLinkedListNode(key, value) - self.cache[key] = new_node - self.list.add(new_node) - self.size += 1 - - def cache_info(self) -> dict[str, Any]: - """Get cache statistics""" - return { - "hits": self.hits, - "misses": self.misses, - "capacity": self.capacity, - "size": self.size, - } - - -def lru_cache(maxsize: int = 128) -> Callable[[Callable[P, R]], Callable[P, R]]: - """LRU Cache decorator""" - - def decorator(func: Callable[P, R]) -> Callable[P, R]: - cache = LRUCache(maxsize) - - @wraps(func) - def wrapper(*args: P.args, **kwargs: P.kwargs) -> R: - # Create normalized cache key - key = (args, tuple(sorted(kwargs.items()))) - - # Try to get cached result - if (cached := cache.get(key)) is not None: - return cast(R, cached) - - # Compute and cache result - result = func(*args, **kwargs) - cache.put(key, result) - return result - - # Attach cache info method - wrapper.cache_info = cache.cache_info # type: ignore[attr-defined] - return wrapper - - return decorator - - -if __name__ == "__main__": - import doctest - - doctest.testmod() + # Bypass node + node.prev.next = node.next + node.next.prev = node.prev + + # Clear node references + node.prev = None + node.next = None + return node + +class LRUCache: """LRU Cache implementation""" + +def __init__(self, capacity: int) -> None: + self.list = DoubleLinkedList() + self.capacity = capacity + self.size = 0 + self.hits = 0 + self.misses = 0 + self.cache: dict[Any, DoubleLinkedListNode] = {} + +def __repr__(self) -> str: + return ( + f"Cache(hits={self.hits}, misses={self.misses}, " + f"cap={self.capacity}, size={self.size})" + ) + +def get(self, key: Any) -> Any | None: + """Get value for key""" + if key in self.cache: + self.hits += 1 + node = self.cache[key] + if self.list.remove(node): + self.list.add(node) + return node.val + self.misses += 1 + return None + +def put(self, key: Any, value: Any) -> None: + """Set value for key""" + if key in self.cache: + # Update existing node + node = self.cache[key] + if self.list.remove(node): + node.val = value + self.list.add(node) + return + + # Evict LRU item if at capacity + if self.size >= self.capacity: + first_node: DoubleLinkedListNode | None = self.list.head.next + if ( + first_node is not None + and first_node.key is not None + and first_node != self.list.rear + and self.list.remove(first_node) + ): + del self.cache[first_node.key] + self.size -= 1 + + # Add new node + new_node = DoubleLinkedListNode(key, value) + self.cache[key] = new_node + self.list.add(new_node) + self.size += 1 + +def cache_info(self) -> dict[str, Any]: + """Get cache statistics""" + return { + "hits": self.hits, + "misses": self.misses, + "capacity": self.capacity, + "size": self.size + } + +def lru_cache(maxsize: int = 128) -> Callable[[Callable[P, R]], Callable[P, R]]: """LRU Cache decorator""" def decorator(func: Callable[P, R]) -> Callable[P, R]: cache = LRUCache(maxsize) + +@wraps(func) + def wrapper(*args: P.args, **kwargs: P.kwargs) -> R: + # Create normalized cache key + key = (args, tuple(sorted(kwargs.items()))) + + # Try to get cached result + cached = cache.get(key) + if cached is not None: + return cast(R, cached) + + # Compute and cache result + result = func(*args, **kwargs) + cache.put(key, result) + return result + + # Attach cache info method + wrapper.cache_info = cache.cache_info # type: ignore[attr-defined] + return wrapper + +return decorator + +if name == "main": import doctest doctest.testmod() From 349e5f4f64f7a90b1035dd952c2cf23689150109 Mon Sep 17 00:00:00 2001 From: lighting9999 Date: Sun, 22 Jun 2025 21:55:10 +0800 Subject: [PATCH 106/107] Update lru_cache.py --- other/lru_cache.py | 314 ++++++++++++++++++++++++--------------------- 1 file changed, 165 insertions(+), 149 deletions(-) diff --git a/other/lru_cache.py b/other/lru_cache.py index 26a7400e03ea..80c2807c6020 100644 --- a/other/lru_cache.py +++ b/other/lru_cache.py @@ -1,151 +1,167 @@ -from future import annotations - -from collections.abc import Callable from functools import wraps from typing import Any, ParamSpec, TypeVar, cast - -P = ParamSpec("P") R = TypeVar("R") - -class DoubleLinkedListNode: """Node for LRU Cache""" - -__slots__ = ("key", "next", "prev", "val") - -def __init__(self, key: Any, val: Any) -> None: - self.key = key - self.val = val - self.next: DoubleLinkedListNode | None = None - self.prev: DoubleLinkedListNode | None = None - -def __repr__(self) -> str: - return f"Node(key={self.key}, val={self.val})" - -class DoubleLinkedList: """Double Linked List for LRU Cache""" - -def __init__(self) -> None: - # Create sentinel nodes - self.head: DoubleLinkedListNode = DoubleLinkedListNode(None, None) - self.rear: DoubleLinkedListNode = DoubleLinkedListNode(None, None) - # Link sentinel nodes together - self.head.next = self.rear - self.rear.prev = self.head - -def __repr__(self) -> str: - nodes = [] - current = self.head - while current: - nodes.append(repr(current)) - current = current.next - return f"LinkedList({nodes})" - -def add(self, node: DoubleLinkedListNode) -> None: - """Add node before rear""" - prev = self.rear.prev - if prev is None: - return - - # Insert node between prev and rear - prev.next = node - node.prev = prev - self.rear.prev = node - node.next = self.rear - -def remove(self, node: DoubleLinkedListNode) -> DoubleLinkedListNode | None: - """Remove node from list""" - if node.prev is None or node.next is None: +from __future__ import annotations + +from collections.abc import Callable +from functools import wraps +from typing import Any, ParamSpec, TypeVar, cast + +P = ParamSpec("P") +R = TypeVar("R") + + +class DoubleLinkedListNode: + """Node for LRU Cache""" + + __slots__ = ("key", "next", "prev", "val") + + def __init__(self, key: Any, val: Any) -> None: + self.key = key + self.val = val + self.next: DoubleLinkedListNode | None = None + self.prev: DoubleLinkedListNode | None = None + + def __repr__(self) -> str: + return f"Node(key={self.key}, val={self.val})" + + +class DoubleLinkedList: + """Double Linked List for LRU Cache""" + + def __init__(self) -> None: + # Create sentinel nodes + self.head: DoubleLinkedListNode = DoubleLinkedListNode(None, None) + self.rear: DoubleLinkedListNode = DoubleLinkedListNode(None, None) + # Link sentinel nodes together + self.head.next = self.rear + self.rear.prev = self.head + + def __repr__(self) -> str: + nodes = [] + current = self.head + while current: + nodes.append(repr(current)) + current = current.next + return f"LinkedList({nodes})" + + def add(self, node: DoubleLinkedListNode) -> None: + """Add node before rear""" + prev = self.rear.prev + if prev is None: + return + + # Insert node between prev and rear + prev.next = node + node.prev = prev + self.rear.prev = node + node.next = self.rear + + def remove(self, node: DoubleLinkedListNode) -> DoubleLinkedListNode | None: + """Remove node from list""" + if node.prev is None or node.next is None: + return None + + # Bypass node + node.prev.next = node.next + node.next.prev = node.prev + + # Clear node references + node.prev = None + node.next = None + return node + + +class LRUCache: + """LRU Cache implementation""" + + def __init__(self, capacity: int) -> None: + self.list = DoubleLinkedList() + self.capacity = capacity + self.size = 0 + self.hits = 0 + self.misses = 0 + self.cache: dict[Any, DoubleLinkedListNode] = {} + + def __repr__(self) -> str: + return ( + f"Cache(hits={self.hits}, misses={self.misses}, " + f"cap={self.capacity}, size={self.size})" + ) + + def get(self, key: Any) -> Any | None: + """Get value for key""" + if key in self.cache: + self.hits += 1 + node = self.cache[key] + if self.list.remove(node): + self.list.add(node) + return node.val + self.misses += 1 return None - # Bypass node - node.prev.next = node.next - node.next.prev = node.prev - - # Clear node references - node.prev = None - node.next = None - return node - -class LRUCache: """LRU Cache implementation""" - -def __init__(self, capacity: int) -> None: - self.list = DoubleLinkedList() - self.capacity = capacity - self.size = 0 - self.hits = 0 - self.misses = 0 - self.cache: dict[Any, DoubleLinkedListNode] = {} - -def __repr__(self) -> str: - return ( - f"Cache(hits={self.hits}, misses={self.misses}, " - f"cap={self.capacity}, size={self.size})" - ) - -def get(self, key: Any) -> Any | None: - """Get value for key""" - if key in self.cache: - self.hits += 1 - node = self.cache[key] - if self.list.remove(node): - self.list.add(node) - return node.val - self.misses += 1 - return None - -def put(self, key: Any, value: Any) -> None: - """Set value for key""" - if key in self.cache: - # Update existing node - node = self.cache[key] - if self.list.remove(node): - node.val = value - self.list.add(node) - return - - # Evict LRU item if at capacity - if self.size >= self.capacity: - first_node: DoubleLinkedListNode | None = self.list.head.next - if ( - first_node is not None - and first_node.key is not None - and first_node != self.list.rear - and self.list.remove(first_node) - ): - del self.cache[first_node.key] - self.size -= 1 - - # Add new node - new_node = DoubleLinkedListNode(key, value) - self.cache[key] = new_node - self.list.add(new_node) - self.size += 1 - -def cache_info(self) -> dict[str, Any]: - """Get cache statistics""" - return { - "hits": self.hits, - "misses": self.misses, - "capacity": self.capacity, - "size": self.size - } - -def lru_cache(maxsize: int = 128) -> Callable[[Callable[P, R]], Callable[P, R]]: """LRU Cache decorator""" def decorator(func: Callable[P, R]) -> Callable[P, R]: cache = LRUCache(maxsize) - -@wraps(func) - def wrapper(*args: P.args, **kwargs: P.kwargs) -> R: - # Create normalized cache key - key = (args, tuple(sorted(kwargs.items()))) - - # Try to get cached result - cached = cache.get(key) - if cached is not None: - return cast(R, cached) - - # Compute and cache result - result = func(*args, **kwargs) - cache.put(key, result) - return result - - # Attach cache info method - wrapper.cache_info = cache.cache_info # type: ignore[attr-defined] - return wrapper - -return decorator - -if name == "main": import doctest doctest.testmod() + def put(self, key: Any, value: Any) -> None: + """Set value for key""" + if key in self.cache: + # Update existing node + node = self.cache[key] + if self.list.remove(node): + node.val = value + self.list.add(node) + return + + # Evict LRU item if at capacity + if self.size >= self.capacity: + first_node: DoubleLinkedListNode | None = self.list.head.next + if ( + first_node is not None + and first_node.key is not None + and first_node != self.list.rear + and self.list.remove(first_node) + ): + del self.cache[first_node.key] + self.size -= 1 + + # Add new node + new_node = DoubleLinkedListNode(key, value) + self.cache[key] = new_node + self.list.add(new_node) + self.size += 1 + + def cache_info(self) -> dict[str, Any]: + """Get cache statistics""" + return { + "hits": self.hits, + "misses": self.misses, + "capacity": self.capacity, + "size": self.size + } + + +def lru_cache(maxsize: int = 128) -> Callable[[Callable[P, R]], Callable[P, R]]: + """LRU Cache decorator""" + def decorator(func: Callable[P, R]) -> Callable[P, R]: + cache = LRUCache(maxsize) + + @wraps(func) + def wrapper(*args: P.args, **kwargs: P.kwargs) -> R: + # Create normalized cache key + key = (args, tuple(sorted(kwargs.items()))) + + # Try to get cached result + cached = cache.get(key) + if cached is not None: + return cast(R, cached) + + # Compute and cache result + result = func(*args, **kwargs) + cache.put(key, result) + return result + + # Attach cache info method + wrapper.cache_info = cache.cache_info # type: ignore[attr-defined] + return wrapper + + return decorator + + +if __name__ == "__main__": + import doctest + doctest.testmod() From 2e378aca93f98a45c5633c8eb0b5887dd2a36304 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 22 Jun 2025 13:55:33 +0000 Subject: [PATCH 107/107] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- other/lru_cache.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/other/lru_cache.py b/other/lru_cache.py index 80c2807c6020..eb8b1d61da43 100644 --- a/other/lru_cache.py +++ b/other/lru_cache.py @@ -131,12 +131,13 @@ def cache_info(self) -> dict[str, Any]: "hits": self.hits, "misses": self.misses, "capacity": self.capacity, - "size": self.size + "size": self.size, } def lru_cache(maxsize: int = 128) -> Callable[[Callable[P, R]], Callable[P, R]]: """LRU Cache decorator""" + def decorator(func: Callable[P, R]) -> Callable[P, R]: cache = LRUCache(maxsize) @@ -146,8 +147,7 @@ def wrapper(*args: P.args, **kwargs: P.kwargs) -> R: key = (args, tuple(sorted(kwargs.items()))) # Try to get cached result - cached = cache.get(key) - if cached is not None: + if (cached := cache.get(key)) is not None: return cast(R, cached) # Compute and cache result @@ -164,4 +164,5 @@ def wrapper(*args: P.args, **kwargs: P.kwargs) -> R: if __name__ == "__main__": import doctest + doctest.testmod()