// gcc --std=gnu99 -o test -Wall -Wextra -O3 rand_thread.c -lpthread

#include <assert.h>
#include <pthread.h>
#include <sys/time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

pthread_mutex_t mu = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

static int count = -1;

#define VALUES 4096L*4
#define CHILDREN 8

void barrier() {
    int r = pthread_mutex_lock(&mu);
    assert(r == 0);
    count -= 1;
    assert(count >= 0);
    if (count == 0) {
        r = pthread_cond_broadcast(&cond);
        assert(r == 0);
    }
    while (count > 0) {
        r = pthread_cond_wait(&cond, &mu);
        assert(r == 0);
    }
    assert(count == 0);
    r = pthread_mutex_unlock(&mu);
    assert(r == 0);
}

void* worker(void* arg) {
    long* array = (long*) arg;

    // Seed the RNG
    unsigned short state[3];
    unsigned int seed = time(NULL) + (unsigned int) pthread_self();
    memcpy(state, &seed, sizeof(seed));
    //~ unsigned int seed = time(NULL) + (unsigned int) pthread_self();
    //~ srand(time(NULL) + (unsigned int) pthread_self());

    barrier();

    // All threads are running: go nuts!
    for (int i = 0; i < VALUES; ++i) {
        //~ array[i] = random();
        //~ array[i] = rand();
        //~ array[i] = rand_r(&seed);
        array[i] = nrand48(state);
    }
    return NULL;
}

int main() {
    long children_arrays[CHILDREN][VALUES];
    pthread_t children[CHILDREN];
    //~ int r;

    // spawn the threads
    count = CHILDREN + 1;    
    for (int i = 0; i < CHILDREN; ++i) {
        int r = pthread_create(&children[i], NULL, worker, children_arrays[i]);
        assert(r == 0);
    }

    barrier();

    for (int i = 0; i < CHILDREN; ++i) {
        int r = pthread_join(children[i], NULL);
        assert(r == 0);
    }

    int matches = 0;
    for (int first_child = 0; first_child < CHILDREN; ++first_child) {
        for (int second_child = first_child+1; second_child < CHILDREN; ++second_child) {
            for (int first_index = 0; first_index < VALUES; ++first_index) {
                for (int second_index = 0; second_index < VALUES; ++second_index) {
                    if (children_arrays[first_child][first_index] == children_arrays[second_child][second_index]) {
                        printf("Match: value %ld child: %d %d index: %d %d\n", children_arrays[first_child][first_index], first_child, second_child, first_index, second_index);
                        matches += 1;
                    }
                }
            }
        }
    }

    // total pairs: pair of children, all pairs of values between them
#define TOTAL_PAIRS (CHILDREN * (CHILDREN - 1) / 2 * ((double) VALUES * VALUES))
    printf("%d matches (p = %g)\n", matches, (double) matches / (double) TOTAL_PAIRS);
    printf("Expected matches = %f (p = %g)\n", 1.0 / (double) RAND_MAX * TOTAL_PAIRS, 1.0 / (double) RAND_MAX);
    
    return 0;
}

