#include <pthread.h>
#include <unistd.h>
#include <assert.h>
#include <stdio.h>
#include "cilk.h"
#include <sys/time.h>

int foo (int i) {
    return i+1;
}

int fooi (void* iv) {
    int *ip=(int*)iv;
    return foo(*ip);
}

extern "C++" {

int N = 1000;

double tdiff (struct timeval *after, struct timeval *before)
{
    return after->tv_sec - before->tv_sec + (1e-6)*(after->tv_usec - before->tv_usec);
}


void do_cilkrun (void) {
    cilk::run(&foo, 0);
}

static cilk::context *ctx;
void do_cilkcxt (void) {
    int i = 0;
    int j __attribute__((__unused__)) = ctx->run(&fooi, (void*)&i);
}

void do_N_cilkcxt (void) {
    struct timeval start,end;
    gettimeofday(&start, 0);
    cilk::context ctx;
    int r=0;
    for (int i=0; i<N; i++) {
	int j=ctx.run(&fooi, (void*)&i);
	r+=j;
    }
    gettimeofday(&end, 0);
    printf("%.3fus per ctx.run (tot=%d)\n", 1e6*tdiff(&end, &start)/N, 0);
}


extern "Cilk++" int cilk_in_pt (int i) {
    assert(i==0);
    return 0;
}

pthread_t pt_for_cilk;
extern void* do_pt_for_cilk(void *extra __attribute__((__unused__))) {
    cilk::run(&cilk_in_pt, 0);
    return 0;
}

void setup_cilk_in_pthread (void) {
    int r = pthread_create(&pt_for_cilk, 0, do_pt_for_cilk, NULL);
    assert(r==0);
}

void do_cilk_in_pthread (void) {
}	

void* do_something (void *extra __attribute__((__unused__))) {
    return 0;
}

void do_pthread(void) {
    pthread_t pt;
    {int r = pthread_create(&pt, 0, do_something, NULL); assert(r==0);}
    {int r = pthread_join(pt, NULL);              assert(r==0);}
}

void measure(void (*f)(void), const char *string) {
    const int maxiter=4;
    for (int j=0; j<maxiter; j++) {
	int M = (j+1==maxiter) ? N : 1<<j;
	struct timeval start,end;
	gettimeofday(&start, 0);
	for (int i=0; i<M; i++) {
	    f();
	}
	gettimeofday(&end, 0);
	printf("%9.3fus per call (%d calls) for %s\n", 1e6*tdiff(&end, &start)/M, M, string);
    }
    printf("\n");
}

int main (int argc __attribute__((__unused__)), char *argv[] __attribute__((__unused__))) {
    measure(do_pthread, "pthread");
    measure(do_cilkrun,  "cilkrun");

    ctx = new cilk::context;
    measure(do_cilkcxt,  "cilkcxt");
    delete ctx;

    return 0;
}

} /* end extern "C" */
