#define _XOPEN_SOURCE 600 /* pthread_barrier_* */
#include <stdio.h>  	/* printf(), puts() */
#include <stdlib.h> 	/* exit() */
#include <unistd.h> 	/* sleep() */
#include <complex.h>	/* complex numbers */
#include <pthread.h>	/* threads */
#define STR_VAL(x) STR(x)
#define STR(x) #x
#define MAX_THREADS 1024
#define new(x) malloc(sizeof(x))
struct {
	pthread_mutex_t lock;
	struct coordinates { complex double z, c; } *coordinates;
	double escape_count;
} result;
pthread_barrier_t complete;
__attribute__((noreturn))
static void usage (char *myself) {
	printf("Usage: %s [threads]\n", myself);
	puts("threads must be between 1 and " STR_VAL(MAX_THREADS) );
	exit(-1);
}
#define tprintf(x, ...) {\
	printf("[%08lX] " x, (unsigned long)pthread_self(), __VA_ARGS__);\
	fflush(stdout);\
}
#define tputs(x) {\
	printf("[%08lX] " x "\n", (unsigned long)pthread_self());\
	fflush(stdout);\
}
static inline void *iterate (void *where) {
	struct coordinates *coordinates = (struct coordinates *)where;
	tprintf("Z = %f+%fi, C = %f+%fi\n",
		creal(coordinates->z), cimag(coordinates->z),
		creal(coordinates->c), cimag(coordinates->c)
	);
	sleep(1 + rand() % 2);
	double count = (double)rand() / RAND_MAX;
	pthread_mutex_lock(&result.lock);
	result.coordinates = coordinates;
	result.escape_count = count;
	pthread_barrier_wait(&complete);
	return NULL;
}
static inline struct coordinates *next_coordinates (void) {
	struct coordinates *coordinates = new(struct coordinates);
	coordinates->z = (double)rand() / RAND_MAX + (double)rand() / RAND_MAX * I;
	coordinates->c = (double)rand() / RAND_MAX + (double)rand() / RAND_MAX * I;
	return coordinates;
}
static inline void cleanup (void) {
	pthread_barrier_wait(&complete);
	printf("[DISPATCH] Z = %f+%fi, C = %f+%fi, escape_count = %f\n",
		creal(result.coordinates->z),
		cimag(result.coordinates->z),
		creal(result.coordinates->c),
		cimag(result.coordinates->c),
		result.escape_count
	);
	free(result.coordinates);
	pthread_mutex_unlock(&result.lock);
}
pthread_attr_t thread_attributes;
static inline void spawn (void) {
	pthread_t tmp;
	pthread_create(&tmp, &thread_attributes, &iterate, next_coordinates());
}
static inline void dispatch (unsigned threads) {
	pthread_barrier_init(&complete, NULL, 2);
	pthread_attr_init(&thread_attributes);
	pthread_attr_setdetachstate(&thread_attributes, PTHREAD_CREATE_DETACHED);
	for (unsigned i = 0; i < threads; i++) spawn();
	/* while unassigned work */
	for (unsigned i = 0; i < threads; i++) { cleanup(); spawn(); }
	for (unsigned i = 0; i < threads; i++) cleanup();
	pthread_attr_destroy(&thread_attributes);
	pthread_barrier_destroy(&complete);
}
int main (int argc, char **argv) {
	if (argc < 2) usage(argv[0]);
	unsigned threads = atoi(argv[1]);
	if (MAX_THREADS < threads || 0 >= threads) usage(argv[0]);
	printf("running %d concurrent threads\n", threads);
	fflush(stdout);
	dispatch(threads);
	return 0;
}