From 937ce83b150f0a217c7fa63c75a095534ae8bfeb Mon Sep 17 00:00:00 2001 From: Om Alve Date: Sun, 8 Oct 2023 16:35:01 +0530 Subject: [PATCH 01/22] Added fractionated_morse_cipher (#9442) * Added fractionated_morse_cipher * Added return type hint for main function * Added doctest for main * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Replaced main function * changed the references section Co-authored-by: Christian Clauss * removed repetitive datatype hint in the docstring Co-authored-by: Christian Clauss * changed dictionary comprehension variable names to something more compact Co-authored-by: Christian Clauss * Update fractionated_morse_cipher.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- ciphers/fractionated_morse_cipher.py | 167 +++++++++++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100644 ciphers/fractionated_morse_cipher.py diff --git a/ciphers/fractionated_morse_cipher.py b/ciphers/fractionated_morse_cipher.py new file mode 100644 index 000000000..c1d5dc6d5 --- /dev/null +++ b/ciphers/fractionated_morse_cipher.py @@ -0,0 +1,167 @@ +""" +Python program for the Fractionated Morse Cipher. + +The Fractionated Morse cipher first converts the plaintext to Morse code, +then enciphers fixed-size blocks of Morse code back to letters. +This procedure means plaintext letters are mixed into the ciphertext letters, +making it more secure than substitution ciphers. + +http://practicalcryptography.com/ciphers/fractionated-morse-cipher/ +""" +import string + +MORSE_CODE_DICT = { + "A": ".-", + "B": "-...", + "C": "-.-.", + "D": "-..", + "E": ".", + "F": "..-.", + "G": "--.", + "H": "....", + "I": "..", + "J": ".---", + "K": "-.-", + "L": ".-..", + "M": "--", + "N": "-.", + "O": "---", + "P": ".--.", + "Q": "--.-", + "R": ".-.", + "S": "...", + "T": "-", + "U": "..-", + "V": "...-", + "W": ".--", + "X": "-..-", + "Y": "-.--", + "Z": "--..", + " ": "", +} + +# Define possible trigrams of Morse code +MORSE_COMBINATIONS = [ + "...", + "..-", + "..x", + ".-.", + ".--", + ".-x", + ".x.", + ".x-", + ".xx", + "-..", + "-.-", + "-.x", + "--.", + "---", + "--x", + "-x.", + "-x-", + "-xx", + "x..", + "x.-", + "x.x", + "x-.", + "x--", + "x-x", + "xx.", + "xx-", + "xxx", +] + +# Create a reverse dictionary for Morse code +REVERSE_DICT = {value: key for key, value in MORSE_CODE_DICT.items()} + + +def encode_to_morse(plaintext: str) -> str: + """Encode a plaintext message into Morse code. + + Args: + plaintext: The plaintext message to encode. + + Returns: + The Morse code representation of the plaintext message. + + Example: + >>> encode_to_morse("defend the east") + '-..x.x..-.x.x-.x-..xx-x....x.xx.x.-x...x-' + """ + return "x".join([MORSE_CODE_DICT.get(letter.upper(), "") for letter in plaintext]) + + +def encrypt_fractionated_morse(plaintext: str, key: str) -> str: + """Encrypt a plaintext message using Fractionated Morse Cipher. + + Args: + plaintext: The plaintext message to encrypt. + key: The encryption key. + + Returns: + The encrypted ciphertext. + + Example: + >>> encrypt_fractionated_morse("defend the east","Roundtable") + 'ESOAVVLJRSSTRX' + + """ + morse_code = encode_to_morse(plaintext) + key = key.upper() + string.ascii_uppercase + key = "".join(sorted(set(key), key=key.find)) + + # Ensure morse_code length is a multiple of 3 + padding_length = 3 - (len(morse_code) % 3) + morse_code += "x" * padding_length + + fractionated_morse_dict = {v: k for k, v in zip(key, MORSE_COMBINATIONS)} + fractionated_morse_dict["xxx"] = "" + encrypted_text = "".join( + [ + fractionated_morse_dict[morse_code[i : i + 3]] + for i in range(0, len(morse_code), 3) + ] + ) + return encrypted_text + + +def decrypt_fractionated_morse(ciphertext: str, key: str) -> str: + """Decrypt a ciphertext message encrypted with Fractionated Morse Cipher. + + Args: + ciphertext: The ciphertext message to decrypt. + key: The decryption key. + + Returns: + The decrypted plaintext message. + + Example: + >>> decrypt_fractionated_morse("ESOAVVLJRSSTRX","Roundtable") + 'DEFEND THE EAST' + """ + key = key.upper() + string.ascii_uppercase + key = "".join(sorted(set(key), key=key.find)) + + inverse_fractionated_morse_dict = dict(zip(key, MORSE_COMBINATIONS)) + morse_code = "".join( + [inverse_fractionated_morse_dict.get(letter, "") for letter in ciphertext] + ) + decrypted_text = "".join( + [REVERSE_DICT[code] for code in morse_code.split("x")] + ).strip() + return decrypted_text + + +if __name__ == "__main__": + """ + Example usage of Fractionated Morse Cipher. + """ + plaintext = "defend the east" + print("Plain Text:", plaintext) + key = "ROUNDTABLE" + + ciphertext = encrypt_fractionated_morse(plaintext, key) + print("Encrypted:", ciphertext) + + decrypted_text = decrypt_fractionated_morse(ciphertext, key) + print("Decrypted:", decrypted_text) From 08d394126c9d46fc9d227a0dc1e343ad1fa70679 Mon Sep 17 00:00:00 2001 From: Kausthub Kannan Date: Sun, 8 Oct 2023 21:18:22 +0530 Subject: [PATCH 02/22] Changed Mish Activation Function to use Softplus (#10111) --- neural_network/activation_functions/mish.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/neural_network/activation_functions/mish.py b/neural_network/activation_functions/mish.py index e4f98307f..e51655df8 100644 --- a/neural_network/activation_functions/mish.py +++ b/neural_network/activation_functions/mish.py @@ -7,6 +7,7 @@ https://en.wikipedia.org/wiki/Rectifier_(neural_networks)#Mish """ import numpy as np +from softplus import softplus def mish(vector: np.ndarray) -> np.ndarray: @@ -30,7 +31,7 @@ def mish(vector: np.ndarray) -> np.ndarray: array([-0.00092952, -0.15113318, 0.33152014, -0.04745745]) """ - return vector * np.tanh(np.log(1 + np.exp(vector))) + return vector * np.tanh(softplus(vector)) if __name__ == "__main__": From 6860daea60a512b202481bd5dd00d6534e162b77 Mon Sep 17 00:00:00 2001 From: Aarya Balwadkar <142713127+AaryaBalwadkar@users.noreply.github.com> Date: Sun, 8 Oct 2023 21:23:38 +0530 Subject: [PATCH 03/22] Made Changes shifted CRT, modular division to maths directory (#10084) --- {blockchain => maths}/chinese_remainder_theorem.py | 0 {blockchain => maths}/modular_division.py | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename {blockchain => maths}/chinese_remainder_theorem.py (100%) rename {blockchain => maths}/modular_division.py (100%) diff --git a/blockchain/chinese_remainder_theorem.py b/maths/chinese_remainder_theorem.py similarity index 100% rename from blockchain/chinese_remainder_theorem.py rename to maths/chinese_remainder_theorem.py diff --git a/blockchain/modular_division.py b/maths/modular_division.py similarity index 100% rename from blockchain/modular_division.py rename to maths/modular_division.py From 81b29066d206217cb689fe2c9c8d530a1aa66cbe Mon Sep 17 00:00:00 2001 From: Arnav Kohli <95236897+THEGAMECHANGER416@users.noreply.github.com> Date: Sun, 8 Oct 2023 21:34:43 +0530 Subject: [PATCH 04/22] Created folder for losses in Machine_Learning (#9969) * Created folder for losses in Machine_Learning * Update binary_cross_entropy.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update mean_squared_error.py * Update binary_cross_entropy.py * Update mean_squared_error.py * Update binary_cross_entropy.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update mean_squared_error.py * Update binary_cross_entropy.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update mean_squared_error.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update binary_cross_entropy.py * Update mean_squared_error.py * Update binary_cross_entropy.py * Update mean_squared_error.py * Update machine_learning/losses/binary_cross_entropy.py Co-authored-by: Christian Clauss * Update machine_learning/losses/mean_squared_error.py Co-authored-by: Christian Clauss * Update machine_learning/losses/binary_cross_entropy.py Co-authored-by: Christian Clauss * Update mean_squared_error.py * Update machine_learning/losses/mean_squared_error.py Co-authored-by: Tianyi Zheng * Update binary_cross_entropy.py * Update mean_squared_error.py * Update binary_cross_entropy.py * Update mean_squared_error.py * Update mean_squared_error.py * Update binary_cross_entropy.py * renamed: losses -> loss_functions * updated 2 files * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update mean_squared_error.py * Update mean_squared_error.py * Update binary_cross_entropy.py * Update mean_squared_error.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss Co-authored-by: Tianyi Zheng --- .../loss_functions/binary_cross_entropy.py | 59 +++++++++++++++++++ .../loss_functions/mean_squared_error.py | 51 ++++++++++++++++ 2 files changed, 110 insertions(+) create mode 100644 machine_learning/loss_functions/binary_cross_entropy.py create mode 100644 machine_learning/loss_functions/mean_squared_error.py diff --git a/machine_learning/loss_functions/binary_cross_entropy.py b/machine_learning/loss_functions/binary_cross_entropy.py new file mode 100644 index 000000000..4ebca7f21 --- /dev/null +++ b/machine_learning/loss_functions/binary_cross_entropy.py @@ -0,0 +1,59 @@ +""" +Binary Cross-Entropy (BCE) Loss Function + +Description: +Quantifies dissimilarity between true labels (0 or 1) and predicted probabilities. +It's widely used in binary classification tasks. + +Formula: +BCE = -Σ(y_true * log(y_pred) + (1 - y_true) * log(1 - y_pred)) + +Source: +[Wikipedia - Cross entropy](https://en.wikipedia.org/wiki/Cross_entropy) +""" + +import numpy as np + + +def binary_cross_entropy( + y_true: np.ndarray, y_pred: np.ndarray, epsilon: float = 1e-15 +) -> float: + """ + Calculate the BCE Loss between true labels and predicted probabilities. + + Parameters: + - y_true: True binary labels (0 or 1). + - y_pred: Predicted probabilities for class 1. + - epsilon: Small constant to avoid numerical instability. + + Returns: + - bce_loss: Binary Cross-Entropy Loss. + + Example Usage: + >>> true_labels = np.array([0, 1, 1, 0, 1]) + >>> predicted_probs = np.array([0.2, 0.7, 0.9, 0.3, 0.8]) + >>> binary_cross_entropy(true_labels, predicted_probs) + 0.2529995012327421 + >>> true_labels = np.array([0, 1, 1, 0, 1]) + >>> predicted_probs = np.array([0.3, 0.8, 0.9, 0.2]) + >>> binary_cross_entropy(true_labels, predicted_probs) + Traceback (most recent call last): + ... + ValueError: Input arrays must have the same length. + """ + if len(y_true) != len(y_pred): + raise ValueError("Input arrays must have the same length.") + # Clip predicted probabilities to avoid log(0) and log(1) + y_pred = np.clip(y_pred, epsilon, 1 - epsilon) + + # Calculate binary cross-entropy loss + bce_loss = -(y_true * np.log(y_pred) + (1 - y_true) * np.log(1 - y_pred)) + + # Take the mean over all samples + return np.mean(bce_loss) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() diff --git a/machine_learning/loss_functions/mean_squared_error.py b/machine_learning/loss_functions/mean_squared_error.py new file mode 100644 index 000000000..d2b0e1e15 --- /dev/null +++ b/machine_learning/loss_functions/mean_squared_error.py @@ -0,0 +1,51 @@ +""" +Mean Squared Error (MSE) Loss Function + +Description: +MSE measures the mean squared difference between true values and predicted values. +It serves as a measure of the model's accuracy in regression tasks. + +Formula: +MSE = (1/n) * Σ(y_true - y_pred)^2 + +Source: +[Wikipedia - Mean squared error](https://en.wikipedia.org/wiki/Mean_squared_error) +""" + +import numpy as np + + +def mean_squared_error(y_true: np.ndarray, y_pred: np.ndarray) -> float: + """ + Calculate the Mean Squared Error (MSE) between two arrays. + + Parameters: + - y_true: The true values (ground truth). + - y_pred: The predicted values. + + Returns: + - mse: The Mean Squared Error between y_true and y_pred. + + Example usage: + >>> true_values = np.array([1.0, 2.0, 3.0, 4.0, 5.0]) + >>> predicted_values = np.array([0.8, 2.1, 2.9, 4.2, 5.2]) + >>> mean_squared_error(true_values, predicted_values) + 0.028000000000000032 + >>> true_labels = np.array([1.0, 2.0, 3.0, 4.0, 5.0]) + >>> predicted_probs = np.array([0.3, 0.8, 0.9, 0.2]) + >>> mean_squared_error(true_labels, predicted_probs) + Traceback (most recent call last): + ... + ValueError: Input arrays must have the same length. + """ + if len(y_true) != len(y_pred): + raise ValueError("Input arrays must have the same length.") + + squared_errors = (y_true - y_pred) ** 2 + return np.mean(squared_errors) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From a12b07f352d51af1cb86c14f865cf2b18aba3ea1 Mon Sep 17 00:00:00 2001 From: Kausthub Kannan Date: Sun, 8 Oct 2023 21:38:37 +0530 Subject: [PATCH 05/22] Added Squareplus Activation Function (#9977) * Added Squareplus Activation Function * Added parameter beta to the function * Fixed Squareplus Function * Update neural_network/activation_functions/squareplus.py --------- Co-authored-by: Tianyi Zheng --- .../activation_functions/squareplus.py | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 neural_network/activation_functions/squareplus.py diff --git a/neural_network/activation_functions/squareplus.py b/neural_network/activation_functions/squareplus.py new file mode 100644 index 000000000..40fa800d6 --- /dev/null +++ b/neural_network/activation_functions/squareplus.py @@ -0,0 +1,38 @@ +""" +Squareplus Activation Function + +Use Case: Squareplus designed to enhance positive values and suppress negative values. +For more detailed information, you can refer to the following link: +https://en.wikipedia.org/wiki/Rectifier_(neural_networks)#Squareplus +""" + +import numpy as np + + +def squareplus(vector: np.ndarray, beta: float) -> np.ndarray: + """ + Implements the SquarePlus activation function. + + Parameters: + vector (np.ndarray): The input array for the SquarePlus activation. + beta (float): size of the curved region + + Returns: + np.ndarray: The input array after applying the SquarePlus activation. + + Formula: f(x) = ( x + sqrt(x^2 + b) ) / 2 + + Examples: + >>> squareplus(np.array([2.3, 0.6, -2, -3.8]), beta=2) + array([2.5 , 1.06811457, 0.22474487, 0.12731349]) + + >>> squareplus(np.array([-9.2, -0.3, 0.45, -4.56]), beta=3) + array([0.0808119 , 0.72891979, 1.11977651, 0.15893419]) + """ + return (vector + np.sqrt(vector**2 + beta)) / 2 + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From e89ae55d8e157cb7c6c3f855188a0fde29083c35 Mon Sep 17 00:00:00 2001 From: Saurabh Mahapatra <98408932+its-100rabh@users.noreply.github.com> Date: Sun, 8 Oct 2023 21:40:41 +0530 Subject: [PATCH 06/22] Create strip.py (#10011) * Create strip.py * Update strip.py --- strings/strip.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 strings/strip.py diff --git a/strings/strip.py b/strings/strip.py new file mode 100644 index 000000000..d4f901f0c --- /dev/null +++ b/strings/strip.py @@ -0,0 +1,33 @@ +def strip(user_string: str, characters: str = " \t\n\r") -> str: + """ + Remove leading and trailing characters (whitespace by default) from a string. + + Args: + user_string (str): The input string to be stripped. + characters (str, optional): Optional characters to be removed + (default is whitespace). + + Returns: + str: The stripped string. + + Examples: + >>> strip(" hello ") + 'hello' + >>> strip("...world...", ".") + 'world' + >>> strip("123hello123", "123") + 'hello' + >>> strip("") + '' + """ + + start = 0 + end = len(user_string) + + while start < end and user_string[start] in characters: + start += 1 + + while end > start and user_string[end - 1] in characters: + end -= 1 + + return user_string[start:end] From 982bc2735872592d036c20389859071f36b13469 Mon Sep 17 00:00:00 2001 From: Kosuri L Indu <118645569+kosuri-indu@users.noreply.github.com> Date: Sun, 8 Oct 2023 22:37:02 +0530 Subject: [PATCH 07/22] add : Best time to buy and sell stock program under GREEDY methods (#10114) * to add best_time_stock program * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update best_time_to_buy_and_sell_stock.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- .../best_time_to_buy_and_sell_stock.py | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 greedy_methods/best_time_to_buy_and_sell_stock.py diff --git a/greedy_methods/best_time_to_buy_and_sell_stock.py b/greedy_methods/best_time_to_buy_and_sell_stock.py new file mode 100644 index 000000000..4aea19172 --- /dev/null +++ b/greedy_methods/best_time_to_buy_and_sell_stock.py @@ -0,0 +1,42 @@ +""" +Given a list of stock prices calculate the maximum profit that can be made from a +single buy and sell of one share of stock. We only allowed to complete one buy +transaction and one sell transaction but must buy before we sell. + +Example : prices = [7, 1, 5, 3, 6, 4] +max_profit will return 5 - which is by buying at price 1 and selling at price 6. + +This problem can be solved using the concept of "GREEDY ALGORITHM". + +We iterate over the price array once, keeping track of the lowest price point +(buy) and the maximum profit we can get at each point. The greedy choice at each point +is to either buy at the current price if it's less than our current buying price, or +sell at the current price if the profit is more than our current maximum profit. +""" + + +def max_profit(prices: list[int]) -> int: + """ + >>> max_profit([7, 1, 5, 3, 6, 4]) + 5 + >>> max_profit([7, 6, 4, 3, 1]) + 0 + """ + if not prices: + return 0 + + min_price = prices[0] + max_profit: int = 0 + + for price in prices: + min_price = min(price, min_price) + max_profit = max(price - min_price, max_profit) + + return max_profit + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + print(max_profit([7, 1, 5, 3, 6, 4])) From e7a59bfff5b182fb01530aa4b1a29b804efb1425 Mon Sep 17 00:00:00 2001 From: SubhranShu2332 <124662904+SubhranShu2332@users.noreply.github.com> Date: Mon, 9 Oct 2023 00:47:02 +0530 Subject: [PATCH 08/22] In place of calculating the factorial several times we can run a loop k times to calculate the combination (#10051) * In place of calculating the factorial several times we can run a loop k times to calculate the combination for example: 5 C 3 = 5! / (3! * (5-3)! ) = (5*4*3*2*1)/[(3*2*1)*(2*1)] =(5*4*3)/(3*2*1) so running a loop k times will reduce the time complexity to O(k) * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update maths/combinations.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Tianyi Zheng --- maths/combinations.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/maths/combinations.py b/maths/combinations.py index a2324012c..6e9e1a807 100644 --- a/maths/combinations.py +++ b/maths/combinations.py @@ -1,7 +1,6 @@ """ https://en.wikipedia.org/wiki/Combination """ -from math import factorial def combinations(n: int, k: int) -> int: @@ -35,7 +34,11 @@ def combinations(n: int, k: int) -> int: # to calculate a factorial of a negative number, which is not possible if n < k or k < 0: raise ValueError("Please enter positive integers for n and k where n >= k") - return factorial(n) // (factorial(k) * factorial(n - k)) + res = 1 + for i in range(k): + res *= n - i + res //= i + 1 + return res if __name__ == "__main__": From c8f6f79f8038ef090a396725c80fa77d9186fb4b Mon Sep 17 00:00:00 2001 From: Siddharth Warrier <117698635+siddwarr@users.noreply.github.com> Date: Mon, 9 Oct 2023 01:10:14 +0530 Subject: [PATCH 09/22] Power of 4 (#9505) * added power_of_4 * updated power_of_4 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * updated power_of_4 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * updated power_of_4 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * updated power_of_4 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * updated power_of_4 * added type check * added tescase --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- bit_manipulation/power_of_4.py | 67 ++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 bit_manipulation/power_of_4.py diff --git a/bit_manipulation/power_of_4.py b/bit_manipulation/power_of_4.py new file mode 100644 index 000000000..09e6e2862 --- /dev/null +++ b/bit_manipulation/power_of_4.py @@ -0,0 +1,67 @@ +""" + +Task: +Given a positive int number. Return True if this number is power of 4 +or False otherwise. + +Implementation notes: Use bit manipulation. +For example if the number is the power of 2 it's bits representation: +n = 0..100..00 +n - 1 = 0..011..11 + +n & (n - 1) - no intersections = 0 +If the number is a power of 4 then it should be a power of 2 +and the set bit should be at an odd position. +""" + + +def power_of_4(number: int) -> bool: + """ + Return True if this number is power of 4 or False otherwise. + + >>> power_of_4(0) + Traceback (most recent call last): + ... + ValueError: number must be positive + >>> power_of_4(1) + True + >>> power_of_4(2) + False + >>> power_of_4(4) + True + >>> power_of_4(6) + False + >>> power_of_4(8) + False + >>> power_of_4(17) + False + >>> power_of_4(64) + True + >>> power_of_4(-1) + Traceback (most recent call last): + ... + ValueError: number must be positive + >>> power_of_4(1.2) + Traceback (most recent call last): + ... + TypeError: number must be an integer + + """ + if not isinstance(number, int): + raise TypeError("number must be an integer") + if number <= 0: + raise ValueError("number must be positive") + if number & (number - 1) == 0: + c = 0 + while number: + c += 1 + number >>= 1 + return c % 2 == 1 + else: + return False + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 17de908d1ad5a3eb7eb0c850e64394b62e4674c3 Mon Sep 17 00:00:00 2001 From: Achal Jain Date: Mon, 9 Oct 2023 01:11:30 +0530 Subject: [PATCH 10/22] Added Median of Medians Algorithm (#9864) * Added Median of Medians Algorithm * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update median_of_medians.py as per requested changes * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- searches/median_of_medians.py | 107 ++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 searches/median_of_medians.py diff --git a/searches/median_of_medians.py b/searches/median_of_medians.py new file mode 100644 index 000000000..a8011a34a --- /dev/null +++ b/searches/median_of_medians.py @@ -0,0 +1,107 @@ +""" +A Python implementation of the Median of Medians algorithm +to select pivots for quick_select, which is efficient for +calculating the value that would appear in the index of a +list if it would be sorted, even if it is not already +sorted. Search in time complexity O(n) at any rank +deterministically +https://en.wikipedia.org/wiki/Median_of_medians +""" + + +def median_of_five(arr: list) -> int: + """ + Return the median of the input list + :param arr: Array to find median of + :return: median of arr + + >>> median_of_five([2, 4, 5, 7, 899]) + 5 + >>> median_of_five([5, 7, 899, 54, 32]) + 32 + >>> median_of_five([5, 4, 3, 2]) + 4 + >>> median_of_five([3, 5, 7, 10, 2]) + 5 + """ + arr = sorted(arr) + return arr[len(arr) // 2] + + +def median_of_medians(arr: list) -> int: + """ + Return a pivot to partition data on by calculating + Median of medians of input data + :param arr: The data to be checked (a list) + :return: median of medians of input array + + >>> median_of_medians([2, 4, 5, 7, 899, 54, 32]) + 54 + >>> median_of_medians([5, 7, 899, 54, 32]) + 32 + >>> median_of_medians([5, 4, 3, 2]) + 4 + >>> median_of_medians([3, 5, 7, 10, 2, 12]) + 12 + """ + + if len(arr) <= 5: + return median_of_five(arr) + medians = [] + i = 0 + while i < len(arr): + if (i + 4) <= len(arr): + medians.append(median_of_five(arr[i:].copy())) + else: + medians.append(median_of_five(arr[i : i + 5].copy())) + i += 5 + return median_of_medians(medians) + + +def quick_select(arr: list, target: int) -> int: + """ + Two way partition the data into smaller and greater lists, + in relationship to the pivot + :param arr: The data to be searched (a list) + :param target: The rank to be searched + :return: element at rank target + + >>> quick_select([2, 4, 5, 7, 899, 54, 32], 5) + 32 + >>> quick_select([2, 4, 5, 7, 899, 54, 32], 1) + 2 + >>> quick_select([5, 4, 3, 2], 2) + 3 + >>> quick_select([3, 5, 7, 10, 2, 12], 3) + 5 + """ + + # Invalid Input + if target > len(arr): + return -1 + + # x is the estimated pivot by median of medians algorithm + x = median_of_medians(arr) + left = [] + right = [] + check = False + for i in range(len(arr)): + if arr[i] < x: + left.append(arr[i]) + elif arr[i] > x: + right.append(arr[i]) + elif arr[i] == x and not check: + check = True + else: + right.append(arr[i]) + rank_x = len(left) + 1 + if rank_x == target: + answer = x + elif rank_x > target: + answer = quick_select(left, target) + elif rank_x < target: + answer = quick_select(right, target - rank_x) + return answer + + +print(median_of_five([5, 4, 3, 2])) From 8e108ed92ab9a50d5a3e6f647fa33238270e21d1 Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Sun, 8 Oct 2023 15:43:07 -0400 Subject: [PATCH 11/22] Rename maths/binary_exponentiation_3.py (#9656) * updating DIRECTORY.md * Rename binary_exponentiation_3.py Rename binary_exponentiation_3.py to binary_exponentiation_2.py because the original binary_exponentiation_2.py was renamed to binary_multiplication.py in PR #9513 * updating DIRECTORY.md * updating DIRECTORY.md * updating DIRECTORY.md * updating DIRECTORY.md --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 5 +++-- ...binary_exponentiation_3.py => binary_exponentiation_2.py} | 0 2 files changed, 3 insertions(+), 2 deletions(-) rename maths/{binary_exponentiation_3.py => binary_exponentiation_2.py} (100%) diff --git a/DIRECTORY.md b/DIRECTORY.md index a975b9264..55b270624 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -556,7 +556,7 @@ * [Bell Numbers](maths/bell_numbers.py) * [Binary Exp Mod](maths/binary_exp_mod.py) * [Binary Exponentiation](maths/binary_exponentiation.py) - * [Binary Exponentiation 3](maths/binary_exponentiation_3.py) + * [Binary Exponentiation 2](maths/binary_exponentiation_2.py) * [Binary Multiplication](maths/binary_multiplication.py) * [Binomial Coefficient](maths/binomial_coefficient.py) * [Binomial Distribution](maths/binomial_distribution.py) @@ -588,7 +588,6 @@ * [Find Min](maths/find_min.py) * [Floor](maths/floor.py) * [Gamma](maths/gamma.py) - * [Gamma Recursive](maths/gamma_recursive.py) * [Gaussian](maths/gaussian.py) * [Gaussian Error Linear Unit](maths/gaussian_error_linear_unit.py) * [Gcd Of N Numbers](maths/gcd_of_n_numbers.py) @@ -723,6 +722,7 @@ * Activation Functions * [Exponential Linear Unit](neural_network/activation_functions/exponential_linear_unit.py) * [Leaky Rectified Linear Unit](neural_network/activation_functions/leaky_rectified_linear_unit.py) + * [Mish](neural_network/activation_functions/mish.py) * [Rectified Linear Unit](neural_network/activation_functions/rectified_linear_unit.py) * [Scaled Exponential Linear Unit](neural_network/activation_functions/scaled_exponential_linear_unit.py) * [Sigmoid Linear Unit](neural_network/activation_functions/sigmoid_linear_unit.py) @@ -748,6 +748,7 @@ * [Linear Congruential Generator](other/linear_congruential_generator.py) * [Lru Cache](other/lru_cache.py) * [Magicdiamondpattern](other/magicdiamondpattern.py) + * [Majority Vote Algorithm](other/majority_vote_algorithm.py) * [Maximum Subsequence](other/maximum_subsequence.py) * [Nested Brackets](other/nested_brackets.py) * [Number Container System](other/number_container_system.py) diff --git a/maths/binary_exponentiation_3.py b/maths/binary_exponentiation_2.py similarity index 100% rename from maths/binary_exponentiation_3.py rename to maths/binary_exponentiation_2.py From 2d02500332533bb314f91675a3c30ea05bd52b5a Mon Sep 17 00:00:00 2001 From: halfhearted <99018821+Arunsiva003@users.noreply.github.com> Date: Mon, 9 Oct 2023 01:14:49 +0530 Subject: [PATCH 12/22] equilibrium index in an array (#9856) * equilibrium index in an array * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * equilibrium index in an array * equilibrium index in an array * equilibrium index in an array removed type in docstring --------- Co-authored-by: ArunSiva Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .../arrays/equilibrium_index_in_array.py | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 data_structures/arrays/equilibrium_index_in_array.py diff --git a/data_structures/arrays/equilibrium_index_in_array.py b/data_structures/arrays/equilibrium_index_in_array.py new file mode 100644 index 000000000..4099896d2 --- /dev/null +++ b/data_structures/arrays/equilibrium_index_in_array.py @@ -0,0 +1,59 @@ +""" +Find the Equilibrium Index of an Array. +Reference: https://www.geeksforgeeks.org/equilibrium-index-of-an-array/ + +Python doctests can be run with the following command: +python -m doctest -v equilibrium_index.py + +Given a sequence arr[] of size n, this function returns +an equilibrium index (if any) or -1 if no equilibrium index exists. + +The equilibrium index of an array is an index such that the sum of +elements at lower indexes is equal to the sum of elements at higher indexes. + + + +Example Input: +arr = [-7, 1, 5, 2, -4, 3, 0] +Output: 3 + +""" + + +def equilibrium_index(arr: list[int], size: int) -> int: + """ + Find the equilibrium index of an array. + + Args: + arr : The input array of integers. + size : The size of the array. + + Returns: + int: The equilibrium index or -1 if no equilibrium index exists. + + Examples: + >>> equilibrium_index([-7, 1, 5, 2, -4, 3, 0], 7) + 3 + >>> equilibrium_index([1, 2, 3, 4, 5], 5) + -1 + >>> equilibrium_index([1, 1, 1, 1, 1], 5) + 2 + >>> equilibrium_index([2, 4, 6, 8, 10, 3], 6) + -1 + """ + total_sum = sum(arr) + left_sum = 0 + + for i in range(size): + total_sum -= arr[i] + if left_sum == total_sum: + return i + left_sum += arr[i] + + return -1 + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 66e4ea6a621cccabd6116f1543432899a4411daa Mon Sep 17 00:00:00 2001 From: Anshu Sharma <142900182+AnshuSharma111@users.noreply.github.com> Date: Mon, 9 Oct 2023 01:47:22 +0530 Subject: [PATCH 13/22] Consolidated two scripts reverse_letters.py and reverse_long_words.py into one (#10140) * Conolidated two scripts reverse_letters.py and reverse_long_words.py into one because of similar functionality * Added a new line to accomodate characters without going over 88 char limit * fixed grammar to pass pre-commit * Changed faulty test case entirely to pass pre commit * fixed a test case which was wrong --------- Co-authored-by: Keyboard-1 <142900182+Keyboard-1@users.noreply.github.com> --- DIRECTORY.md | 1 - strings/reverse_letters.py | 27 ++++++++++++++++----------- strings/reverse_long_words.py | 21 --------------------- 3 files changed, 16 insertions(+), 33 deletions(-) delete mode 100644 strings/reverse_long_words.py diff --git a/DIRECTORY.md b/DIRECTORY.md index 55b270624..b1a23a239 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -1197,7 +1197,6 @@ * [Rabin Karp](strings/rabin_karp.py) * [Remove Duplicate](strings/remove_duplicate.py) * [Reverse Letters](strings/reverse_letters.py) - * [Reverse Long Words](strings/reverse_long_words.py) * [Reverse Words](strings/reverse_words.py) * [Snake Case To Camel Pascal Case](strings/snake_case_to_camel_pascal_case.py) * [Split](strings/split.py) diff --git a/strings/reverse_letters.py b/strings/reverse_letters.py index 10b8a6d72..4f73f816b 100644 --- a/strings/reverse_letters.py +++ b/strings/reverse_letters.py @@ -1,19 +1,24 @@ -def reverse_letters(input_str: str) -> str: +def reverse_letters(sentence: str, length: int = 0) -> str: """ - Reverses letters in a given string without adjusting the position of the words - >>> reverse_letters('The cat in the hat') - 'ehT tac ni eht tah' - >>> reverse_letters('The quick brown fox jumped over the lazy dog.') - 'ehT kciuq nworb xof depmuj revo eht yzal .god' - >>> reverse_letters('Is this true?') - 'sI siht ?eurt' - >>> reverse_letters("I love Python") - 'I evol nohtyP' + Reverse all words that are longer than the given length of characters in a sentence. + If unspecified, length is taken as 0 + + >>> reverse_letters("Hey wollef sroirraw", 3) + 'Hey fellow warriors' + >>> reverse_letters("nohtyP is nohtyP", 2) + 'Python is Python' + >>> reverse_letters("1 12 123 1234 54321 654321", 0) + '1 21 321 4321 12345 123456' + >>> reverse_letters("racecar") + 'racecar' """ - return " ".join([word[::-1] for word in input_str.split()]) + return " ".join( + "".join(word[::-1]) if len(word) > length else word for word in sentence.split() + ) if __name__ == "__main__": import doctest doctest.testmod() + print(reverse_letters("Hey wollef sroirraw")) diff --git a/strings/reverse_long_words.py b/strings/reverse_long_words.py deleted file mode 100644 index 39ef11513..000000000 --- a/strings/reverse_long_words.py +++ /dev/null @@ -1,21 +0,0 @@ -def reverse_long_words(sentence: str) -> str: - """ - Reverse all words that are longer than 4 characters in a sentence. - - >>> reverse_long_words("Hey wollef sroirraw") - 'Hey fellow warriors' - >>> reverse_long_words("nohtyP is nohtyP") - 'Python is Python' - >>> reverse_long_words("1 12 123 1234 54321 654321") - '1 12 123 1234 12345 123456' - """ - return " ".join( - "".join(word[::-1]) if len(word) > 4 else word for word in sentence.split() - ) - - -if __name__ == "__main__": - import doctest - - doctest.testmod() - print(reverse_long_words("Hey wollef sroirraw")) From a2b695dabd6e9c6dd82784bd534c2e7570939be2 Mon Sep 17 00:00:00 2001 From: Megan Payne Date: Sun, 8 Oct 2023 23:33:50 +0200 Subject: [PATCH 14/22] Added Germain primes algorithm to the maths folder (#10120) * Added algorithm for Germain Primes to maths folder * Fixed test errors Germain primes. * Formatting Germain primes after pre-commit * Fixed path to maths * Update maths/germain_primes.py Co-authored-by: Tianyi Zheng * Update maths/germain_primes.py Co-authored-by: Tianyi Zheng * Added function for safe primes * Update maths/germain_primes.py Co-authored-by: Tianyi Zheng * Apply suggestions from code review --------- Co-authored-by: Megan Payne Co-authored-by: Tianyi Zheng --- maths/germain_primes.py | 72 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 maths/germain_primes.py diff --git a/maths/germain_primes.py b/maths/germain_primes.py new file mode 100644 index 000000000..078d1967f --- /dev/null +++ b/maths/germain_primes.py @@ -0,0 +1,72 @@ +""" +A Sophie Germain prime is any prime p, where 2p + 1 is also prime. +The second number, 2p + 1 is called a safe prime. + +Examples of Germain primes include: 2, 3, 5, 11, 23 + +Their corresponding safe primes: 5, 7, 11, 23, 47 +https://en.wikipedia.org/wiki/Safe_and_Sophie_Germain_primes +""" + +from maths.prime_check import is_prime + + +def is_germain_prime(number: int) -> bool: + """Checks if input number and 2*number + 1 are prime. + + >>> is_germain_prime(3) + True + >>> is_germain_prime(11) + True + >>> is_germain_prime(4) + False + >>> is_germain_prime(23) + True + >>> is_germain_prime(13) + False + >>> is_germain_prime(20) + False + >>> is_germain_prime('abc') + Traceback (most recent call last): + ... + TypeError: Input value must be a positive integer. Input value: abc + """ + if not isinstance(number, int) or number < 1: + msg = f"Input value must be a positive integer. Input value: {number}" + raise TypeError(msg) + + return is_prime(number) and is_prime(2 * number + 1) + + +def is_safe_prime(number: int) -> bool: + """Checks if input number and (number - 1)/2 are prime. + The smallest safe prime is 5, with the Germain prime is 2. + + >>> is_safe_prime(5) + True + >>> is_safe_prime(11) + True + >>> is_safe_prime(1) + False + >>> is_safe_prime(2) + False + >>> is_safe_prime(3) + False + >>> is_safe_prime(47) + True + >>> is_safe_prime('abc') + Traceback (most recent call last): + ... + TypeError: Input value must be a positive integer. Input value: abc + """ + if not isinstance(number, int) or number < 1: + msg = f"Input value must be a positive integer. Input value: {number}" + raise TypeError(msg) + + return (number - 1) % 2 == 0 and is_prime(number) and is_prime((number - 1) // 2) + + +if __name__ == "__main__": + from doctest import testmod + + testmod() From 2260961a803ebd037f471ef18fa5032a547d42da Mon Sep 17 00:00:00 2001 From: Saahil Mahato <115351000+saahil-mahato@users.noreply.github.com> Date: Mon, 9 Oct 2023 05:04:28 +0545 Subject: [PATCH 15/22] Add Soboleva Modified Hyberbolic Tangent function (#10043) * Add Sobovela Modified Hyberbolic Tangent function * fix: typo * Update and rename sobovela_modified_hyperbolic_tangent.py to soboleva_modified_hyperbolic_tangent.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix: typo * Apply suggestions from code review --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Tianyi Zheng --- .../soboleva_modified_hyperbolic_tangent.py | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 neural_network/activation_functions/soboleva_modified_hyperbolic_tangent.py diff --git a/neural_network/activation_functions/soboleva_modified_hyperbolic_tangent.py b/neural_network/activation_functions/soboleva_modified_hyperbolic_tangent.py new file mode 100644 index 000000000..603ac0b7e --- /dev/null +++ b/neural_network/activation_functions/soboleva_modified_hyperbolic_tangent.py @@ -0,0 +1,49 @@ +""" +This script implements the Soboleva Modified Hyperbolic Tangent function. + +The function applies the Soboleva Modified Hyperbolic Tangent function +to each element of the vector. + +More details about the activation function can be found on: +https://en.wikipedia.org/wiki/Soboleva_modified_hyperbolic_tangent +""" + + +import numpy as np + + +def soboleva_modified_hyperbolic_tangent( + vector: np.ndarray, a_value: float, b_value: float, c_value: float, d_value: float +) -> np.ndarray: + """ + Implements the Soboleva Modified Hyperbolic Tangent function + + Parameters: + vector (ndarray): A vector that consists of numeric values + a_value (float): parameter a of the equation + b_value (float): parameter b of the equation + c_value (float): parameter c of the equation + d_value (float): parameter d of the equation + + Returns: + vector (ndarray): Input array after applying SMHT function + + >>> vector = np.array([5.4, -2.4, 6.3, -5.23, 3.27, 0.56]) + >>> soboleva_modified_hyperbolic_tangent(vector, 0.2, 0.4, 0.6, 0.8) + array([ 0.11075085, -0.28236685, 0.07861169, -0.1180085 , 0.22999056, + 0.1566043 ]) + """ + + # Separate the numerator and denominator for simplicity + # Calculate the numerator and denominator element-wise + numerator = np.exp(a_value * vector) - np.exp(-b_value * vector) + denominator = np.exp(c_value * vector) + np.exp(-d_value * vector) + + # Calculate and return the final result element-wise + return numerator / denominator + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From ed19b1cf0c3d8284027e17cc025d65b3f924acc0 Mon Sep 17 00:00:00 2001 From: Saahil Mahato <115351000+saahil-mahato@users.noreply.github.com> Date: Mon, 9 Oct 2023 05:19:50 +0545 Subject: [PATCH 16/22] Add binary step activation function (#10030) * Add binary step activation function * fix: ruff line too long error * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * refactor: add link to directory * revert: add link to directory * fix: algorithm bug and docs * Update neural_network/activation_functions/binary_step.py * fix: ruff line too long error --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Tianyi Zheng --- .../activation_functions/binary_step.py | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 neural_network/activation_functions/binary_step.py diff --git a/neural_network/activation_functions/binary_step.py b/neural_network/activation_functions/binary_step.py new file mode 100644 index 000000000..8f8f4d405 --- /dev/null +++ b/neural_network/activation_functions/binary_step.py @@ -0,0 +1,36 @@ +""" +This script demonstrates the implementation of the Binary Step function. + +It's an activation function in which the neuron is activated if the input is positive +or 0, else it is deactivated + +It's a simple activation function which is mentioned in this wikipedia article: +https://en.wikipedia.org/wiki/Activation_function +""" + + +import numpy as np + + +def binary_step(vector: np.ndarray) -> np.ndarray: + """ + Implements the binary step function + + Parameters: + vector (ndarray): A vector that consists of numeric values + + Returns: + vector (ndarray): Input vector after applying binary step function + + >>> vector = np.array([-1.2, 0, 2, 1.45, -3.7, 0.3]) + >>> binary_step(vector) + array([0, 1, 1, 1, 0, 1]) + """ + + return np.where(vector >= 0, 1, 0) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 12e8e9ca876ed3ae7f1effa1de407ca29a06cb36 Mon Sep 17 00:00:00 2001 From: Sai Harsha Kottapalli Date: Mon, 9 Oct 2023 17:36:16 +0530 Subject: [PATCH 17/22] Add DocTests to is_palindrome.py (#10081) * add doctest ut * test complete * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * format * ruff update * cover line 154 * Update data_structures/linked_list/is_palindrome.py Co-authored-by: Christian Clauss * use dataclass * pre-commit fix * Fix mypy errors * use future annotations --------- Co-authored-by: Harsha Kottapalli Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- data_structures/linked_list/is_palindrome.py | 172 +++++++++++++++---- 1 file changed, 140 insertions(+), 32 deletions(-) diff --git a/data_structures/linked_list/is_palindrome.py b/data_structures/linked_list/is_palindrome.py index d540fb69f..7d89f085c 100644 --- a/data_structures/linked_list/is_palindrome.py +++ b/data_structures/linked_list/is_palindrome.py @@ -1,65 +1,167 @@ -def is_palindrome(head): +from __future__ import annotations + +from dataclasses import dataclass + + +@dataclass +class ListNode: + val: int = 0 + next_node: ListNode | None = None + + +def is_palindrome(head: ListNode | None) -> bool: + """ + Check if a linked list is a palindrome. + + Args: + head: The head of the linked list. + + Returns: + bool: True if the linked list is a palindrome, False otherwise. + + Examples: + >>> is_palindrome(None) + True + + >>> is_palindrome(ListNode(1)) + True + + >>> is_palindrome(ListNode(1, ListNode(2))) + False + + >>> is_palindrome(ListNode(1, ListNode(2, ListNode(1)))) + True + + >>> is_palindrome(ListNode(1, ListNode(2, ListNode(2, ListNode(1))))) + True + """ if not head: return True # split the list to two parts - fast, slow = head.next, head - while fast and fast.next: - fast = fast.next.next - slow = slow.next - second = slow.next - slow.next = None # Don't forget here! But forget still works! + fast: ListNode | None = head.next_node + slow: ListNode | None = head + while fast and fast.next_node: + fast = fast.next_node.next_node + slow = slow.next_node if slow else None + if slow: + # slow will always be defined, + # adding this check to resolve mypy static check + second = slow.next_node + slow.next_node = None # Don't forget here! But forget still works! # reverse the second part - node = None + node: ListNode | None = None while second: - nxt = second.next - second.next = node + nxt = second.next_node + second.next_node = node node = second second = nxt # compare two parts # second part has the same or one less node - while node: + while node and head: if node.val != head.val: return False - node = node.next - head = head.next + node = node.next_node + head = head.next_node return True -def is_palindrome_stack(head): - if not head or not head.next: +def is_palindrome_stack(head: ListNode | None) -> bool: + """ + Check if a linked list is a palindrome using a stack. + + Args: + head (ListNode): The head of the linked list. + + Returns: + bool: True if the linked list is a palindrome, False otherwise. + + Examples: + >>> is_palindrome_stack(None) + True + + >>> is_palindrome_stack(ListNode(1)) + True + + >>> is_palindrome_stack(ListNode(1, ListNode(2))) + False + + >>> is_palindrome_stack(ListNode(1, ListNode(2, ListNode(1)))) + True + + >>> is_palindrome_stack(ListNode(1, ListNode(2, ListNode(2, ListNode(1))))) + True + """ + if not head or not head.next_node: return True # 1. Get the midpoint (slow) - slow = fast = cur = head - while fast and fast.next: - fast, slow = fast.next.next, slow.next + slow: ListNode | None = head + fast: ListNode | None = head + while fast and fast.next_node: + fast = fast.next_node.next_node + slow = slow.next_node if slow else None - # 2. Push the second half into the stack - stack = [slow.val] - while slow.next: - slow = slow.next - stack.append(slow.val) + # slow will always be defined, + # adding this check to resolve mypy static check + if slow: + stack = [slow.val] - # 3. Comparison - while stack: - if stack.pop() != cur.val: - return False - cur = cur.next + # 2. Push the second half into the stack + while slow.next_node: + slow = slow.next_node + stack.append(slow.val) + + # 3. Comparison + cur: ListNode | None = head + while stack and cur: + if stack.pop() != cur.val: + return False + cur = cur.next_node return True -def is_palindrome_dict(head): - if not head or not head.next: +def is_palindrome_dict(head: ListNode | None) -> bool: + """ + Check if a linked list is a palindrome using a dictionary. + + Args: + head (ListNode): The head of the linked list. + + Returns: + bool: True if the linked list is a palindrome, False otherwise. + + Examples: + >>> is_palindrome_dict(None) + True + + >>> is_palindrome_dict(ListNode(1)) + True + + >>> is_palindrome_dict(ListNode(1, ListNode(2))) + False + + >>> is_palindrome_dict(ListNode(1, ListNode(2, ListNode(1)))) + True + + >>> is_palindrome_dict(ListNode(1, ListNode(2, ListNode(2, ListNode(1))))) + True + + >>> is_palindrome_dict(\ + ListNode(\ + 1, ListNode(2, ListNode(1, ListNode(3, ListNode(2, ListNode(1))))))) + False + """ + if not head or not head.next_node: return True - d = {} + d: dict[int, list[int]] = {} pos = 0 while head: if head.val in d: d[head.val].append(pos) else: d[head.val] = [pos] - head = head.next + head = head.next_node pos += 1 checksum = pos - 1 middle = 0 @@ -75,3 +177,9 @@ def is_palindrome_dict(head): if middle > 1: return False return True + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 876087be998d5b366d68cbb9394b6b92b7f619f6 Mon Sep 17 00:00:00 2001 From: Sai Harsha Kottapalli Date: Mon, 9 Oct 2023 17:46:43 +0530 Subject: [PATCH 18/22] Add DocTests to magicdiamondpattern.py (#10135) * magicdiamondpattern doctest * remove start part --------- Co-authored-by: Harsha Kottapalli --- other/magicdiamondpattern.py | 74 ++++++++++++++++++++++++------------ 1 file changed, 49 insertions(+), 25 deletions(-) diff --git a/other/magicdiamondpattern.py b/other/magicdiamondpattern.py index 89b973bb4..58889280a 100644 --- a/other/magicdiamondpattern.py +++ b/other/magicdiamondpattern.py @@ -4,52 +4,76 @@ # Function to print upper half of diamond (pyramid) def floyd(n): """ - Parameters: - n : size of pattern + Print the upper half of a diamond pattern with '*' characters. + + Args: + n (int): Size of the pattern. + + Examples: + >>> floyd(3) + ' * \\n * * \\n* * * \\n' + + >>> floyd(5) + ' * \\n * * \\n * * * \\n * * * * \\n* * * * * \\n' """ + result = "" for i in range(n): for _ in range(n - i - 1): # printing spaces - print(" ", end="") + result += " " for _ in range(i + 1): # printing stars - print("* ", end="") - print() + result += "* " + result += "\n" + return result # Function to print lower half of diamond (pyramid) def reverse_floyd(n): """ - Parameters: - n : size of pattern + Print the lower half of a diamond pattern with '*' characters. + + Args: + n (int): Size of the pattern. + + Examples: + >>> reverse_floyd(3) + '* * * \\n * * \\n * \\n ' + + >>> reverse_floyd(5) + '* * * * * \\n * * * * \\n * * * \\n * * \\n * \\n ' """ + result = "" for i in range(n, 0, -1): for _ in range(i, 0, -1): # printing stars - print("* ", end="") - print() + result += "* " + result += "\n" for _ in range(n - i + 1, 0, -1): # printing spaces - print(" ", end="") + result += " " + return result # Function to print complete diamond pattern of "*" def pretty_print(n): """ - Parameters: - n : size of pattern + Print a complete diamond pattern with '*' characters. + + Args: + n (int): Size of the pattern. + + Examples: + >>> pretty_print(0) + ' ... .... nothing printing :(' + + >>> pretty_print(3) + ' * \\n * * \\n* * * \\n* * * \\n * * \\n * \\n ' """ if n <= 0: - print(" ... .... nothing printing :(") - return - floyd(n) # upper half - reverse_floyd(n) # lower half + return " ... .... nothing printing :(" + upper_half = floyd(n) # upper half + lower_half = reverse_floyd(n) # lower half + return upper_half + lower_half if __name__ == "__main__": - print(r"| /\ | |- | |- |--| |\ /| |-") - print(r"|/ \| |- |_ |_ |__| | \/ | |_") - K = 1 - while K: - user_number = int(input("enter the number and , and see the magic : ")) - print() - pretty_print(user_number) - K = int(input("press 0 to exit... and 1 to continue...")) + import doctest - print("Good Bye...") + doctest.testmod() From 583a614fefaa9c932e6d650abfea2eaa75a93b05 Mon Sep 17 00:00:00 2001 From: Siddik Patel <70135775+Siddikpatel@users.noreply.github.com> Date: Mon, 9 Oct 2023 17:49:12 +0530 Subject: [PATCH 19/22] Removed redundant greatest_common_divisor code (#9358) * Deleted greatest_common_divisor def from many files and instead imported the method from Maths folder * Deleted greatest_common_divisor def from many files and instead imported the method from Maths folder, also fixed comments * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Deleted greatest_common_divisor def from many files and instead imported the method from Maths folder, also fixed comments * Imports organized * recursive gcd function implementation rolledback * more gcd duplicates removed * more gcd duplicates removed * Update maths/carmichael_number.py * updated files * moved a file to another location --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Tianyi Zheng --- blockchain/diophantine_equation.py | 32 +++--------------------- ciphers/affine_cipher.py | 6 +++-- ciphers/cryptomath_module.py | 7 ++---- ciphers/hill_cipher.py | 14 +---------- ciphers/rsa_key_generator.py | 4 ++- maths/carmichael_number.py | 11 ++------ maths/least_common_multiple.py | 22 ++-------------- maths/primelib.py | 40 +++--------------------------- project_euler/problem_005/sol2.py | 19 ++------------ 9 files changed, 24 insertions(+), 131 deletions(-) diff --git a/blockchain/diophantine_equation.py b/blockchain/diophantine_equation.py index 22b0cad75..7110d9023 100644 --- a/blockchain/diophantine_equation.py +++ b/blockchain/diophantine_equation.py @@ -1,11 +1,13 @@ from __future__ import annotations +from maths.greatest_common_divisor import greatest_common_divisor + def diophantine(a: int, b: int, c: int) -> tuple[float, float]: """ Diophantine Equation : Given integers a,b,c ( at least one of a and b != 0), the diophantine equation a*x + b*y = c has a solution (where x and y are integers) - iff gcd(a,b) divides c. + iff greatest_common_divisor(a,b) divides c. GCD ( Greatest Common Divisor ) or HCF ( Highest Common Factor ) @@ -22,7 +24,7 @@ def diophantine(a: int, b: int, c: int) -> tuple[float, float]: assert ( c % greatest_common_divisor(a, b) == 0 - ) # greatest_common_divisor(a,b) function implemented below + ) # greatest_common_divisor(a,b) is in maths directory (d, x, y) = extended_gcd(a, b) # extended_gcd(a,b) function implemented below r = c / d return (r * x, r * y) @@ -69,32 +71,6 @@ def diophantine_all_soln(a: int, b: int, c: int, n: int = 2) -> None: print(x, y) -def greatest_common_divisor(a: int, b: int) -> int: - """ - Euclid's Lemma : d divides a and b, if and only if d divides a-b and b - - Euclid's Algorithm - - >>> greatest_common_divisor(7,5) - 1 - - Note : In number theory, two integers a and b are said to be relatively prime, - mutually prime, or co-prime if the only positive integer (factor) that - divides both of them is 1 i.e., gcd(a,b) = 1. - - >>> greatest_common_divisor(121, 11) - 11 - - """ - if a < b: - a, b = b, a - - while a % b != 0: - a, b = b, a % b - - return b - - def extended_gcd(a: int, b: int) -> tuple[int, int, int]: """ Extended Euclid's Algorithm : If d divides a and b and d = a*x + b*y for integers diff --git a/ciphers/affine_cipher.py b/ciphers/affine_cipher.py index cd1e33b88..10d16367c 100644 --- a/ciphers/affine_cipher.py +++ b/ciphers/affine_cipher.py @@ -1,6 +1,8 @@ import random import sys +from maths.greatest_common_divisor import gcd_by_iterative + from . import cryptomath_module as cryptomath SYMBOLS = ( @@ -26,7 +28,7 @@ def check_keys(key_a: int, key_b: int, mode: str) -> None: "Key A must be greater than 0 and key B must " f"be between 0 and {len(SYMBOLS) - 1}." ) - if cryptomath.gcd(key_a, len(SYMBOLS)) != 1: + if gcd_by_iterative(key_a, len(SYMBOLS)) != 1: sys.exit( f"Key A {key_a} and the symbol set size {len(SYMBOLS)} " "are not relatively prime. Choose a different key." @@ -76,7 +78,7 @@ def get_random_key() -> int: while True: key_b = random.randint(2, len(SYMBOLS)) key_b = random.randint(2, len(SYMBOLS)) - if cryptomath.gcd(key_b, len(SYMBOLS)) == 1 and key_b % len(SYMBOLS) != 0: + if gcd_by_iterative(key_b, len(SYMBOLS)) == 1 and key_b % len(SYMBOLS) != 0: return key_b * len(SYMBOLS) + key_b diff --git a/ciphers/cryptomath_module.py b/ciphers/cryptomath_module.py index 6f15f7b73..02e94e4b9 100644 --- a/ciphers/cryptomath_module.py +++ b/ciphers/cryptomath_module.py @@ -1,11 +1,8 @@ -def gcd(a: int, b: int) -> int: - while a != 0: - a, b = b % a, a - return b +from maths.greatest_common_divisor import gcd_by_iterative def find_mod_inverse(a: int, m: int) -> int: - if gcd(a, m) != 1: + if gcd_by_iterative(a, m) != 1: msg = f"mod inverse of {a!r} and {m!r} does not exist" raise ValueError(msg) u1, u2, u3 = 1, 0, a diff --git a/ciphers/hill_cipher.py b/ciphers/hill_cipher.py index b4424e822..1201fda90 100644 --- a/ciphers/hill_cipher.py +++ b/ciphers/hill_cipher.py @@ -39,19 +39,7 @@ import string import numpy - -def greatest_common_divisor(a: int, b: int) -> int: - """ - >>> greatest_common_divisor(4, 8) - 4 - >>> greatest_common_divisor(8, 4) - 4 - >>> greatest_common_divisor(4, 7) - 1 - >>> greatest_common_divisor(0, 10) - 10 - """ - return b if a == 0 else greatest_common_divisor(b % a, a) +from maths.greatest_common_divisor import greatest_common_divisor class HillCipher: diff --git a/ciphers/rsa_key_generator.py b/ciphers/rsa_key_generator.py index eedc73368..44970e8cb 100644 --- a/ciphers/rsa_key_generator.py +++ b/ciphers/rsa_key_generator.py @@ -2,6 +2,8 @@ import os import random import sys +from maths.greatest_common_divisor import gcd_by_iterative + from . import cryptomath_module, rabin_miller @@ -27,7 +29,7 @@ def generate_key(key_size: int) -> tuple[tuple[int, int], tuple[int, int]]: # Generate e that is relatively prime to (p - 1) * (q - 1) while True: e = random.randrange(2 ** (key_size - 1), 2 ** (key_size)) - if cryptomath_module.gcd(e, (p - 1) * (q - 1)) == 1: + if gcd_by_iterative(e, (p - 1) * (q - 1)) == 1: break # Calculate d that is mod inverse of e diff --git a/maths/carmichael_number.py b/maths/carmichael_number.py index c9c144759..81712520f 100644 --- a/maths/carmichael_number.py +++ b/maths/carmichael_number.py @@ -10,14 +10,7 @@ satisfies the following modular arithmetic condition: Examples of Carmichael Numbers: 561, 1105, ... https://en.wikipedia.org/wiki/Carmichael_number """ - - -def gcd(a: int, b: int) -> int: - if a < b: - return gcd(b, a) - if a % b == 0: - return b - return gcd(b, a % b) +from maths.greatest_common_divisor import greatest_common_divisor def power(x: int, y: int, mod: int) -> int: @@ -33,7 +26,7 @@ def power(x: int, y: int, mod: int) -> int: def is_carmichael_number(n: int) -> bool: b = 2 while b < n: - if gcd(b, n) == 1 and power(b, n - 1, n) != 1: + if greatest_common_divisor(b, n) == 1 and power(b, n - 1, n) != 1: return False b += 1 return True diff --git a/maths/least_common_multiple.py b/maths/least_common_multiple.py index 10cc63ac7..4f28da8ab 100644 --- a/maths/least_common_multiple.py +++ b/maths/least_common_multiple.py @@ -1,6 +1,8 @@ import unittest from timeit import timeit +from maths.greatest_common_divisor import greatest_common_divisor + def least_common_multiple_slow(first_num: int, second_num: int) -> int: """ @@ -20,26 +22,6 @@ def least_common_multiple_slow(first_num: int, second_num: int) -> int: return common_mult -def greatest_common_divisor(a: int, b: int) -> int: - """ - Calculate Greatest Common Divisor (GCD). - see greatest_common_divisor.py - >>> greatest_common_divisor(24, 40) - 8 - >>> greatest_common_divisor(1, 1) - 1 - >>> greatest_common_divisor(1, 800) - 1 - >>> greatest_common_divisor(11, 37) - 1 - >>> greatest_common_divisor(3, 5) - 1 - >>> greatest_common_divisor(16, 4) - 4 - """ - return b if a == 0 else greatest_common_divisor(b % a, a) - - def least_common_multiple_fast(first_num: int, second_num: int) -> int: """ Find the least common multiple of two numbers. diff --git a/maths/primelib.py b/maths/primelib.py index 28b5aee9d..cf01750cf 100644 --- a/maths/primelib.py +++ b/maths/primelib.py @@ -21,7 +21,6 @@ get_primes_between(pNumber1, pNumber2) is_even(number) is_odd(number) -gcd(number1, number2) // greatest common divisor kg_v(number1, number2) // least common multiple get_divisors(number) // all divisors of 'number' inclusive 1, number is_perfect_number(number) @@ -40,6 +39,8 @@ goldbach(number) // Goldbach's assumption from math import sqrt +from maths.greatest_common_divisor import gcd_by_iterative + def is_prime(number: int) -> bool: """ @@ -317,39 +318,6 @@ def goldbach(number): # ---------------------------------------------- -def gcd(number1, number2): - """ - Greatest common divisor - input: two positive integer 'number1' and 'number2' - returns the greatest common divisor of 'number1' and 'number2' - """ - - # precondition - assert ( - isinstance(number1, int) - and isinstance(number2, int) - and (number1 >= 0) - and (number2 >= 0) - ), "'number1' and 'number2' must been positive integer." - - rest = 0 - - while number2 != 0: - rest = number1 % number2 - number1 = number2 - number2 = rest - - # precondition - assert isinstance(number1, int) and ( - number1 >= 0 - ), "'number' must been from type int and positive" - - return number1 - - -# ---------------------------------------------------- - - def kg_v(number1, number2): """ Least common multiple @@ -567,14 +535,14 @@ def simplify_fraction(numerator, denominator): ), "The arguments must been from type int and 'denominator' != 0" # build the greatest common divisor of numerator and denominator. - gcd_of_fraction = gcd(abs(numerator), abs(denominator)) + gcd_of_fraction = gcd_by_iterative(abs(numerator), abs(denominator)) # precondition assert ( isinstance(gcd_of_fraction, int) and (numerator % gcd_of_fraction == 0) and (denominator % gcd_of_fraction == 0) - ), "Error in function gcd(...,...)" + ), "Error in function gcd_by_iterative(...,...)" return (numerator // gcd_of_fraction, denominator // gcd_of_fraction) diff --git a/project_euler/problem_005/sol2.py b/project_euler/problem_005/sol2.py index 1b3e5e130..4558e21fd 100644 --- a/project_euler/problem_005/sol2.py +++ b/project_euler/problem_005/sol2.py @@ -1,3 +1,5 @@ +from maths.greatest_common_divisor import greatest_common_divisor + """ Project Euler Problem 5: https://projecteuler.net/problem=5 @@ -16,23 +18,6 @@ References: """ -def greatest_common_divisor(x: int, y: int) -> int: - """ - Euclidean Greatest Common Divisor algorithm - - >>> greatest_common_divisor(0, 0) - 0 - >>> greatest_common_divisor(23, 42) - 1 - >>> greatest_common_divisor(15, 33) - 3 - >>> greatest_common_divisor(12345, 67890) - 15 - """ - - return x if y == 0 else greatest_common_divisor(y, x % y) - - def lcm(x: int, y: int) -> int: """ Least Common Multiple. From 53d78b9cc09021c8f65fae41f8b345304a88aedd Mon Sep 17 00:00:00 2001 From: Kausthub Kannan Date: Mon, 9 Oct 2023 20:03:47 +0530 Subject: [PATCH 20/22] Added Huber Loss Function (#10141) --- machine_learning/loss_functions/huber_loss.py | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 machine_learning/loss_functions/huber_loss.py diff --git a/machine_learning/loss_functions/huber_loss.py b/machine_learning/loss_functions/huber_loss.py new file mode 100644 index 000000000..202e013f2 --- /dev/null +++ b/machine_learning/loss_functions/huber_loss.py @@ -0,0 +1,52 @@ +""" +Huber Loss Function + +Description: +Huber loss function describes the penalty incurred by an estimation procedure. +It serves as a measure of the model's accuracy in regression tasks. + +Formula: +Huber Loss = if |y_true - y_pred| <= delta then 0.5 * (y_true - y_pred)^2 + else delta * |y_true - y_pred| - 0.5 * delta^2 + +Source: +[Wikipedia - Huber Loss](https://en.wikipedia.org/wiki/Huber_loss) +""" + +import numpy as np + + +def huber_loss(y_true: np.ndarray, y_pred: np.ndarray, delta: float) -> float: + """ + Calculate the mean of Huber Loss. + + Parameters: + - y_true: The true values (ground truth). + - y_pred: The predicted values. + + Returns: + - huber_loss: The mean of Huber Loss between y_true and y_pred. + + Example usage: + >>> true_values = np.array([0.9, 10.0, 2.0, 1.0, 5.2]) + >>> predicted_values = np.array([0.8, 2.1, 2.9, 4.2, 5.2]) + >>> np.isclose(huber_loss(true_values, predicted_values, 1.0), 2.102) + True + >>> true_labels = np.array([11.0, 21.0, 3.32, 4.0, 5.0]) + >>> predicted_probs = np.array([8.3, 20.8, 2.9, 11.2, 5.0]) + >>> np.isclose(huber_loss(true_labels, predicted_probs, 1.0), 1.80164) + True + """ + + if len(y_true) != len(y_pred): + raise ValueError("Input arrays must have the same length.") + + huber_mse = 0.5 * (y_true - y_pred) ** 2 + huber_mae = delta * (np.abs(y_true - y_pred) - 0.5 * delta) + return np.where(np.abs(y_true - y_pred) <= delta, huber_mse, huber_mae).mean() + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From c0da015b7d49f9f6e99fffd279f65c5605a0ebe1 Mon Sep 17 00:00:00 2001 From: Sai Harsha Kottapalli Date: Mon, 9 Oct 2023 20:49:05 +0530 Subject: [PATCH 21/22] Add DocTests to diffie.py (#10156) * diffie doctest * fix ut * update doctest --------- Co-authored-by: Harsha Kottapalli --- ciphers/diffie.py | 45 +++++++++++++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/ciphers/diffie.py b/ciphers/diffie.py index 4ff90be00..1e1e86899 100644 --- a/ciphers/diffie.py +++ b/ciphers/diffie.py @@ -1,11 +1,28 @@ from __future__ import annotations -def find_primitive(n: int) -> int | None: - for r in range(1, n): +def find_primitive(modulus: int) -> int | None: + """ + Find a primitive root modulo modulus, if one exists. + + Args: + modulus : The modulus for which to find a primitive root. + + Returns: + The primitive root if one exists, or None if there is none. + + Examples: + >>> find_primitive(7) # Modulo 7 has primitive root 3 + 3 + >>> find_primitive(11) # Modulo 11 has primitive root 2 + 2 + >>> find_primitive(8) == None # Modulo 8 has no primitive root + True + """ + for r in range(1, modulus): li = [] - for x in range(n - 1): - val = pow(r, x, n) + for x in range(modulus - 1): + val = pow(r, x, modulus) if val in li: break li.append(val) @@ -15,18 +32,22 @@ def find_primitive(n: int) -> int | None: if __name__ == "__main__": - q = int(input("Enter a prime number q: ")) - a = find_primitive(q) - if a is None: - print(f"Cannot find the primitive for the value: {a!r}") + import doctest + + doctest.testmod() + + prime = int(input("Enter a prime number q: ")) + primitive_root = find_primitive(prime) + if primitive_root is None: + print(f"Cannot find the primitive for the value: {primitive_root!r}") else: a_private = int(input("Enter private key of A: ")) - a_public = pow(a, a_private, q) + a_public = pow(primitive_root, a_private, prime) b_private = int(input("Enter private key of B: ")) - b_public = pow(a, b_private, q) + b_public = pow(primitive_root, b_private, prime) - a_secret = pow(b_public, a_private, q) - b_secret = pow(a_public, b_private, q) + a_secret = pow(b_public, a_private, prime) + b_secret = pow(a_public, b_private, prime) print("The key value generated by A is: ", a_secret) print("The key value generated by B is: ", b_secret) From ba828fe621d1f5623fffcf0014b243da3a6122fc Mon Sep 17 00:00:00 2001 From: Kamil <32775019+quant12345@users.noreply.github.com> Date: Mon, 9 Oct 2023 20:46:38 +0500 Subject: [PATCH 22/22] test_digital_image_processing -> test_local_binary_pattern replacing a large image with a smaller one (#10161) * Replacing the generator with numpy vector operations from lu_decomposition. * Revert "Replacing the generator with numpy vector operations from lu_decomposition." This reverts commit ad217c66165898d62b76cc89ba09c2d7049b6448. * Replaced lena.jpg with lena_small.jpg to make tests faster. * Update digital_image_processing/test_digital_image_processing.py Co-authored-by: Christian Clauss * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update test_digital_image_processing.py tests fail, I'll try an empty commit * Apply suggestions from code review * Update test_digital_image_processing.py added clarifications * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update test_digital_image_processing.py --------- Co-authored-by: Christian Clauss Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Tianyi Zheng --- .../test_digital_image_processing.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/digital_image_processing/test_digital_image_processing.py b/digital_image_processing/test_digital_image_processing.py index 528b4bc3b..2e5630458 100644 --- a/digital_image_processing/test_digital_image_processing.py +++ b/digital_image_processing/test_digital_image_processing.py @@ -96,9 +96,16 @@ def test_nearest_neighbour( def test_local_binary_pattern(): - file_path = "digital_image_processing/image_data/lena.jpg" + # pull request 10161 before: + # "digital_image_processing/image_data/lena.jpg" + # after: "digital_image_processing/image_data/lena_small.jpg" - # Reading the image and converting it to grayscale. + from os import getenv # Speed up our Continuous Integration tests + + file_name = "lena_small.jpg" if getenv("CI") else "lena.jpg" + file_path = f"digital_image_processing/image_data/{file_name}" + + # Reading the image and converting it to grayscale image = imread(file_path, 0) # Test for get_neighbors_pixel function() return not None