use crate::thread;

#[test_case]
fn get_tid() {
    assert_eq!(0, thread::tid());
}

/// Exponentially multithreaded Fibonacci implementation
extern "C" fn fib(n: usize) -> usize {
    match n {
        0 | 1 => 1,
        n => {
            let t1 = thread::spawn(fib, n - 2).unwrap();
            let t2 = thread::spawn(fib, n - 1).unwrap();

            let op1 = thread::join(t1).unwrap();
            let op2 = thread::join(t2).unwrap();

            op1 + op2
        }
    }
}

/// A fast iterator-based approach to check against
struct FibIterator {
    curr: usize,
    next: usize,
}

impl Iterator for FibIterator {
    type Item = usize;

    fn next(&mut self) -> Option<usize> {
        let next = self.curr + self.next;
        let prev = self.curr;
        self.curr = self.next;
        self.next = next;
        Some(prev)
    }
}

impl Default for FibIterator {
    fn default() -> FibIterator {
        FibIterator { curr: 1, next: 1 }
    }
}

/// Test driver
fn fib_test(n: usize) {
    FibIterator::default()
        .take(n)
        .enumerate()
        .for_each(|(i, v)| assert_eq!(v, fib(i)))
}

#[test_case]
fn fib_multithread_1() {
    fib_test(1)
}

#[test_case]
fn fib_multithread_2() {
    fib_test(2)
}

#[test_case]
fn fib_multithread_4() {
    fib_test(4)
}

#[test_case]
fn fib_multithread_8() {
    fib_test(8)
}

#[test_case]
fn fib_multithread_12() {
    fib_test(12)
}
