fib_rs/lib.rs
1//! # fib-rs
2//!
3//! A highly optimized Fibonacci number calculator for Rust that efficiently computes
4//! arbitrarily large Fibonacci numbers.
5//!
6//! ## Features
7//!
8//! - **Fast doubling algorithm**: Calculates Fibonacci numbers in O(log n) time
9//! - **Handles massive inputs**: Compute Fibonacci numbers up to F(10,000,000) and beyond
10//! - **Range calculation**: Generate sequences of consecutive Fibonacci numbers with parallel processing
11//! - **BigUint support**: Uses arbitrary precision integers for handling large Fibonacci numbers
12//!
13//! ## Examples
14//!
15//! ```
16//! use fib_rs::Fib;
17//! use num_bigint::BigUint;
18//! use num_traits::Zero;
19//!
20//! // Calculate a single Fibonacci number
21//! let n = 100;
22//! let result = Fib::single(n);
23//! assert!(result > BigUint::zero());
24//!
25//! // Calculate a range of Fibonacci numbers (F(3) through F(10))
26//! let start = 3;
27//! let end = 10;
28//! let results = Fib::range(start, end);
29//! assert_eq!(results.len(), (end - start + 1) as usize);
30//! ```
31//!
32//! ## Algorithm Details
33//!
34//! ### Single Fibonacci Number
35//!
36//! For computing a single Fibonacci number, this implementation uses the fast doubling algorithm
37//! with logarithmic time complexity:
38//!
39//! - For even n: F(2k) = F(k) * (2*F(k+1) - F(k))
40//! - For odd n: F(2k+1) = F(k+1)^2 + F(k)^2
41//!
42//! ### Fibonacci Range
43//!
44//! The range implementation combines three approaches for optimal performance:
45//!
46//! 1. **Parallel processing**: Divides the requested range into optimal chunks based on available CPU threads
47//! 2. **Smart initialization**: Uses the fast doubling algorithm to efficiently find the starting values for each chunk
48//! 3. **Iterative calculation**: After finding starting values, computes subsequent Fibonacci numbers iteratively within each chunk
49
50use std::{
51 cmp::{max, min},
52 iter::from_fn,
53 mem::{replace, take},
54};
55
56use num_bigint::BigUint;
57use num_traits::{One, Zero};
58use rayon::{current_num_threads, prelude::*};
59
60/// Type alias for the result of the fast doubling algorithm
61///
62/// Represents a pair of consecutive Fibonacci numbers (F(n), F(n+1))
63type FibPair = (BigUint, BigUint);
64
65/// A utility struct for computing Fibonacci numbers efficiently.
66///
67/// This struct provides static methods for calculating Fibonacci numbers using
68/// highly optimized algorithms. It supports both single Fibonacci number calculation
69/// and generating ranges of consecutive Fibonacci numbers.
70///
71/// The implementation uses a fast doubling algorithm for O(log n) time complexity
72/// and leverages parallel processing for range calculations to maximize performance.
73///
74/// Uses `BigUint` for arbitrary precision, ensuring correct results for extremely large
75/// Fibonacci numbers.
76pub struct Fib;
77
78impl Fib {
79 /// Calculate the nth Fibonacci number using an optimized fast doubling algorithm.
80 ///
81 /// This method efficiently computes Fibonacci numbers of arbitrary size by using
82 /// a divide-and-conquer approach based on matrix identities.
83 ///
84 /// # Arguments
85 ///
86 /// * `n` - The index of the Fibonacci number to calculate (0-indexed, where F(0)=0, F(1)=1)
87 ///
88 /// # Returns
89 ///
90 /// * The nth Fibonacci number as a `BigUint`
91 ///
92 /// # Complexity
93 ///
94 /// * Time complexity: O(log n) due to the fast doubling algorithm
95 /// * Space complexity: O(log n) for the recursive call stack
96 ///
97 /// # Examples
98 ///
99 /// ```
100 /// use fib_rs::Fib;
101 /// use num_bigint::BigUint;
102 /// use num_traits::Zero;
103 ///
104 /// assert_eq!(Fib::single(0), BigUint::zero()); // F(0) = 0
105 /// assert_eq!(Fib::single(10), BigUint::from(55u32)); // F(10) = 55
106 /// assert!(Fib::single(200) > BigUint::from(u128::MAX)); // Large value example (would overflow primitive types)
107 /// ```
108 pub fn single(n: u128) -> BigUint {
109 match n {
110 0 => BigUint::zero(),
111 1 => BigUint::one(),
112 _ => Self::fib_fast_doubling_helper(n).0,
113 }
114 }
115
116 /// Helper function for the fast doubling algorithm.
117 ///
118 /// This function implements the recursive divide-and-conquer approach for computing
119 /// Fibonacci numbers using the fast doubling method. It returns a pair of consecutive
120 /// Fibonacci numbers (F(n), F(n+1)) to enable the recursion.
121 ///
122 /// The algorithm is based on the following mathematical identities:
123 /// - For even n: F(2k) = F(k) * (2*F(k+1) - F(k))
124 /// - For odd n: F(2k+1) = F(k+1)^2 + F(k)^2
125 ///
126 /// # Arguments
127 ///
128 /// * `n` - The index of the Fibonacci number to calculate
129 ///
130 /// # Returns
131 ///
132 /// * A tuple (F(n), F(n+1)) containing the nth and (n+1)th Fibonacci numbers
133 ///
134 /// # Time Complexity
135 ///
136 /// * O(log n) due to the recursive divide-and-conquer approach
137 fn fib_fast_doubling_helper(n: u128) -> FibPair {
138 if n == 0 {
139 return (BigUint::zero(), BigUint::one());
140 }
141
142 // Calculate F(k) and F(k+1) where k = floor(n/2)
143 let (fk, fk1) = Self::fib_fast_doubling_helper(n / 2);
144
145 // Calculate F(2k) and F(2k+1)
146 let two_fk1 = &fk1 << 1; // Efficiently multiply by 2 using bit shift
147 let term = &two_fk1 - &fk;
148 let f2k = &fk * &term; // F(2k) = F(k) * (2*F(k+1) - F(k))
149 let f2k1 = &fk * &fk + &fk1 * &fk1; // F(2k+1) = F(k)^2 + F(k+1)^2
150
151 // Return appropriate pair based on whether n is even or odd
152 // Already internally does a bitwise operation
153 if n.is_multiple_of(2) {
154 (f2k, f2k1)
155 } else {
156 let f2k2 = &f2k1 + &f2k;
157 (f2k1, f2k2)
158 }
159 }
160
161 /// Generates Fibonacci numbers for indices in the given inclusive range.
162 ///
163 /// This method efficiently computes a sequence of consecutive Fibonacci numbers
164 /// using parallel processing for improved performance. It divides the requested range
165 /// into optimal chunks based on the available CPU threads, calculates each chunk in
166 /// parallel, and then combines the results.
167 ///
168 /// The implementation uses a hybrid approach that:
169 /// 1. Uses the fast doubling algorithm to efficiently find the starting values for each chunk
170 /// 2. Computes subsequent Fibonacci numbers iteratively within each chunk
171 /// 3. Processes chunks in parallel using the Rayon library
172 ///
173 /// # Arguments
174 ///
175 /// * `start` - The starting index of the range
176 /// * `end` - The ending index of the range (inclusive)
177 ///
178 /// # Returns
179 ///
180 /// * A `Vec<BigUint>` containing ordered Fibonacci numbers for indices in the specified inclusive range.
181 ///
182 /// # Complexity
183 ///
184 /// * Time complexity: Approximately O(n/t + log(start)) where n is the range size and t is the number of threads.
185 /// * Space complexity: O(n) for storing the Fibonacci numbers in a vector.
186 ///
187 /// # Performance
188 ///
189 /// Performance scales with the number of available CPU cores. For large ranges, the
190 /// parallelization provides significant speedup compared to sequential calculation.
191 ///
192 /// # Examples
193 ///
194 /// ```
195 /// use fib_rs::Fib;
196 /// use num_bigint::BigUint;
197 /// use num_traits::{Zero, One};
198 ///
199 /// // Generate Fibonacci numbers from index 3 to 10 (both 3 and 10 inclusive)
200 /// let fibs = Fib::range(3, 10);
201 ///
202 /// assert_eq!(fibs.len(), 8); // indices 3 to 10
203 /// assert_eq!(fibs[0], BigUint::from(2u32)); // F(3) = 2
204 /// assert_eq!(fibs[7], BigUint::from(55u32)); // F(10) = 55
205 /// ```
206 ///
207 /// For large ranges, the parallel implementation provides significant performance improvements:
208 ///
209 /// ```
210 /// use fib_rs::Fib;
211 ///
212 /// // Generate 10,000 consecutive Fibonacci numbers efficiently
213 /// let large_range = Fib::range(1000, 10999);
214 /// assert_eq!(large_range.len(), 10000);
215 /// ```
216 pub fn range(start: u128, end: u128) -> Vec<BigUint> {
217 // Validate input range
218 if end < start {
219 return Vec::new();
220 }
221
222 // Calculate total number of Fibonacci numbers to generate
223 let total_count = (end - start + 1) as usize;
224
225 // Determine optimal chunk size for parallelization based on available CPU threads
226 // This balances parallelism with the overhead of creating too many small chunks
227 let num_threads = current_num_threads();
228 let chunk_size = max(1, total_count / num_threads);
229
230 // Calculate number of chunks with ceiling division to ensure we cover the entire range
231 let num_chunks = total_count.div_ceil(chunk_size);
232
233 // Create chunk boundaries for parallel processing
234 // Each chunk represents a subrange of consecutive Fibonacci numbers
235 let chunks: Vec<_> = (0..num_chunks)
236 .map(|i| {
237 let chunk_start = start + (i as u128) * (chunk_size as u128);
238 let chunk_end = min(chunk_start + (chunk_size as u128) - 1, end);
239 (chunk_start, chunk_end)
240 })
241 .collect();
242
243 // Process each chunk in parallel using Rayon's parallel iterator
244 // Each thread calculates a portion of the Fibonacci sequence independently
245 chunks
246 .par_iter()
247 .flat_map_iter(|&(chunk_start, chunk_end)| {
248 let chunk_size = (chunk_end - chunk_start + 1) as usize;
249
250 // Use the fast doubling algorithm to efficiently find the starting values
251 // for this chunk. This is a key optimization for chunk initialization.
252 let (mut a, mut b) = Self::fib_fast_doubling_helper(chunk_start);
253 let mut remaining = chunk_size;
254
255 // Compute the chunk iteratively using the recurrence relation:
256 // F(n+2) = F(n+1) + F(n)
257 // This is more efficient than using the fast doubling algorithm for each number
258 from_fn(move || {
259 if remaining == 0 {
260 return None;
261 }
262 let next = &a + &b;
263 let out = take(&mut a);
264 a = replace(&mut b, next);
265 remaining -= 1;
266 Some(out)
267 })
268 })
269 .collect()
270 }
271}
272
273#[cfg(test)]
274mod tests {
275 use super::*;
276 use std::str::FromStr;
277
278 #[test]
279 fn correct_fib_formula() {
280 // The formula is respected
281 assert_eq!(Fib::single(0), BigUint::zero());
282 assert_eq!(Fib::single(1), BigUint::one());
283 assert_eq!(Fib::single(10), BigUint::from_str("55").unwrap());
284 assert_eq!(Fib::single(20), BigUint::from_str("6765").unwrap());
285 // Beyond u128 outputs are correct
286 assert_eq!(
287 Fib::single(187),
288 BigUint::from_str("538522340430300790495419781092981030533").unwrap()
289 );
290 // Beyond u8 inputs produces correct output
291 assert_eq!(
292 Fib::single(256),
293 BigUint::from_str("141693817714056513234709965875411919657707794958199867").unwrap()
294 );
295 }
296
297 #[test]
298 fn correct_sequence_generation() {
299 let fib_seq_1 = Fib::range(0, 100);
300 assert_eq!(fib_seq_1.len(), 101);
301 assert_eq!(fib_seq_1[0], BigUint::zero());
302 assert_eq!(fib_seq_1[1], BigUint::one());
303 assert_eq!(fib_seq_1[10], BigUint::from_str("55").unwrap());
304 assert_eq!(fib_seq_1[20], BigUint::from_str("6765").unwrap());
305
306 let fib_seq_2 = Fib::range(100, 300);
307 assert_eq!(fib_seq_2.len(), 201);
308 assert_eq!(
309 fib_seq_2[0],
310 BigUint::from_str("354224848179261915075").unwrap()
311 ); // Supposed to be fib 100
312 assert_eq!(
313 fib_seq_2[1],
314 BigUint::from_str("573147844013817084101").unwrap()
315 ); // Supposed to be fib 101
316 assert_eq!(
317 fib_seq_2[156],
318 BigUint::from_str("141693817714056513234709965875411919657707794958199867").unwrap()
319 );
320 }
321
322 #[test]
323 fn ordering_check() {
324 // Check that the Fibonacci sequence is strictly increasing
325 let fib_seq = Fib::range(0, 100);
326 // Start from the third element since the first three results are 0, 1, 1
327 for i in 3..fib_seq.len() {
328 assert!(fib_seq[i] > fib_seq[i - 1]);
329 }
330 }
331
332 // Loop over all the Fibonacci numbers to ensure the function does not panic over a variety of inputs
333 #[test]
334 fn loop_over_fibonacci() {
335 for i in 0..=1000 {
336 Fib::single(i);
337 }
338 }
339
340 // Test with various ranges
341 #[test]
342 fn loop_over_fibonacci_ranges() {
343 let ranges = vec![
344 (0, 100),
345 (50, 150),
346 (100, 200),
347 (150, 250),
348 (200, 300),
349 (350, 450),
350 (400, 500),
351 (450, 550),
352 (500, 600),
353 (550, 650),
354 (600, 700),
355 (650, 750),
356 (700, 800),
357 (750, 850),
358 (800, 900),
359 (850, 950),
360 (900, 1000),
361 ];
362
363 for (start, end) in ranges {
364 Fib::range(start, end);
365 }
366 }
367}