--- /dev/null
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <assert.h>
+#include "bigraph.h"
+
+struct edgenode
+{
+ struct edgenode* next;
+ struct vertex* this;
+ struct vertex* other;
+};
+
+struct vertex
+{
+ void* data;
+ struct edgenode* connections;
+ int degree;
+};
+
+static uint32_t edge_hash_function(void* key)
+{
+ struct edgenode* en = key;
+ /* This will always return the same value, even if the two endpoints are swapped. */
+ return standard_pointer_hash_function(en->this) ^ standard_pointer_hash_function(en->other);
+}
+
+static bool edge_comparison_function(void* key1, void* key2)
+{
+ struct edgenode* en1 = key1;
+ struct edgenode* en2 = key2;
+
+ return ((en1->this == en2->this) && (en1->other == en2->other))
+ || ((en1->this == en2->other) && (en1->other == en2->this));
+}
+
+static void lazy_init(struct graph* g)
+{
+ g->edges.hashfunction = edge_hash_function;
+ g->edges.cmpfunction = edge_comparison_function;
+}
+
+void graph_reset(struct graph* g)
+{
+ lazy_init(g);
+
+ for (;;)
+ {
+ struct vertex* vertex = hashtable_pop(&g->vertices);
+ if (!vertex)
+ return;
+
+ while (vertex->connections)
+ {
+ struct edgenode* next = vertex->connections->next;
+ free(vertex->connections);
+ vertex->connections = next;
+ }
+
+ free(vertex);
+ }
+}
+
+bool graph_contains_vertex(struct graph* g, void* data)
+{
+ lazy_init(g);
+
+ return hashtable_contains(&g->vertices, data);
+}
+
+static struct vertex* find_or_add_vertex(struct graph* g, void* data)
+{
+ struct vertex* vertex;
+
+ lazy_init(g);
+
+ vertex = hashtable_get(&g->vertices, data);
+ if (!vertex)
+ {
+ vertex = calloc(1, sizeof(struct vertex));
+ vertex->data = data;
+ hashtable_put(&g->vertices, data, vertex);
+ }
+
+ return vertex;
+}
+
+static struct edgenode** find_edgep(struct vertex* v1, struct vertex* v2)
+{
+ struct edgenode** ep = &v1->connections;
+
+ while (*ep)
+ {
+ if ((*ep)->other == v2)
+ return ep;
+ ep = &(*ep)->next;
+ }
+
+ return ep;
+}
+
+static struct edgenode* add_edge(struct vertex* v1, struct vertex* v2)
+{
+ struct edgenode** ep = find_edgep(v1, v2);
+
+ if (!*ep)
+ {
+ *ep = calloc(1, sizeof(struct edgenode));
+ (*ep)->this = v1;
+ (*ep)->other = v2;
+ v1->degree++;
+ }
+
+ return *ep;
+}
+
+void graph_add_edge(struct graph* g, void* data1, void* data2)
+{
+ struct vertex* v1 = find_or_add_vertex(g, data1);
+ struct vertex* v2 = find_or_add_vertex(g, data2);
+ struct edgenode* e;
+
+ add_edge(v1, v2);
+ e = add_edge(v2, v1);
+
+ hashtable_put(&g->edges, e, e);
+}
+
+static struct edgenode* remove_edge(struct vertex* v1, struct vertex* v2)
+{
+ struct edgenode** ep = find_edgep(v1, v2);
+
+ if (*ep)
+ {
+ struct edgenode* old = *ep;
+ *ep = (*ep)->next;
+ v1->degree--;
+ return old;
+ }
+
+ return NULL;
+}
+
+void graph_remove_edge(struct graph* g, void* data1, void* data2)
+{
+ struct vertex* v1 = find_or_add_vertex(g, data1);
+ struct vertex* v2 = find_or_add_vertex(g, data2);
+ struct edgenode* e1 = remove_edge(v1, v2);
+ struct edgenode* e2 = remove_edge(v2, v1);
+
+ assert(!e1 == !e2);
+
+ if (e1)
+ {
+ /* e1 is a template; the actual object in the hashtable might actually
+ * be e2 (as they compare the same). So, remove from the hashtable
+ * before freeing anything. */
+ hashtable_remove(&g->edges, e1);
+ free(e1);
+ }
+ if (e2)
+ free(e2);
+}
+
+void graph_add_vertex(struct graph* g, void* data)
+{
+ find_or_add_vertex(g, data);
+}
+
+void graph_remove_vertex(struct graph* g, void* data)
+{
+ struct vertex* vertex;
+
+ lazy_init(g);
+ vertex = hashtable_get(&g->vertices, data);
+ if (!vertex)
+ return;
+
+ while (vertex->connections)
+ {
+ struct edgenode* next = vertex->connections->next;
+ struct edgenode* e = remove_edge(vertex->connections->other, vertex);
+ hashtable_remove(&g->edges, vertex->connections);
+ free(e);
+ free(vertex->connections);
+ vertex->connections = next;
+ }
+
+ hashtable_remove(&g->vertices, data);
+}
+
+int graph_get_vertex_degree(struct graph* g, void* data)
+{
+ struct vertex* vertex;
+
+ lazy_init(g);
+ vertex = hashtable_get(&g->vertices, data);
+ assert(vertex);
+ return vertex->degree;
+}
+
+void* graph_next_vertex(struct graph* g, struct vertex_iterator* it)
+{
+ struct vertex* vertex = hashtable_next(&g->vertices, &it->hit);
+ if (vertex)
+ {
+ it->data = vertex->data;
+ return vertex->data;
+ }
+ else
+ return NULL;
+}
+
+bool graph_next_edge(struct graph* g, struct edge_iterator* it)
+{
+ struct edgenode* e = hashtable_next(&g->edges, &it->hit);
+ if (e)
+ {
+ it->left = e->this->data;
+ it->right = e->other->data;
+ return true;
+ }
+ else
+ {
+ it->left = it->right = NULL;
+ return false;
+ }
+}
--- /dev/null
+#ifndef BIGRAPH_H
+#define BIGRAPH_H
+
+#include "hashtable.h"
+
+/* A bidirectional graph with node addition and removal capabilities. */
+
+struct graph
+{
+ struct hashtable vertices;
+ struct hashtable edges;
+};
+
+struct vertex_iterator
+{
+ /* Public */
+ void* data;
+
+ /* Private */
+ struct hashtable_iterator hit;
+};
+
+struct edge_iterator
+{
+ /* Public */
+ void* left;
+ void* right;
+
+ /* Private */
+ struct hashtable_iterator hit;
+};
+
+extern void graph_reset(struct graph* g);
+
+extern bool graph_contains_vertex(struct graph* g, void* data);
+extern void graph_add_vertex(struct graph* g, void* data);
+extern void graph_remove_vertex(struct graph* g, void* data);
+extern void graph_add_edge(struct graph* g, void* data1, void* data2);
+extern void graph_remove_edge(struct graph* g, void* data1, void* data2);
+extern int graph_get_vertex_degree(struct graph* g, void* data);
+
+extern void* graph_next_vertex(struct graph* g, struct vertex_iterator* it);
+extern bool graph_next_edge(struct graph* g, struct edge_iterator* it);
+
+#endif
--- /dev/null
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include "hashtable.h"
+
+struct hashnode
+{
+ struct hashnode* next;
+ void* key;
+ void* data;
+};
+
+uint32_t standard_pointer_hash_function(void* key)
+{
+ /* Basile-Starynkevitch pointer hash */
+ uintptr_t ptr = (uintptr_t) key;
+ return (uint32_t) ((13 * ptr) ^ (ptr >> 15));
+}
+
+bool standard_pointer_comparison_function(void* key1, void* key2)
+{
+ return (key1 == key2);
+}
+
+static void lazy_init(struct hashtable* ht)
+{
+ if (!ht->num_buckets)
+ ht->num_buckets = 37;
+
+ if (!ht->hashfunction)
+ ht->hashfunction = standard_pointer_hash_function;
+
+ if (!ht->cmpfunction)
+ ht->cmpfunction = standard_pointer_comparison_function;
+
+ if (!ht->buckets)
+ ht->buckets = calloc(ht->num_buckets, sizeof(struct hashnode*));
+}
+
+void hashtable_reset(struct hashtable* ht)
+{
+ while (ht->size)
+ hashtable_pop(ht);
+
+ free(ht->buckets);
+ ht->buckets = NULL;
+}
+
+static struct hashnode** findnodep(struct hashtable* ht, void* key)
+{
+ uint32_t hash;
+ struct hashnode** hnp;
+
+ lazy_init(ht);
+ hash = ht->hashfunction(key) % ht->num_buckets;
+ hnp = &ht->buckets[hash];
+
+ while (*hnp && !ht->cmpfunction((*hnp)->key, key))
+ hnp = &(*hnp)->next;
+
+ return hnp;
+}
+
+void* hashtable_put(struct hashtable* ht, void* key, void* data)
+{
+ void* olddata;
+ struct hashnode** hnp = findnodep(ht, key);
+ if (!*hnp)
+ {
+ *hnp = calloc(1, sizeof(struct hashnode));
+ ht->size++;
+ }
+
+ olddata = (*hnp)->data;
+ (*hnp)->key = key;
+ (*hnp)->data = data;
+ return olddata;
+}
+
+void* hashtable_get(struct hashtable* ht, void* key)
+{
+ struct hashnode** hnp = findnodep(ht, key);
+ if (*hnp)
+ return (*hnp)->data;
+ return NULL;
+}
+
+bool hashtable_contains(struct hashtable* ht, void* key)
+{
+ return *findnodep(ht, key);
+}
+
+bool hashtable_remove(struct hashtable* ht, void* key)
+{
+ struct hashnode** hnp = findnodep(ht, key);
+ if (*hnp)
+ {
+ struct hashnode* hn = *hnp;
+ *hnp = hn->next;
+ free(hn);
+ ht->size--;
+ return true;
+ }
+
+ return false;
+}
+
+void* hashtable_pop(struct hashtable* ht)
+{
+ int i;
+
+ if (ht->size == 0)
+ return NULL;
+
+ lazy_init(ht);
+ for (i=0; i<ht->num_buckets; i++)
+ {
+ struct hashnode** hnp = &ht->buckets[i];
+ if (*hnp)
+ {
+ struct hashnode* hn = *hnp;
+ void* data = hn->data;
+ *hnp = hn->next;
+ free(hn);
+ ht->size--;
+ return data;
+ }
+ }
+
+ return NULL;
+}
+
+void* hashtable_next(struct hashtable* ht, struct hashtable_iterator* it)
+{
+ while (!it->node)
+ {
+ if (it->bucket == ht->num_buckets)
+ {
+ it->data = NULL;
+ it->bucket = 0;
+ it->node = NULL;
+ return NULL;
+ }
+
+ it->node = ht->buckets[it->bucket];
+ if (it->node)
+ break;
+
+ it->bucket++;
+ }
+
+ it->data = it->node->data;
+ it->node = it->node->next;
+ if (!it->node)
+ it->bucket++;
+
+ return it->data;
+}
--- /dev/null
+#ifndef HASHTABLE_H
+#define HASHTABLE_H
+
+/* A simple, autoresizing hash table. */
+
+typedef uint32_t hashfunction_t(void* key);
+typedef bool cmpfunction_t(void* key1, void* key2);
+
+extern uint32_t standard_pointer_hash_function(void* key);
+extern bool standard_pointer_comparison_function(void* key1, void* key2);
+
+struct hashtable
+{
+ unsigned int num_buckets;
+ hashfunction_t* hashfunction;
+ cmpfunction_t* cmpfunction;
+ struct hashnode** buckets;
+ int size;
+};
+
+struct hashtable_iterator
+{
+ /* Public */
+ void* data;
+
+ /* Private */
+ int bucket;
+ struct hashnode* node;
+};
+
+extern void hashtable_reset(struct hashtable* ht);
+
+extern void* hashtable_put(struct hashtable* ht, void* key, void* data);
+extern void* hashtable_get(struct hashtable* ht, void* key);
+extern bool hashtable_contains(struct hashtable* ht, void* key);
+extern bool hashtable_remove(struct hashtable* ht, void* key);
+extern void* hashtable_pop(struct hashtable* ht);
+extern void* hashtable_next(struct hashtable* ht, struct hashtable_iterator* it);
+
+#endif