Fix a horrible bug where removing edges wouldn't work; add the ability to
authorDavid Given <dg@cowlark.com>
Thu, 19 Jan 2017 22:21:15 +0000 (23:21 +0100)
committerDavid Given <dg@cowlark.com>
Thu, 19 Jan 2017 22:21:15 +0000 (23:21 +0100)
iterate over a vertex's neighbours; add the ability to merge vertices.

modules/src/data/bigraph.c
modules/src/data/bigraph.h

index 7f24c94..7f44ee0 100644 (file)
@@ -21,7 +21,8 @@ struct vertex
 static uint32_t edge_hash_function(void* key)
 {
     struct edge* edge = key;
-    /* This will always return the same value, even if the two endpoints are swapped. */
+    /* This will always return the same value for any given pair, even if the two endpoints
+     * are swapped. */
     return standard_pointer_hash_function(edge->v1) ^ standard_pointer_hash_function(edge->v2);
 }
 
@@ -40,7 +41,7 @@ static void lazy_init(struct graph* g)
     g->edges.table.cmpfunction = edge_comparison_function;
 }
 
-void graph_reset(struct graph* g)
+void graph_empty(struct graph* g)
 {
     lazy_init(g);
 
@@ -48,7 +49,7 @@ void graph_reset(struct graph* g)
     {
         struct vertex* vertex = hashtable_pop(&g->vertices);
         if (!vertex)
-            return;
+            break;
 
                set_reset(&vertex->edges);
         free(vertex);
@@ -57,10 +58,17 @@ void graph_reset(struct graph* g)
        for (;;)
        {
                struct edge* edge = set_pop(&g->edges);
+        if (!edge)
+            break;
                free(edge);
        }
 }
 
+void graph_reset(struct graph* g)
+{
+    graph_empty(g);
+}
+
 bool graph_contains_vertex(struct graph* g, void* data)
 {
     lazy_init(g);
@@ -79,9 +87,11 @@ static struct vertex* find_or_add_vertex(struct graph* g, void* data)
     {
         vertex = calloc(1, sizeof(struct vertex));
         vertex->data = data;
+        vertex->edges.table.hashfunction = edge_hash_function;
+        vertex->edges.table.cmpfunction = edge_comparison_function;
         hashtable_put(&g->vertices, data, vertex);
     }
-    
+
     return vertex;
 }
 
@@ -103,6 +113,9 @@ void graph_add_edge(struct graph* g, void* data1, void* data2)
        struct edge template;
        struct edge* e;
 
+    if (data1 == data2)
+        return;
+
     template.v1 = find_or_add_vertex(g, data1);
     template.v2 = find_or_add_vertex(g, data2);
        if (template.v1 == template.v2)
@@ -152,7 +165,7 @@ void graph_remove_vertex(struct graph* g, void* data)
     vertex = hashtable_get(&g->vertices, data);
     if (!vertex)
         return;
-    
+
        for (;;)
        {
                struct vertex* other;
@@ -174,6 +187,40 @@ void graph_remove_vertex(struct graph* g, void* data)
        free(vertex);
 }
 
+void graph_merge_vertices(struct graph* g, void* master, void* slave)
+{
+    struct vertex* masterVertex;
+    struct vertex* slaveVertex;
+
+    lazy_init(g);
+    masterVertex = hashtable_get(&g->vertices, master);
+    slaveVertex = hashtable_get(&g->vertices, slave);
+
+    if (!masterVertex || !slaveVertex)
+        return;
+
+    for (;;)
+    {
+        struct vertex* v1;
+        struct vertex* v2;
+        struct edge* e = set_get_any(&slaveVertex->edges);
+        if (!e)
+            break;
+
+        v1 = e->v1;
+        v2 = e->v2;
+
+        if (v1 == slaveVertex)
+            graph_add_edge(g, master, v2->data);
+        if (v2 == slaveVertex)
+            graph_add_edge(g, master, v1->data);
+        graph_remove_edge(g, v1->data, v2->data);
+    }
+
+    assert(slaveVertex->degree == 0);
+    graph_remove_vertex(g, slave);
+}
+
 int graph_get_vertex_degree(struct graph* g, void* data)
 {
     struct vertex* vertex;
@@ -211,3 +258,28 @@ bool graph_next_edge(struct graph* g, struct edge_iterator* it)
         return false;
     }
 }
+
+void* graph_next_neighbour(struct graph* g, void* origin, struct neighbour_iterator* it)
+{
+    struct edge* e;
+
+    lazy_init(g);
+    if (!it->vertex)
+        it->vertex = hashtable_get(&g->vertices, origin);
+    if (it->vertex)
+    {
+        e = set_next(&it->vertex->edges, &it->sit);
+        if (e)
+        {
+            if (e->v1 == it->vertex)
+                it->data = e->v2->data;
+            else
+                it->data = e->v1->data;
+            return it->data;
+        }
+    }
+
+    it->vertex = NULL;
+    it->data = NULL;
+    return it->data;
+}
index e33762b..e57dad0 100644 (file)
@@ -31,11 +31,23 @@ struct edge_iterator
     struct set_iterator sit;
 };
 
+struct neighbour_iterator
+{
+    /* Public */
+    void* data;
+
+    /* Private */
+    struct vertex* vertex;
+    struct set_iterator sit;
+};
+
+extern void graph_empty(struct graph* g);
 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_merge_vertices(struct graph* g, void* master, void* slave);
 extern bool graph_contains_edge(struct graph* g, void* data1, void* data2);
 extern void graph_add_edge(struct graph* g, void* data1, void* data2);
 extern void graph_remove_edge(struct graph* g, void* data1, void* data2);
@@ -43,5 +55,6 @@ 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);
+extern void* graph_next_neighbour(struct graph* g, void* origin, struct neighbour_iterator* it);
 
 #endif