/*
 * Fork/Join Framework 
 *
 * Test 2.
 *
 * Tests running multiple tasks.
 *
 * Written by G. Back for CS3214 Fall 2014.
 */
#include <pthread.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <time.h>

#include "threadpool.h"
#include "threadpool_lib.h"
#define DEFAULT_THREADS 1

/* Data to be passed to callable. */
struct arg2 {
    uintptr_t a;
    uintptr_t b;
};

/* 
 * A FJ task that multiplies 2 numbers. 
 */
static void *
multiplier_task(struct thread_pool *pool, struct arg2 * data)
{
    return (void *)(data->a * data->b);
}

static int
run_test(int nthreads)
{
    struct benchmark_data * bdata = start_benchmark();
    struct thread_pool * threadpool = thread_pool_new(nthreads);
   
#define NTASKS 200
    struct future *f[NTASKS];
    struct arg2 *args[NTASKS];
    int i;
    for (i = 0; i < NTASKS; i++) {
        args[i] = malloc(sizeof (struct arg2));
        args[i]->a = i;
        args[i]->b = i+1;
        f[i] = thread_pool_submit(threadpool, (fork_join_task_t) multiplier_task, args[i]);
    }

    bool success = true;
    for (i = 0; i < NTASKS; i++) {
        uintptr_t sprod = (uintptr_t) future_get(f[i]);
        future_free(f[i]);
        free(args[i]);
        if (sprod != i * (i + 1))
            success = false;
    }
    thread_pool_shutdown_and_destroy(threadpool);

    stop_benchmark(bdata);

    // consistency check
    if (!success) {
        fprintf(stderr, "Wrong result\n");
        abort();
    }

    report_benchmark_results(bdata);
    printf("Test successful.\n");
    free(bdata);
    return 0;
}

/**********************************************************************************/

static void
usage(char *av0, int exvalue)
{
    fprintf(stderr, "Usage: %s [-n <n>]\n"
                    " -n number of threads in pool, default %d\n"
                    , av0, DEFAULT_THREADS);
    exit(exvalue);
}

int 
main(int ac, char *av[]) 
{
    int c, nthreads = DEFAULT_THREADS;
    while ((c = getopt(ac, av, "hn:")) != EOF) {
        switch (c) {
        case 'n':
            nthreads = atoi(optarg);
            break;
        case 'h':
            usage(av[0], EXIT_SUCCESS);
        }
    }

    return run_test(nthreads);
}