Skip to content

Commit dda8aab

Browse files
committed
Improved Test Coverage for dynamic_programming/fibonacci.py
1 parent cd3c3c3 commit dda8aab

File tree

1 file changed

+209
-27
lines changed

1 file changed

+209
-27
lines changed

dynamic_programming/fibonacci.py

Lines changed: 209 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,233 @@
11
"""
2-
This is a pure Python implementation of Dynamic Programming solution to the fibonacci
2+
This is a pure Python implementation of Dynamic Programming solution to the Fibonacci
33
sequence problem.
4+
5+
The Fibonacci sequence is a series of numbers where each number is the sum of the
6+
two preceding ones, usually starting with 0 and 1. The sequence begins:
7+
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, ...
8+
9+
Reference: https://en.wikipedia.org/wiki/Fibonacci_number
10+
11+
Time Complexity: O(n) for first calculation, O(1) for subsequent calls with memoization
12+
Space Complexity: O(n) for storing the sequence
413
"""
514

615

716
class Fibonacci:
17+
"""
18+
Dynamic Programming implementation of Fibonacci sequence generator.
19+
20+
This class maintains a memoized sequence of Fibonacci numbers and can efficiently
21+
generate new numbers by building on previously calculated values.
22+
23+
Attributes:
24+
sequence (list[int]): Memoized Fibonacci sequence starting with [0, 1]
25+
"""
26+
827
def __init__(self) -> None:
28+
"""Initialize the Fibonacci sequence with the first two numbers."""
929
self.sequence = [0, 1]
10-
11-
def get(self, index: int) -> list:
30+
31+
def get(self, index: int) -> list[int]:
1232
"""
13-
Get the Fibonacci number of `index`. If the number does not exist,
14-
calculate all missing numbers leading up to the number of `index`.
15-
16-
>>> Fibonacci().get(10)
17-
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
18-
>>> Fibonacci().get(5)
19-
[0, 1, 1, 2, 3]
33+
Get the first `index` Fibonacci numbers. If numbers don't exist in the sequence,
34+
calculate all missing numbers leading up to the required index.
35+
36+
Args:
37+
index (int): Number of Fibonacci numbers to return (must be non-negative)
38+
39+
Returns:
40+
list[int]: List containing the first `index` Fibonacci numbers
41+
42+
Raises:
43+
ValueError: If index is negative
44+
45+
Examples:
46+
>>> fib = Fibonacci()
47+
>>> fib.get(0)
48+
[]
49+
>>> fib.get(1)
50+
[0]
51+
>>> fib.get(2)
52+
[0, 1]
53+
>>> fib.get(5)
54+
[0, 1, 1, 2, 3]
55+
>>> fib.get(10)
56+
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
57+
>>> fib.get(1) # Test memoization - should not recalculate
58+
[0]
59+
>>> fib.get(15) # Test extending existing sequence
60+
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377]
61+
62+
# Test edge cases
63+
>>> fib_new = Fibonacci()
64+
>>> fib_new.get(0)
65+
[]
66+
>>> fib_new.get(1)
67+
[0]
68+
>>> fib_new.get(2)
69+
[0, 1]
70+
71+
# Test error handling
72+
>>> fib_error = Fibonacci()
73+
>>> fib_error.get(-1)
74+
Traceback (most recent call last):
75+
...
76+
ValueError: Index must be non-negative, got -1
77+
>>> fib_error.get(-5)
78+
Traceback (most recent call last):
79+
...
80+
ValueError: Index must be non-negative, got -5
81+
82+
# Test large numbers
83+
>>> fib_large = Fibonacci()
84+
>>> result = fib_large.get(20)
85+
>>> len(result)
86+
20
87+
>>> result[-1] # 20th Fibonacci number (0-indexed, so 19th position)
88+
4181
89+
>>> result[0], result[1], result[2]
90+
(0, 1, 1)
91+
92+
# Test sequence correctness
93+
>>> fib_test = Fibonacci()
94+
>>> seq = fib_test.get(8)
95+
>>> all(seq[i] == seq[i-1] + seq[i-2] for i in range(2, len(seq)))
96+
True
2097
"""
21-
if (difference := index - (len(self.sequence) - 2)) >= 1:
98+
if index < 0:
99+
raise ValueError(f"Index must be non-negative, got {index}")
100+
101+
if index == 0:
102+
return []
103+
104+
# Calculate missing numbers if needed
105+
if (difference := index - len(self.sequence)) > 0:
22106
for _ in range(difference):
23107
self.sequence.append(self.sequence[-1] + self.sequence[-2])
108+
24109
return self.sequence[:index]
110+
111+
def get_nth(self, n: int) -> int:
112+
"""
113+
Get the nth Fibonacci number (0-indexed).
114+
115+
Args:
116+
n (int): The index of the Fibonacci number to retrieve
117+
118+
Returns:
119+
int: The nth Fibonacci number
120+
121+
Raises:
122+
ValueError: If n is negative
123+
124+
Examples:
125+
>>> fib = Fibonacci()
126+
>>> fib.get_nth(0)
127+
0
128+
>>> fib.get_nth(1)
129+
1
130+
>>> fib.get_nth(2)
131+
1
132+
>>> fib.get_nth(5)
133+
5
134+
>>> fib.get_nth(10)
135+
55
136+
>>> fib.get_nth(-1)
137+
Traceback (most recent call last):
138+
...
139+
ValueError: Index must be non-negative, got -1
140+
"""
141+
if n < 0:
142+
raise ValueError(f"Index must be non-negative, got {n}")
143+
144+
# Extend sequence if needed
145+
if n >= len(self.sequence):
146+
difference = n - len(self.sequence) + 1
147+
for _ in range(difference):
148+
self.sequence.append(self.sequence[-1] + self.sequence[-2])
149+
150+
return self.sequence[n]
151+
152+
def reset(self) -> None:
153+
"""
154+
Reset the sequence to its initial state.
155+
156+
Examples:
157+
>>> fib = Fibonacci()
158+
>>> fib.get(10)
159+
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
160+
>>> len(fib.sequence)
161+
10
162+
>>> fib.reset()
163+
>>> fib.sequence
164+
[0, 1]
165+
"""
166+
self.sequence = [0, 1]
25167

26168

27169
def main() -> None:
170+
"""
171+
Interactive CLI for generating Fibonacci numbers.
172+
173+
Allows users to input indices and get the corresponding Fibonacci sequence.
174+
Supports commands: 'exit', 'quit', 'reset', 'help'
175+
"""
28176
print(
29-
"Fibonacci Series Using Dynamic Programming\n",
30-
"Enter the index of the Fibonacci number you want to calculate ",
31-
"in the prompt below. (To exit enter exit or Ctrl-C)\n",
32-
sep="",
177+
"Fibonacci Series Using Dynamic Programming\n"
178+
"Enter the index of the Fibonacci number you want to calculate\n"
179+
"Commands: 'exit'/'quit' to exit, 'reset' to clear cache, 'help' for help\n"
33180
)
181+
34182
fibonacci = Fibonacci()
35-
183+
36184
while True:
37-
prompt: str = input(">> ")
38-
if prompt in {"exit", "quit"}:
39-
break
40-
41185
try:
42-
index: int = int(prompt)
43-
except ValueError:
44-
print("Enter a number or 'exit'")
45-
continue
46-
47-
print(fibonacci.get(index))
186+
prompt: str = input(">> ").strip().lower()
187+
188+
if prompt in {"exit", "quit"}:
189+
print("Goodbye!")
190+
break
191+
elif prompt == "reset":
192+
fibonacci.reset()
193+
print("Fibonacci sequence cache cleared.")
194+
continue
195+
elif prompt == "help":
196+
print(
197+
"Commands:\n"
198+
" <number> - Get first N Fibonacci numbers\n"
199+
" reset - Clear the memoized sequence\n"
200+
" help - Show this help message\n"
201+
" exit/quit - Exit the program\n"
202+
)
203+
continue
204+
elif prompt == "":
205+
continue
206+
207+
try:
208+
index: int = int(prompt)
209+
if index < 0:
210+
print("Please enter a non-negative number.")
211+
continue
212+
213+
result = fibonacci.get(index)
214+
if not result:
215+
print("[]")
216+
else:
217+
print(f"First {index} Fibonacci numbers: {result}")
218+
if index > 0:
219+
print(f"The {index}th Fibonacci number is: {result[-1]}")
220+
221+
except ValueError:
222+
print("Invalid input. Enter a number or use 'help' for commands.")
223+
224+
except KeyboardInterrupt:
225+
print("\nGoodbye!")
226+
break
227+
except EOFError:
228+
print("\nGoodbye!")
229+
break
48230

49231

50232
if __name__ == "__main__":
51-
main()
233+
main()

0 commit comments

Comments
 (0)