#include <stdio.h>
#include <math.h>

#ifdef _OPENMP
    #include <omp.h>
#endif

int fibonacci(const int n) {
    printf("n = %i\n", n);
    if (n == 0) {
        return 0;
    }
    if (n == 1) {
        return 1;
    }
    return fibonacci(n - 1) + fibonacci(n - 2); 
}

/*

n = 10
n = 9
n = 8
...
fibonacci_10 = 55

*/

int fibonacci_OpenMP(const int n) {
    #pragma omp critical
    printf("thread_num = %i, n = %i\n", omp_get_thread_num(), n);

    if (n == 0) {
        return 0;
    }
    if (n == 1) {
        return 1;
    }
    int fibonacci_n_minus_1;
    int fibonacci_n_minus_2;
    #pragma omp task shared(fibonacci_n_minus_1)
    fibonacci_n_minus_1 = fibonacci_OpenMP(n - 1);
    #pragma omp task shared(fibonacci_n_minus_2)
    fibonacci_n_minus_2 = fibonacci_OpenMP(n - 2);
    #pragma omp taskwait
    return fibonacci_n_minus_1 + fibonacci_n_minus_2;
}

/*

thread_num = 0, n = 10
thread_num = 3, n = 9
thread_num = 1, n = 8
thread_num = 2, n = 7
...
fibonacci_10 = 55

*/

int main(int argc, char *argv[]) {

    const int N = 10;
    int fibonacci_N;
    
    fibonacci_N = fibonacci(N);
    printf ("fibonacci_%i = %i\n", N, fibonacci_N);

    #pragma omp parallel
    {
        #pragma omp single
        fibonacci_N = fibonacci_OpenMP(N);
    }
    printf ("fibonacci_%i = %i\n", N, fibonacci_N);


    fibonacci_N = 1. / sqrt(5) * (pow((1. + sqrt(5)) / 2., N) - pow((1. - sqrt(5)) /2., N));
    printf ("fibonacci_%i = %i\n", N, fibonacci_N);
    // fibonacci_10 = 55

    return 0;
}