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    mem::replace,
53};
54
55use num_bigint::BigUint;
56use num_traits::{One, Zero};
57use rayon::{current_num_threads, prelude::*};
58
59/// Type alias for the result of the fast doubling algorithm
60///
61/// Represents a pair of consecutive Fibonacci numbers (F(n), F(n+1))
62type FibPair = (BigUint, BigUint);
63
64/// A utility struct for computing Fibonacci numbers efficiently.
65///
66/// This struct provides static methods for calculating Fibonacci numbers using
67/// highly optimized algorithms. It supports both single Fibonacci number calculation
68/// and generating ranges of consecutive Fibonacci numbers.
69///
70/// The implementation uses a fast doubling algorithm for O(log n) time complexity
71/// and leverages parallel processing for range calculations to maximize performance.
72///
73/// For Fibonacci numbers beyond F(185), the implementation automatically switches to
74/// using `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        if n.is_multiple_of(2) {
153            (f2k, f2k1)
154        } else {
155            let f2k2 = &f2k1 + &f2k;
156            (f2k1, f2k2)
157        }
158    }
159
160    /// Generates Fibonacci numbers for indices in the given inclusive range.
161    ///
162    /// This method efficiently computes a sequence of consecutive Fibonacci numbers
163    /// using parallel processing for improved performance. It divides the requested range
164    /// into optimal chunks based on the available CPU threads, calculates each chunk in
165    /// parallel, and then combines the results.
166    ///
167    /// The implementation uses a hybrid approach that:
168    /// 1. Uses the fast doubling algorithm to efficiently find the starting values for each chunk
169    /// 2. Computes subsequent Fibonacci numbers iteratively within each chunk
170    /// 3. Processes chunks in parallel using the Rayon library
171    ///
172    /// # Arguments
173    ///
174    /// * `start` - The starting index of the range
175    /// * `end` - The ending index of the range (inclusive)
176    ///
177    /// # Returns
178    ///
179    /// * A `Vec<BigUint>` containing ordered Fibonacci numbers for indices in the specified inclusive range.
180    ///
181    /// # Complexity
182    ///
183    /// * Time complexity: Approximately O(n/t + log(start)) where n is the range size and t is the number of threads.
184    /// * Space complexity: O(n) for storing the Fibonacci numbers in a vector.
185    ///
186    /// # Performance
187    ///
188    /// Performance scales with the number of available CPU cores. For large ranges, the
189    /// parallelization provides significant speedup compared to sequential calculation.
190    ///
191    /// # Examples
192    ///
193    /// ```
194    /// use fib_rs::Fib;
195    /// use num_bigint::BigUint;
196    /// use num_traits::{Zero, One};
197    ///
198    /// // Generate Fibonacci numbers from index 3 to 10 (both 3 and 10 inclusive)
199    /// let fibs = Fib::range(3, 10);
200    ///
201    /// assert_eq!(fibs.len(), 8); // indices 3 to 10
202    /// assert_eq!(fibs[0], BigUint::from(2u32)); // F(3) = 2
203    /// assert_eq!(fibs[7], BigUint::from(55u32)); // F(10) = 55
204    /// ```
205    ///
206    /// For large ranges, the parallel implementation provides significant performance improvements:
207    ///
208    /// ```
209    /// use fib_rs::Fib;
210    ///
211    /// // Generate 10,000 consecutive Fibonacci numbers efficiently
212    /// let large_range = Fib::range(1000, 10999);
213    /// assert_eq!(large_range.len(), 10000);
214    /// ```
215    pub fn range(start: u128, end: u128) -> Vec<BigUint> {
216        // Validate input range
217        if end < start {
218            return Vec::new();
219        }
220
221        // Calculate total number of Fibonacci numbers to generate
222        let total_count = (end - start + 1) as usize;
223
224        // Determine optimal chunk size for parallelization based on available CPU threads
225        // This balances parallelism with the overhead of creating too many small chunks
226        let num_threads = current_num_threads();
227        let chunk_size = max(1, total_count / num_threads);
228
229        // Calculate number of chunks with ceiling division to ensure we cover the entire range
230        let num_chunks = total_count.div_ceil(chunk_size);
231
232        // Create chunk boundaries for parallel processing
233        // Each chunk represents a subrange of consecutive Fibonacci numbers
234        let chunks: Vec<_> = (0..num_chunks)
235            .map(|i| {
236                let chunk_start = start + (i as u128) * (chunk_size as u128);
237                let chunk_end = min(chunk_start + (chunk_size as u128) - 1, end);
238                (chunk_start, chunk_end)
239            })
240            .collect();
241
242        // Process each chunk in parallel using Rayon's parallel iterator
243        // Each thread calculates a portion of the Fibonacci sequence independently
244        let results: Vec<Vec<BigUint>> = chunks
245            .par_iter()
246            .map(|&(chunk_start, chunk_end)| {
247                let chunk_size = (chunk_end - chunk_start + 1) as usize;
248                let mut result = Vec::with_capacity(chunk_size);
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
254                // Add the first value (F(chunk_start)) to the result
255                result.push(a.clone());
256
257                // Compute the rest of the chunk iteratively using the recurrence relation:
258                // F(n+2) = F(n+1) + F(n)
259                // This is more efficient than using the fast doubling algorithm for each number
260                for _ in 1..chunk_size {
261                    let next = &a + &b;
262                    a = replace(&mut b, next); // Efficiently swap values
263                    result.push(a.clone());
264                }
265
266                result
267            })
268            .collect();
269
270        // Combine results from all chunks into a single, continuous vector
271        // Pre-allocate the exact size needed to avoid multiple reallocations
272        let mut result = Vec::with_capacity(total_count);
273        for chunk in results {
274            result.extend(chunk);
275        }
276
277        result
278    }
279}
280
281#[cfg(test)]
282mod tests {
283    use super::*;
284    use std::str::FromStr;
285
286    #[test]
287    fn correct_fib_formula() {
288        // The formula is respected
289        assert_eq!(Fib::single(0), BigUint::zero());
290        assert_eq!(Fib::single(1), BigUint::one());
291        assert_eq!(Fib::single(10), BigUint::from_str("55").unwrap());
292        assert_eq!(Fib::single(20), BigUint::from_str("6765").unwrap());
293        // Beyond u128 outputs are correct
294        assert_eq!(
295            Fib::single(187),
296            BigUint::from_str("538522340430300790495419781092981030533").unwrap()
297        );
298        // Beyond u8 inputs produces correct output
299        assert_eq!(
300            Fib::single(256),
301            BigUint::from_str("141693817714056513234709965875411919657707794958199867").unwrap()
302        );
303    }
304
305    #[test]
306    fn correct_sequence_generation() {
307        let fib_seq_1 = Fib::range(0, 100);
308        assert_eq!(fib_seq_1.len(), 101);
309        assert_eq!(fib_seq_1[0], BigUint::zero());
310        assert_eq!(fib_seq_1[1], BigUint::one());
311        assert_eq!(fib_seq_1[10], BigUint::from_str("55").unwrap());
312        assert_eq!(fib_seq_1[20], BigUint::from_str("6765").unwrap());
313
314        let fib_seq_2 = Fib::range(100, 300);
315        assert_eq!(fib_seq_2.len(), 201);
316        assert_eq!(
317            fib_seq_2[0],
318            BigUint::from_str("354224848179261915075").unwrap()
319        ); // Supposed to be fib 100
320        assert_eq!(
321            fib_seq_2[1],
322            BigUint::from_str("573147844013817084101").unwrap()
323        ); // Supposed to be fib 101
324        assert_eq!(
325            fib_seq_2[156],
326            BigUint::from_str("141693817714056513234709965875411919657707794958199867").unwrap()
327        );
328    }
329
330    #[test]
331    fn ordering_check() {
332        // Check that the Fibonacci sequence is strictly increasing
333        let fib_seq = Fib::range(0, 100);
334        // Start from the third element since the first three results are 0, 1, 1
335        for i in 3..fib_seq.len() {
336            assert!(fib_seq[i] > fib_seq[i - 1]);
337        }
338    }
339
340    // Loop over all the Fibonacci numbers to ensure the function does not panic over a variety of inputs
341    #[test]
342    fn loop_over_fibonacci() {
343        for i in 0..=1000 {
344            Fib::single(i);
345        }
346    }
347
348    // Test with various ranges
349    #[test]
350    fn loop_over_fibonacci_ranges() {
351        let ranges = vec![
352            (0, 100),
353            (50, 150),
354            (100, 200),
355            (150, 250),
356            (200, 300),
357            (350, 450),
358            (400, 500),
359            (450, 550),
360            (500, 600),
361            (550, 650),
362            (600, 700),
363            (650, 750),
364            (700, 800),
365            (750, 850),
366            (800, 900),
367            (850, 950),
368            (900, 1000),
369        ];
370
371        for (start, end) in ranges {
372            Fib::range(start, end);
373        }
374    }
375}