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
33sequence 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
716class 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
27169def 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 ("\n Goodbye!" )
226+ break
227+ except EOFError :
228+ print ("\n Goodbye!" )
229+ break
48230
49231
50232if __name__ == "__main__" :
51- main ()
233+ main ()
0 commit comments