------------------------------ graph.h ------------------------------

#ifndef _OLIO_GRAPH_H_
#define _OLIO_GRAPH_H_

/* simple graph data structure and algorithms for quick update, traversal, and acyclic test */

#include <inttypes.h>

typedef struct _olio_graph {
    /* directed edge connectivity is stored as a bitmap */
    uint8_t * connectivity;
    /* directed edge values between nodes as a 2D matrix */
    int32_t * matrix;
    /* tracking node color for certain graph alogrithms */
    uint8_t * node_color;
    uint16_t node_count;
} olio_graph;

/* type for graph traversal callback function */
typedef int(*olio_graph_traversal_cb)(olio_graph *, 
	uint16_t head, uint16_t tail,
	uint8_t color, void * user_data);

#ifdef __cplusplus
extern "C" {
#endif

int olio_graph_init(olio_graph *, uint16_t count);
void olio_graph_free(olio_graph *);
void olio_graph_reset(olio_graph *);
/* a depth first traversal of the graph, calling back for each node */
int olio_graph_dfs_traversal(olio_graph *, olio_graph_traversal_cb,
	void * user_data);
/* check if graph is acyclic */
int olio_graph_acyclic(olio_graph *);

#ifdef OLIO_DEBUG
void olio_graph_display(olio_graph *);
#endif

#ifdef __cplusplus
} /* extern C */
#endif

static inline void olio_graph_set_edge(olio_graph * g,
	uint16_t head, uint16_t tail, int32_t value)
{
    /* set bit for head->tail connectivity */
    g->connectivity[head * ((g->node_count + 0x7) >> 3) + (tail >> 3)]
	|= 1 << (tail & 0x7);
    /* store edge value */
    g->matrix[head * g->node_count + tail] = value;
}

static inline void olio_graph_set_edge_nondirected(olio_graph * g,
	uint16_t node1, uint16_t node2, int32_t value)
{
    olio_graph_set_edge(g, node1, node2, value);
    olio_graph_set_edge(g, node2, node1, value);
}

static inline void olio_graph_unset_edge(olio_graph * g,
	uint16_t head, uint16_t tail)
{
    /* clear bit for head->tail connectivity */
    g->connectivity[head * ((g->node_count + 0x7) >> 3) + (tail >> 3)]
	&= ~(1 << (tail & 0x7));
    /* reset edge value */
    g->matrix[head * g->node_count + tail] = 0;
}

static inline void olio_graph_unset_edge_nondirected(olio_graph * g,
	uint16_t node1, uint16_t node2)
{
    olio_graph_unset_edge(g, node1, node2);
    olio_graph_unset_edge(g, node2, node1);
}

static inline int olio_graph_is_edge_set(olio_graph * g,
	uint16_t head, uint16_t tail)
{
    /* test for head->tail connectivity */
    return (g->connectivity[head * ((g->node_count + 0x7) >> 3) + 
			    (tail >> 3)] & (1 << (tail & 0x7)));
}

static inline int32_t olio_graph_get_edge(olio_graph * g,
	uint16_t head, uint16_t tail)
{
    return (g->matrix[head * g->node_count + tail]);
}

#endif /* _OLIO_GRAPH_H_ */

------------------------------ graph.c ------------------------------

#include <string.h>

#include "graph.h"
#include "array.h"

int olio_graph_init(olio_graph * g, uint16_t count)
{
    g->node_count = count;
    g->node_color = (uint8_t *) malloc(sizeof(uint8_t) * count);
    /* connectivity bitmap: effectively a count-sized array, each entry
       having count number of bits. with 8 bits per byte, we only need to
       allocate count * (count/8 + 1) bytes. */
    g->connectivity = (uint8_t *) malloc(sizeof(uint8_t) * count *
					 ((count + 0x7) >> 3));
    /* a 2D array holding edge values */
    g->matrix = (int32_t *) malloc(sizeof(int32_t) * count * count);

    if (g->node_color == NULL || g->connectivity == NULL ||
	g->matrix == NULL) {
	olio_graph_free(g);
	return -1;
    }
    olio_graph_reset(g);
    return 0;
}

void olio_graph_free(olio_graph * g)
{
    if (g->node_color != NULL) free(g->node_color);
    if (g->connectivity != NULL) free(g->connectivity);
    if (g->matrix != NULL) free(g->matrix);
}

void olio_graph_reset(olio_graph * g)
{
    /* reset connectivity bitmap... */
    memset(g->connectivity, 0, sizeof(uint8_t) * g->node_count *
	   ((g->node_count + 0x7) >> 3));
    /* and the edge values */
    memset(g->matrix, 0, sizeof(int32_t) * g->node_count * g->node_count);
}

struct edge_pair {
    uint16_t start;
    uint16_t end;
};

/* traverse graph with depth-first search, calling "cb" with each edge we find.
   this is a stack-based, non-recursive implementation. further, the connectivity
   bitmap provides good cache coherency on the inner loop edge test. */
int olio_graph_dfs_traversal(olio_graph * g, olio_graph_traversal_cb cb,
			     void * user_data)
{
    OLIO_ARRAY_STACK(visit, struct edge_pair, 64);
    uint16_t i, j;
    struct edge_pair a, b;
    int rc = 0;

    /* reset node colors (used for tracking visit state) */
    memset(g->node_color, 0, sizeof(uint8_t) * g->node_count);
    
    for (i = 0; i < g->node_count; i++) {
        /* has node been visited? */
	if (g->node_color[i] == 0) { 
	    /* start a new edge stack */
	    a.start = 0xffff;
	    a.end = i;
	    olio_array_prepend(visit, &a, 1);

	    /* loop while we have edges on the stack to check... */
	    while (olio_array_length(visit) > 0) {
		struct edge_pair * t = 
		    (struct edge_pair *) 
		    olio_array_contents(visit);
		a.start = t->start;
		a.end = t->end;
		olio_array_remove(visit, 0, 1);
		/* mark this node as "visited from" */
		g->node_color[a.end] = 1;
		
		/* find nodes this node is connected to */
		for (j = 0; j < g->node_count; j++) {
		    /* don't go backwards */
		    if (j == a.start) continue;

		    if (olio_graph_is_edge_set(g, a.end, j)) {
			/* a connected node. have we seen it before? */
	    		if (g->node_color[j] == 0) {
			    /* no. add this edge to the stack */
			    b.start = a.end;
			    b.end = j;
			    olio_array_prepend(visit, &b, 1);
	    		}
			/* call callback with the edge */
		    	rc = cb(g, a.end, j, g->node_color[j], user_data);
		    	if (rc != 0) {
			    /* the callback aborted the traversal. */
			    goto end_dfs_traversal;
		    	}
			/* mark this node as "visited to" */
		    	g->node_color[j] = 2;
		    }
	   	}
	    }
	}
    }
    
end_dfs_traversal:
    olio_array_free(visit);
    
    return rc;
}

static int _acyclic_test(olio_graph * g, uint16_t head, uint16_t tail, 
			 uint8_t color, void * user_data)
{
    /* if we find an edge that points to a node we've already seen before,
       then the graph is cyclic, so we abort the traversal with the status. */
    if (color != 0) return 1;
    return 0;
}

/* check if the graph is acyclic by doing a dfs traversal with the callback above. */
int olio_graph_acyclic(olio_graph * g)
{
    int rc = olio_graph_dfs_traversal(g, _acyclic_test, NULL);
    return (rc == 0);
}

#ifdef OLIO_DEBUG

#include <stdio.h>

void olio_graph_display(olio_graph * g)
{
    uint16_t i, j;

    /* print a formatted matrix of the graph's connectivity/edge values */
    for (i = 0; i < g->node_count; i++) {
	for (j = 0; j < g->node_count; j++) {
	    if (olio_graph_is_edge_set(g, i, j)) {
		fprintf(stderr, "% 3li ", olio_graph_get_edge(g, i, j));
	    } else {
		fprintf(stderr, "  . ");
	    }
	}
	fprintf(stderr, "\n");
    }
}

#endif /* OLIO_DEBUG */