From dda8aabd466b1b4e68ad6c50ad29f379b92756f3 Mon Sep 17 00:00:00 2001 From: Achintya Sharma Date: Sun, 6 Jul 2025 21:31:57 +0530 Subject: [PATCH 1/4] Improved Test Coverage for dynamic_programming/fibonacci.py --- dynamic_programming/fibonacci.py | 236 +++++++++++++++++++++++++++---- 1 file changed, 209 insertions(+), 27 deletions(-) diff --git a/dynamic_programming/fibonacci.py b/dynamic_programming/fibonacci.py index c102493aa00b..5331bdf7a773 100644 --- a/dynamic_programming/fibonacci.py +++ b/dynamic_programming/fibonacci.py @@ -1,51 +1,233 @@ """ -This is a pure Python implementation of Dynamic Programming solution to the fibonacci +This is a pure Python implementation of Dynamic Programming solution to the Fibonacci sequence problem. + +The Fibonacci sequence is a series of numbers where each number is the sum of the +two preceding ones, usually starting with 0 and 1. The sequence begins: +0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, ... + +Reference: https://en.wikipedia.org/wiki/Fibonacci_number + +Time Complexity: O(n) for first calculation, O(1) for subsequent calls with memoization +Space Complexity: O(n) for storing the sequence """ class Fibonacci: + """ + Dynamic Programming implementation of Fibonacci sequence generator. + + This class maintains a memoized sequence of Fibonacci numbers and can efficiently + generate new numbers by building on previously calculated values. + + Attributes: + sequence (list[int]): Memoized Fibonacci sequence starting with [0, 1] + """ + def __init__(self) -> None: + """Initialize the Fibonacci sequence with the first two numbers.""" self.sequence = [0, 1] - - def get(self, index: int) -> list: + + def get(self, index: int) -> list[int]: """ - Get the Fibonacci number of `index`. If the number does not exist, - calculate all missing numbers leading up to the number of `index`. - - >>> Fibonacci().get(10) - [0, 1, 1, 2, 3, 5, 8, 13, 21, 34] - >>> Fibonacci().get(5) - [0, 1, 1, 2, 3] + Get the first `index` Fibonacci numbers. If numbers don't exist in the sequence, + calculate all missing numbers leading up to the required index. + + Args: + index (int): Number of Fibonacci numbers to return (must be non-negative) + + Returns: + list[int]: List containing the first `index` Fibonacci numbers + + Raises: + ValueError: If index is negative + + Examples: + >>> fib = Fibonacci() + >>> fib.get(0) + [] + >>> fib.get(1) + [0] + >>> fib.get(2) + [0, 1] + >>> fib.get(5) + [0, 1, 1, 2, 3] + >>> fib.get(10) + [0, 1, 1, 2, 3, 5, 8, 13, 21, 34] + >>> fib.get(1) # Test memoization - should not recalculate + [0] + >>> fib.get(15) # Test extending existing sequence + [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377] + + # Test edge cases + >>> fib_new = Fibonacci() + >>> fib_new.get(0) + [] + >>> fib_new.get(1) + [0] + >>> fib_new.get(2) + [0, 1] + + # Test error handling + >>> fib_error = Fibonacci() + >>> fib_error.get(-1) + Traceback (most recent call last): + ... + ValueError: Index must be non-negative, got -1 + >>> fib_error.get(-5) + Traceback (most recent call last): + ... + ValueError: Index must be non-negative, got -5 + + # Test large numbers + >>> fib_large = Fibonacci() + >>> result = fib_large.get(20) + >>> len(result) + 20 + >>> result[-1] # 20th Fibonacci number (0-indexed, so 19th position) + 4181 + >>> result[0], result[1], result[2] + (0, 1, 1) + + # Test sequence correctness + >>> fib_test = Fibonacci() + >>> seq = fib_test.get(8) + >>> all(seq[i] == seq[i-1] + seq[i-2] for i in range(2, len(seq))) + True """ - if (difference := index - (len(self.sequence) - 2)) >= 1: + if index < 0: + raise ValueError(f"Index must be non-negative, got {index}") + + if index == 0: + return [] + + # Calculate missing numbers if needed + if (difference := index - len(self.sequence)) > 0: for _ in range(difference): self.sequence.append(self.sequence[-1] + self.sequence[-2]) + return self.sequence[:index] + + def get_nth(self, n: int) -> int: + """ + Get the nth Fibonacci number (0-indexed). + + Args: + n (int): The index of the Fibonacci number to retrieve + + Returns: + int: The nth Fibonacci number + + Raises: + ValueError: If n is negative + + Examples: + >>> fib = Fibonacci() + >>> fib.get_nth(0) + 0 + >>> fib.get_nth(1) + 1 + >>> fib.get_nth(2) + 1 + >>> fib.get_nth(5) + 5 + >>> fib.get_nth(10) + 55 + >>> fib.get_nth(-1) + Traceback (most recent call last): + ... + ValueError: Index must be non-negative, got -1 + """ + if n < 0: + raise ValueError(f"Index must be non-negative, got {n}") + + # Extend sequence if needed + if n >= len(self.sequence): + difference = n - len(self.sequence) + 1 + for _ in range(difference): + self.sequence.append(self.sequence[-1] + self.sequence[-2]) + + return self.sequence[n] + + def reset(self) -> None: + """ + Reset the sequence to its initial state. + + Examples: + >>> fib = Fibonacci() + >>> fib.get(10) + [0, 1, 1, 2, 3, 5, 8, 13, 21, 34] + >>> len(fib.sequence) + 10 + >>> fib.reset() + >>> fib.sequence + [0, 1] + """ + self.sequence = [0, 1] def main() -> None: + """ + Interactive CLI for generating Fibonacci numbers. + + Allows users to input indices and get the corresponding Fibonacci sequence. + Supports commands: 'exit', 'quit', 'reset', 'help' + """ print( - "Fibonacci Series Using Dynamic Programming\n", - "Enter the index of the Fibonacci number you want to calculate ", - "in the prompt below. (To exit enter exit or Ctrl-C)\n", - sep="", + "Fibonacci Series Using Dynamic Programming\n" + "Enter the index of the Fibonacci number you want to calculate\n" + "Commands: 'exit'/'quit' to exit, 'reset' to clear cache, 'help' for help\n" ) + fibonacci = Fibonacci() - + while True: - prompt: str = input(">> ") - if prompt in {"exit", "quit"}: - break - try: - index: int = int(prompt) - except ValueError: - print("Enter a number or 'exit'") - continue - - print(fibonacci.get(index)) + prompt: str = input(">> ").strip().lower() + + if prompt in {"exit", "quit"}: + print("Goodbye!") + break + elif prompt == "reset": + fibonacci.reset() + print("Fibonacci sequence cache cleared.") + continue + elif prompt == "help": + print( + "Commands:\n" + " - Get first N Fibonacci numbers\n" + " reset - Clear the memoized sequence\n" + " help - Show this help message\n" + " exit/quit - Exit the program\n" + ) + continue + elif prompt == "": + continue + + try: + index: int = int(prompt) + if index < 0: + print("Please enter a non-negative number.") + continue + + result = fibonacci.get(index) + if not result: + print("[]") + else: + print(f"First {index} Fibonacci numbers: {result}") + if index > 0: + print(f"The {index}th Fibonacci number is: {result[-1]}") + + except ValueError: + print("Invalid input. Enter a number or use 'help' for commands.") + + except KeyboardInterrupt: + print("\nGoodbye!") + break + except EOFError: + print("\nGoodbye!") + break if __name__ == "__main__": - main() + main() \ No newline at end of file From 74826f001d366a419cacb12763ba05447cb555da Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 6 Jul 2025 16:04:37 +0000 Subject: [PATCH 2/4] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- dynamic_programming/fibonacci.py | 66 ++++++++++++++++---------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/dynamic_programming/fibonacci.py b/dynamic_programming/fibonacci.py index 5331bdf7a773..7cc3238521d8 100644 --- a/dynamic_programming/fibonacci.py +++ b/dynamic_programming/fibonacci.py @@ -16,32 +16,32 @@ class Fibonacci: """ Dynamic Programming implementation of Fibonacci sequence generator. - + This class maintains a memoized sequence of Fibonacci numbers and can efficiently generate new numbers by building on previously calculated values. - + Attributes: sequence (list[int]): Memoized Fibonacci sequence starting with [0, 1] """ - + def __init__(self) -> None: """Initialize the Fibonacci sequence with the first two numbers.""" self.sequence = [0, 1] - + def get(self, index: int) -> list[int]: """ Get the first `index` Fibonacci numbers. If numbers don't exist in the sequence, calculate all missing numbers leading up to the required index. - + Args: index (int): Number of Fibonacci numbers to return (must be non-negative) - + Returns: list[int]: List containing the first `index` Fibonacci numbers - + Raises: ValueError: If index is negative - + Examples: >>> fib = Fibonacci() >>> fib.get(0) @@ -58,7 +58,7 @@ def get(self, index: int) -> list[int]: [0] >>> fib.get(15) # Test extending existing sequence [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377] - + # Test edge cases >>> fib_new = Fibonacci() >>> fib_new.get(0) @@ -67,7 +67,7 @@ def get(self, index: int) -> list[int]: [0] >>> fib_new.get(2) [0, 1] - + # Test error handling >>> fib_error = Fibonacci() >>> fib_error.get(-1) @@ -78,7 +78,7 @@ def get(self, index: int) -> list[int]: Traceback (most recent call last): ... ValueError: Index must be non-negative, got -5 - + # Test large numbers >>> fib_large = Fibonacci() >>> result = fib_large.get(20) @@ -88,7 +88,7 @@ def get(self, index: int) -> list[int]: 4181 >>> result[0], result[1], result[2] (0, 1, 1) - + # Test sequence correctness >>> fib_test = Fibonacci() >>> seq = fib_test.get(8) @@ -97,30 +97,30 @@ def get(self, index: int) -> list[int]: """ if index < 0: raise ValueError(f"Index must be non-negative, got {index}") - + if index == 0: return [] - + # Calculate missing numbers if needed if (difference := index - len(self.sequence)) > 0: for _ in range(difference): self.sequence.append(self.sequence[-1] + self.sequence[-2]) - + return self.sequence[:index] - + def get_nth(self, n: int) -> int: """ Get the nth Fibonacci number (0-indexed). - + Args: n (int): The index of the Fibonacci number to retrieve - + Returns: int: The nth Fibonacci number - + Raises: ValueError: If n is negative - + Examples: >>> fib = Fibonacci() >>> fib.get_nth(0) @@ -140,19 +140,19 @@ def get_nth(self, n: int) -> int: """ if n < 0: raise ValueError(f"Index must be non-negative, got {n}") - + # Extend sequence if needed if n >= len(self.sequence): difference = n - len(self.sequence) + 1 for _ in range(difference): self.sequence.append(self.sequence[-1] + self.sequence[-2]) - + return self.sequence[n] - + def reset(self) -> None: """ Reset the sequence to its initial state. - + Examples: >>> fib = Fibonacci() >>> fib.get(10) @@ -169,7 +169,7 @@ def reset(self) -> None: def main() -> None: """ Interactive CLI for generating Fibonacci numbers. - + Allows users to input indices and get the corresponding Fibonacci sequence. Supports commands: 'exit', 'quit', 'reset', 'help' """ @@ -178,13 +178,13 @@ def main() -> None: "Enter the index of the Fibonacci number you want to calculate\n" "Commands: 'exit'/'quit' to exit, 'reset' to clear cache, 'help' for help\n" ) - + fibonacci = Fibonacci() - + while True: try: prompt: str = input(">> ").strip().lower() - + if prompt in {"exit", "quit"}: print("Goodbye!") break @@ -203,13 +203,13 @@ def main() -> None: continue elif prompt == "": continue - + try: index: int = int(prompt) if index < 0: print("Please enter a non-negative number.") continue - + result = fibonacci.get(index) if not result: print("[]") @@ -217,10 +217,10 @@ def main() -> None: print(f"First {index} Fibonacci numbers: {result}") if index > 0: print(f"The {index}th Fibonacci number is: {result[-1]}") - + except ValueError: print("Invalid input. Enter a number or use 'help' for commands.") - + except KeyboardInterrupt: print("\nGoodbye!") break @@ -230,4 +230,4 @@ def main() -> None: if __name__ == "__main__": - main() \ No newline at end of file + main() From 6cc951a9342f30f6a723b99cfd1468acfec9f0f6 Mon Sep 17 00:00:00 2001 From: NostraWho <138015113+Achintya47@users.noreply.github.com> Date: Wed, 9 Jul 2025 02:29:10 +0530 Subject: [PATCH 3/4] Update dynamic_programming/fibonacci.py Co-authored-by: Bertrand Awenze <156874668+bertrand-awz@users.noreply.github.com> --- dynamic_programming/fibonacci.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dynamic_programming/fibonacci.py b/dynamic_programming/fibonacci.py index 7cc3238521d8..18397c5554fc 100644 --- a/dynamic_programming/fibonacci.py +++ b/dynamic_programming/fibonacci.py @@ -96,7 +96,8 @@ def get(self, index: int) -> list[int]: True """ if index < 0: - raise ValueError(f"Index must be non-negative, got {index}") + error_msg = f"Index must be non-negative, got {index}" + raise ValueError(error_msg) if index == 0: return [] From 631037da4763ee7860fae72ee6d0d5ef2e7e981f Mon Sep 17 00:00:00 2001 From: NostraWho <138015113+Achintya47@users.noreply.github.com> Date: Wed, 9 Jul 2025 02:29:25 +0530 Subject: [PATCH 4/4] Update dynamic_programming/fibonacci.py Co-authored-by: Bertrand Awenze <156874668+bertrand-awz@users.noreply.github.com> --- dynamic_programming/fibonacci.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dynamic_programming/fibonacci.py b/dynamic_programming/fibonacci.py index 18397c5554fc..a976701c91b3 100644 --- a/dynamic_programming/fibonacci.py +++ b/dynamic_programming/fibonacci.py @@ -140,7 +140,8 @@ def get_nth(self, n: int) -> int: ValueError: Index must be non-negative, got -1 """ if n < 0: - raise ValueError(f"Index must be non-negative, got {n}") + error_msg = f"Index must be non-negative, got {n}" + raise ValueError(error_msg) # Extend sequence if needed if n >= len(self.sequence):