├── .gitignore ├── README.md └── fibonacci.c /.gitignore: -------------------------------------------------------------------------------- 1 | # Executables 2 | *.exe 3 | *.out 4 | *.o 5 | *.obj 6 | 7 | # Generated files 8 | Fibonacci_*.txt 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FIBONACCI_GMP_OMP_C 2 | 3 | >A blazing-fast, accurate Fibonacci number calculator using the GMP (GNU Multiple Precision Arithmetic) library for arbitrary-precision integer math with optional OpenMP parallelization*. 4 | 5 | 6 | ## Features 7 | - Computes F(N) for any N (default: 20,000,000) 8 | - Uses GMP for true arbitrary-precision (no overflow, no floating point) 9 | - Extremely fast: O(log n * M(d)), where M(d) is the multiplication time for d digits 10 | - OpenMP parallelization support for ~1.5-2x speedup on multi-core systems 11 | - Automatic fallback to single-threaded when OpenMP unavailable 12 | - Save results to file with a flag 13 | 14 | ## Requirements 15 | - GCC (or compatible C compiler) 16 | - GMP library (https://gmplib.org/) 17 | 18 | ## Build 19 | 20 | ```sh 21 | # Simple build (reliable & fast): 22 | windows: 23 | gcc -O3 -march=native -mtune=native -fopenmp -o fib.exe fibonacci.c -lgmp 24 | linux: 25 | gcc -O3 -march=native -mtune=native -fopenmp -o fib fibonacci.c -lgmp 26 | android: 27 | pkg install gmp clang openmp 28 | clang -O3 -march=native -mtune=native -fopenmp -o fib fibonacci.c -lgmp 29 | 30 | # Maximum performance build (aggressive optimizations): 31 | windows: 32 | gcc -O3 -march=native -mtune=native -mavx512f -fopenmp -flto -funroll-loops -finline-functions -fomit-frame-pointer -falign-functions=32 -falign-loops=32 -fprefetch-loop-arrays -fno-stack-protector -DNDEBUG -o fib.exe fibonacci.c -lgmp 33 | linux: 34 | gcc -O3 -march=native -mtune=native -mavx512f -fopenmp -flto -funroll-loops -finline-functions -fomit-frame-pointer -falign-functions=32 -falign-loops=32 -fprefetch-loop-arrays -fno-stack-protector -DNDEBUG -o fib fibonacci.c -lgmp 35 | android: 36 | clang -O3 -march=native -mtune=native -fopenmp -flto -funroll-loops -finline-functions -fomit-frame-pointer -falign-functions=32 -falign-loops=32 -fprefetch-loop-arrays -fno-stack-protector -DNDEBUG -o fib fibonacci.c -lgmp 37 | 38 | # Note: OpenMP (-fopenmp) is optional. Without it, runs single-threaded. 39 | ``` 40 | 41 | ## Usage 42 | 43 | ```sh 44 | ./fib.exe [-s] [-h] [N] 45 | ``` 46 | - `-s` : Save result to file (optional) 47 | - `-h` : Show help message 48 | - `N` : Fibonacci number to compute 49 | 50 | ## Algorithm Deep Dive: Fast Doubling Recursion 51 | 52 | The algorithm uses a **divide-and-conquer** approach that recursively splits the problem in half until reaching the base case, then computes results as the recursion unwinds: 53 | 54 | ### Recursion Flow 55 | ``` 56 | fast_fib_calc(1000000) 57 | ├─ fast_fib_calc(500000) 58 | ├─ fast_fib_calc(250000) 59 | └─ ... keeps dividing by 2 (≈20 levels deep) 60 | └─ fast_fib_calc(0) ← BASE CASE: returns A=0, B=1 61 | ``` 62 | 63 | As recursion unwinds, each level computes F(2k) and F(2k+1) from F(k) and F(k+1) using fast doubling formulas, with OpenMP parallelization for N ≥ 100M. 64 | 65 | **Why it's fast**: O(log N) levels × O(M(d)) per level = **O(log N × M(d))** instead of O(N) for naive iteration. 66 | 67 | ## Algorithm Visualization 68 | 69 | The program uses a recursive "fast doubling" approach with OpenMP parallelization for large N: 70 | 71 | ``` 72 | fast_fib_calc(N, A, B) 73 | ↓ 74 | ┌─────────────────────┐ 75 | │ A = F(N) │ ← Output (mpz_t) 76 | │ B = F(N+1) │ ← Big integers 77 | └─────────────────────┘ 78 | ↑ 79 | ┌─────────────┴─────────────┐ 80 | │ │ 81 | if N == 0: else: 82 | A = 0, B = 1 Initialize: F0, F1, TEMP, F0_Q 83 | (base case) ↓ 84 | return fast_fib_calc(N/2, F0, F1) 85 | ↓ 86 | ┌─────────────────────────────┐ 87 | │ F0 = F(N/2) │ 88 | │ F1 = F(N/2+1) │ 89 | └─────────────────────────────┘ 90 | ↓ 91 | Apply Fast Doubling Formulas (Parallel if N >= 100K): 92 | ┌──────────────────────┬──────────────────────┐ 93 | │ #pragma omp section │ #pragma omp section │ 94 | │ ┌──────────────────┐ │ ┌──────────────────┐ │ 95 | │ │ TEMP = F1 << 1 │ │ │ B = F1 * F1 │ │ 96 | │ │ TEMP = TEMP - F0 │ │ │ F0_Q = F0 * F0 │ │ 97 | │ │ A = F0 * TEMP │ │ │ B = B + F0_Q │ │ 98 | │ │ // F(2k) │ │ │ // F(2k+1) │ │ 99 | │ └──────────────────┘ │ └──────────────────┘ │ 100 | └──────────────────────┴──────────────────────┘ 101 | ↓ 102 | ┌─────────────┐ 103 | │ N % 2 != 0?│ 104 | │ (N is odd) │ 105 | └─────────────┘ 106 | ↙ ↘ 107 | No ↙ ↘ Yes 108 | ┌──────────┐ ┌───────────────┐ 109 | │ Keep: │ │ mpz_swap(A,B) │ 110 | │ A=F(2k) │ │ mpz_add(B,B,A)│ 111 | │ B=F(2k+1)│ │ → A=F(N) │ 112 | └──────────┘ │ B=F(N+1) │ 113 | └───────────────┘ 114 | ↓ 115 | Clear: F0, F1, TEMP, F0_Q 116 | return A, B 117 | 118 | Recursion Call Stack for N=13: 119 | fast_fib_calc(13) // N odd 120 | ├─ fast_fib_calc(6) // 13/2 = 6 121 | ├─ fast_fib_calc(3) // 6/2 = 3, N odd 122 | ├─ fast_fib_calc(1) // 3/2 = 1, N odd 123 | └─ fast_fib_calc(0) ← Base: A=0, B=1 124 | └─ Compute F(2), F(3), then swap+add → F(1)=1, F(2)=1 125 | └─ Compute F(6), F(7), then swap+add → F(3)=2, F(4)=3 126 | └─ Compute F(12), F(13) → F(6)=8, F(7)=13 127 | └─ Compute F(26), F(27), then swap+add → F(13)=233, F(14)=377 128 | 129 | Time Complexity: O(log N) levels × O(M(d)) per level = O(log N × M(d)) 130 | Space Complexity: O(d) where d = digits in F(N), O(log N) stack depth 131 | ``` 132 | 133 | ## Examples 134 | 135 | ```sh 136 | $ ./fib.exe -s 16 137 | Computing F(16)... 138 | Result will be saved to file. 139 | Computation completed in 0.000056 seconds 140 | 141 | F(16) has 4 digits 142 | Number saved to: Fibonacci_16.txt 143 | Full number: 987 144 | Performance: 71301 digits/second 145 | 146 | 147 | 148 | $ ./fib.exe -h 149 | Usage: ./fib.exe [-s] [-h] [N] 150 | -s Save result to file 151 | -h Show this help message 152 | N Fibonacci number to compute 153 | 154 | Best Examples with N=16: 155 | ./fib.exe 16 # Output: Computing F(16)... F(16) = 987 156 | ./fib.exe -s 16 # Output: Computing F(16)... saves to Fibonacci_16.txt 157 | ./fib.exe -h # Show this help message 158 | ``` 159 | 160 | ## Performance Benchmarks 161 | 162 | **F(1,000,000,000) - World-Class Performance:** 163 | 164 | ```sh 165 | # ARM 3GHz CPU 166 | Computation completed in 10.168324 seconds 167 | Performance: 20552812 digits/second 168 | 169 | # x86 3GHz CPU 170 | Computation completed in 7.216904 seconds 171 | Performance: 28958076 digits/second 172 | 173 | # x86 5GHz CPU 174 | Computation completed in 4.577705 seconds 175 | Performance: 45653362 digits/second 176 | ``` 177 | *F(1 billion) computed in under 5 seconds - good performance for a cpu only approach* 178 | 179 | ## License 180 | MIT 181 | -------------------------------------------------------------------------------- /fibonacci.c: -------------------------------------------------------------------------------- 1 | /* 2 | Fibonacci Fast Doubling BIG_INTEGER: 3 | - Computes F(N) using the fast doubling method and GMP for arbitrary precision. 4 | - Fast doubling: Recursively computes F(2k) and F(2k+1) from F(k), F(k+1) in O(log N) time. 5 | - No approximations or floating point; exact results for huge N. 6 | - Supports OpenMP parallelization for multi-core acceleration on large N (>1M). 7 | 8 | Performance: 9 | - Time: O(log N * M(d)), where M(d) is the time to multiply d-digit numbers (GMP-optimized) 10 | - Space: O(d), d = number of digits in F(N) 11 | - Parallel speedup: ~1.5-2x on multi-core systems for very large N 12 | 13 | Usage: 14 | - ./fib.exe [-s] [-h] [N] 15 | - -s: Save result to file (optional) 16 | - -h: Show help message 17 | - N: Fibonacci number to compute (default: 20000000) 18 | 19 | Simple Compile: 20 | windows: 21 | gcc -O3 -march=native -mtune=native -fopenmp -o fib.exe fibonacci.c -lgmp 22 | linux: 23 | gcc -O3 -march=native -mtune=native -fopenmp -o fib fibonacci.c -lgmp 24 | 25 | Note: OpenMP (-fopenmp) is optional. Without it, runs single-threaded. 26 | 27 | Info: 28 | This code is so portable and efficient, it'll run on your smartfridge. 29 | */ 30 | 31 | #ifdef _OPENMP 32 | #include 33 | #endif 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | int RETURN_FLAG = 0; 42 | 43 | 44 | 45 | // F(2k) = F(k) * (2*F(k+1) - F(k)) 46 | // F(2k+1) = F(k+1)^2 + F(k)^2 47 | void fast_fib_calc(unsigned long N, mpz_t A, mpz_t B) 48 | { 49 | 50 | // If N is 0, start compute cases 51 | if (N == 0) 52 | { 53 | mpz_set_ui(A, 0); 54 | mpz_set_ui(B, 1); 55 | return; 56 | } 57 | 58 | // Init vars 59 | mpz_t F0, F1, TEMP, F0_Q; 60 | mpz_init(F0); 61 | mpz_init(F1); 62 | mpz_init(TEMP); 63 | mpz_init(F0_Q); 64 | 65 | // Recursive until N == 0 66 | fast_fib_calc(N / 2, F0, F1); 67 | 68 | 69 | // Independent calculations when (N >= 100000) res ~0.4 seconds 70 | #pragma omp parallel sections if (N >= 100000) 71 | { 72 | #pragma omp section 73 | { 74 | // TEMP = F1 * 2^1 = F1 << 1 75 | mpz_mul_2exp(TEMP, F1, 1); 76 | // TEMP = TEMP - F0 77 | mpz_sub(TEMP, TEMP, F0); 78 | // A = F0 * TEMP 79 | mpz_mul(A, F0, TEMP); 80 | } 81 | #pragma omp section 82 | { 83 | // B = F1 * F1 84 | mpz_mul(B, F1, F1); 85 | // F0_Q = F0 * F0 86 | mpz_mul(F0_Q, F0, F0); 87 | // B = B + F0_Q 88 | mpz_add(B, B, F0_Q); 89 | } 90 | } 91 | 92 | // If odd do swap 93 | if (N % 2 != 0) 94 | { 95 | mpz_swap(A, B); 96 | mpz_add(B, B, A); 97 | } 98 | 99 | // Clear temp vars 100 | mpz_clear(F0); 101 | mpz_clear(F1); 102 | mpz_clear(TEMP); 103 | mpz_clear(F0_Q); 104 | } 105 | 106 | 107 | 108 | // Print Fibonacci number 109 | void print_fib_info(mpz_t A, unsigned long N, int SAVE) 110 | { 111 | // Basic info 112 | size_t NUM_DIGITS = mpz_sizeinbase(A, 10); 113 | printf("F(%lu) has %zu digits\n", N, NUM_DIGITS); 114 | 115 | // For very large nums 116 | if (NUM_DIGITS > 1000000 && !SAVE) 117 | { 118 | mpz_t TEMP, D; 119 | mpz_init(TEMP); 120 | mpz_init(D); 121 | 122 | mpz_ui_pow_ui(D, 10, NUM_DIGITS - 50); 123 | mpz_div(TEMP, A, D); 124 | char *FIRST_DIGITS = mpz_get_str(NULL, 10, TEMP); 125 | 126 | // Last 50 digits: A % 10^50 127 | mpz_ui_pow_ui(D, 10, 50); 128 | mpz_mod(TEMP, A, D); 129 | char *LAST_DIGITS = mpz_get_str(NULL, 10, TEMP); 130 | 131 | printf("First ~50 digits: %s\n", FIRST_DIGITS); 132 | printf("Last 50 digits: %50s\n", LAST_DIGITS); 133 | 134 | free(FIRST_DIGITS); 135 | free(LAST_DIGITS); 136 | mpz_clear(TEMP); 137 | mpz_clear(D); 138 | return; 139 | } 140 | 141 | char *FIB_STR = NULL; 142 | 143 | // Only convert to string if needed 144 | if (SAVE || NUM_DIGITS <= 100 || NUM_DIGITS <= 1000000) 145 | { 146 | fflush(stdout); 147 | FIB_STR = mpz_get_str(NULL, 10, A); 148 | } 149 | 150 | // Save Fibonacci number to file with [-s] 151 | if (SAVE && FIB_STR) 152 | { 153 | char FILENAME[50]; 154 | sprintf(FILENAME, "Fibonacci_%lu.txt", N); 155 | 156 | printf("Writing to file %s... ", FILENAME); 157 | fflush(stdout); 158 | 159 | FILE *FILE_PTR = fopen(FILENAME, "w"); 160 | if (FILE_PTR != NULL) 161 | { 162 | fprintf(FILE_PTR, "F(%lu) = %s\n", N, FIB_STR); 163 | fclose(FILE_PTR); 164 | printf("Number saved to: %s\n", FILENAME); 165 | } 166 | else 167 | { 168 | printf("failed.\n"); 169 | printf("Couldn't create file %s\n", FILENAME); 170 | } 171 | } 172 | 173 | // Display digits 174 | if (FIB_STR) 175 | { 176 | if (NUM_DIGITS > 100) 177 | { 178 | printf("First 50 digits: %.50s\n", FIB_STR); 179 | printf("Last 50 digits: %s\n", FIB_STR + NUM_DIGITS - 50); 180 | } 181 | else 182 | { 183 | printf("Full number: %s\n", FIB_STR); 184 | } 185 | free(FIB_STR); 186 | } 187 | } 188 | 189 | 190 | 191 | void print_usage(const char *PROGRAM_NAME) 192 | { 193 | printf("Usage: %s [-s] [-h] [N]\n", PROGRAM_NAME); 194 | printf(" -s Save result to file (optional)\n"); 195 | printf(" -h Show this help message\n"); 196 | printf(" N Fibonacci number to compute (default: 20000000)\n"); 197 | printf("\nExamples:\n"); 198 | printf(" %s # Compute F(20000000), don't save\n", PROGRAM_NAME); 199 | printf(" %s -s # Compute F(20000000), save to file\n", PROGRAM_NAME); 200 | printf(" %s -s 1000000 # Compute F(1000000), save to file\n", PROGRAM_NAME); 201 | printf(" %s 100 # Compute F(100), don't save\n", PROGRAM_NAME); 202 | } 203 | 204 | 205 | 206 | int main(int argc, char *argv[]) 207 | { 208 | 209 | // Init GMP BigInt Arrays 210 | mpz_t FIB_N, FIB_N1; 211 | mpz_init(FIB_N); 212 | mpz_init(FIB_N1); 213 | 214 | unsigned long TARGET = 20000000; // Default: compute F(20,000,000) 215 | int SAVE_TO_FILE = 0; // Default: don't save to file 216 | 217 | // Parse command line arguments 218 | for (int I = 1; I < argc; I++) 219 | { 220 | if (strcmp(argv[I], "-s") == 0) 221 | { 222 | SAVE_TO_FILE = 1; // File saving 223 | } 224 | else if (strcmp(argv[I], "-h") == 0) 225 | { 226 | print_usage(argv[0]); // help and exit 227 | 228 | goto EXIT; 229 | } 230 | else 231 | { 232 | char *ENDPTR; 233 | unsigned long PARSED = strtoul(argv[I], &ENDPTR, 10); 234 | if (*ENDPTR == '\0' && PARSED > 0) 235 | { 236 | TARGET = PARSED; 237 | } 238 | else 239 | { 240 | printf("Error: Invalid argument '%s'\n\n", argv[I]); 241 | print_usage(argv[0]); 242 | 243 | RETURN_FLAG = 1; 244 | goto EXIT; 245 | } 246 | } 247 | } 248 | 249 | printf("Computing F(%lu)...\n\n", TARGET); 250 | if (SAVE_TO_FILE) 251 | { 252 | printf("Result will be saved to file.\n"); 253 | } 254 | 255 | struct timespec START_TIME, END_TIME; 256 | clock_gettime(CLOCK_MONOTONIC, &START_TIME); 257 | 258 | 259 | // Compute F(TARGET) 260 | fast_fib_calc(TARGET, FIB_N, FIB_N1); 261 | 262 | 263 | clock_gettime(CLOCK_MONOTONIC, &END_TIME); 264 | double ELAPSED = (END_TIME.tv_sec - START_TIME.tv_sec) + 265 | (END_TIME.tv_nsec - START_TIME.tv_nsec) / 1000000000.0; 266 | 267 | printf("Computation completed in %.6f seconds\n", ELAPSED); 268 | struct timespec PRINT_START, PRINT_END; 269 | clock_gettime(CLOCK_MONOTONIC, &PRINT_START); 270 | 271 | 272 | 273 | // Print results 274 | print_fib_info(FIB_N, TARGET, SAVE_TO_FILE); 275 | 276 | 277 | 278 | clock_gettime(CLOCK_MONOTONIC, &PRINT_END); 279 | double PRINT_ELAPSED = (PRINT_END.tv_sec - PRINT_START.tv_sec) + 280 | (PRINT_END.tv_nsec - PRINT_START.tv_nsec) / 1000000000.0; 281 | 282 | size_t NUM_DIGITS = mpz_sizeinbase(FIB_N, 10); 283 | printf("\nPerformance summary:\n"); 284 | printf(" Computation: %.6f seconds (%.0f digits/second)\n", ELAPSED, NUM_DIGITS / ELAPSED); 285 | if (PRINT_ELAPSED > 0.001) 286 | printf(" System(I/O): %.6f seconds\n", PRINT_ELAPSED); 287 | 288 | 289 | EXIT: 290 | 291 | // Clean up GMP vars 292 | mpz_clear(FIB_N); 293 | mpz_clear(FIB_N1); 294 | 295 | return RETURN_FLAG; 296 | } --------------------------------------------------------------------------------