#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>

// compile:
//  gcc -std=c99 fibonacci-demo.c -lpthread 
//
// run single-thread:
//  time ./a.out 0
//
// run multi-thread:
//  time ./a.out 1

int fibo_num = 30;
int repeat = 800;

int fibonacci(int n) {
    if (n > 1)
        return fibonacci(n - 1) + fibonacci(n - 2);
    else return n;
}

void single_thread_job() {
    for (int i = 0; i < repeat; i++) {
        int result = fibonacci(fibo_num);
        printf("Single-Thread-#%d: fibo(%d) => %d\n", i, fibo_num, result);
    }
}

struct fibo_arg {
    int idx;
    int repeat;
};

void *fibo_calc_work(void *arg) {
    int thread_idx = ((struct fibo_arg *) arg)->idx;
    int per_thread_repeat = ((struct fibo_arg *) arg)->repeat;

    printf("Thread #%d started, repeat = %d\n", thread_idx, per_thread_repeat);

    for (int i = 0; i < per_thread_repeat; i++) {
        int result = fibonacci(fibo_num);
        printf("Multi-Thread-#%d--#%d: fibo(%d) => %d\n", thread_idx, i, fibo_num, result);
    }

    if (arg) free(arg);
}

void multi_thread_job() {
    int num_threads = 8;
    pthread_t tids[num_threads];

    for (int i = 0; i < num_threads; i++) {
        struct fibo_arg *pfarg = malloc(sizeof(struct fibo_arg));
        pfarg->idx = i;
        pfarg->repeat = repeat / num_threads + i;

        printf("Try start thread: idx=%d, repeat=%d\n", pfarg->idx, pfarg->repeat);

        int err = pthread_create(&tids[i], NULL, fibo_calc_work, pfarg);
        if (err) {
            printf("Failed to create thread: %s\n", strerror(err));
            exit(err);
        }
    }
    for (int i = 0; i < num_threads; i++) {
        pthread_join(tids[i], NULL);
        printf("##### thread tid 0x%x finished.\n", (int) tids[i]);
    }
}

int main(int argc, char *argv[]) {
    char arg = ' ';
    if (argc > 1) arg = argv[1][0];

    if (arg == '0')
        single_thread_job();
    else if (arg == '1')
        multi_thread_job();
    else
        printf("Bad arg: %c\n", arg);
}
