#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "../memcheck.h"

/* 
   Incompetent memory management

   Simulate what happens when trying to manage a dense brancy cyclic
   structure with refcounting.
 */

static int bytes, blocks;

struct node {
	struct node *l, *r;
	int ref;
};

static void ref(struct node *n)
{
	if (n)
		n->ref++;
}

static void unref(struct node *n)
{
	if (n && --n->ref == 0) {
		bytes -= sizeof(*n);
		blocks--;
		free(n);
	}
}

static struct node *alloc()
{
	struct node *n = malloc(sizeof(*n));
	n->l = n->r = NULL;
	n->ref = 0;

	bytes += sizeof(*n);
	blocks++;

	return n;
}

static void assign(struct node **np, struct node *n)
{
	ref(n);
	unref(*np);

	*np = n;
}

static int rrange(int range)
{
	long long ret = rand() * (long long)range;

	return (int)(ret / RAND_MAX);
}

#define ITER	100000
#define N	1000

static struct node *nodes[N];

static struct node *mk()
{
	int idx = rrange(N);
	struct node *n;

	/* 
	   50% recycle
	   25% alloc new
	   25% return NULL
	 */
	if (rand() > RAND_MAX/2) {
		/* recycle existing block */
		n = nodes[idx];
	} else if (rand() > RAND_MAX/2) {
		/* alloc new block */
		n = alloc();
		assign(&n->l, mk());
		assign(&n->r, mk());
	} else {
		/* no block */
		n = NULL;
	}

	assign(&nodes[idx], n);

	return n;
}

int main()
{
	int i;
	long base_definite, base_dubious, base_reachable, base_suppressed;
	long definite, dubious, reachable, suppressed;
        int total;

        /* we require these longs to have same size as a machine word */
        assert(sizeof(long) == sizeof(void*));

	/* get a baseline in case the runtime allocated some memory */
	VALGRIND_DO_LEAK_CHECK;
	base_definite = base_dubious = base_reachable = base_suppressed = 0;
	VALGRIND_COUNT_LEAKS(base_definite, base_dubious, 
			     base_reachable, base_suppressed);

	for(i = 0; i < ITER; i++) {
		mk();

		if ((i % (ITER/10)) == 0) {
			if (0)
				printf("%d living blocks, %d bytes\n",
				       blocks, bytes);
			VALGRIND_DO_LEAK_CHECK;
		}
	}
	
	/* "free all memory" */
	for(i = 0; i < N; i++)
		assign(&nodes[i], NULL);


	if (0)
		printf("FINISHED: %d living blocks, %d bytes\n",
		       blocks, bytes);

	VALGRIND_DO_LEAK_CHECK;

	/* Shouldn't be necessary, but COUNT_LEAKS doesn't define its
	   result values */
	definite = dubious = reachable = suppressed = 0;
	VALGRIND_COUNT_LEAKS(definite, dubious, reachable, suppressed);

	definite   -= base_definite;
	dubious    -= base_dubious;
	reachable  -= base_reachable;
	suppressed -= base_suppressed;

	total = definite+dubious+reachable+suppressed;

	if (0)
		printf("leaks: definite %d, dubious %d, reachable %d, suppressed %d = %d\n",
		       (int)definite, (int)dubious, (int)reachable, (int)suppressed, total);

	if (reachable != 0)
		printf("FAILED: I freed everything, "
		       "but there's still %d bytes reachable\n", 
		       (int)reachable);
	else if (total != bytes)
		printf("FAILED: I count %d bytes, leakcheck says %d\n",
		       bytes, total);
	else
		printf("PASS\n");
	return 0;
}
