From: David Given Date: Thu, 19 Jan 2017 22:21:15 +0000 (+0100) Subject: Fix a horrible bug where removing edges wouldn't work; add the ability to X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=4b03d2993cb8ccbdb68769e6232a97ee10baac0f;p=ack.git Fix a horrible bug where removing edges wouldn't work; add the ability to iterate over a vertex's neighbours; add the ability to merge vertices. --- diff --git a/modules/src/data/bigraph.c b/modules/src/data/bigraph.c index 7f24c9468..7f44ee07a 100644 --- a/modules/src/data/bigraph.c +++ b/modules/src/data/bigraph.c @@ -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; +} diff --git a/modules/src/data/bigraph.h b/modules/src/data/bigraph.h index e33762bce..e57dad0e1 100644 --- a/modules/src/data/bigraph.h +++ b/modules/src/data/bigraph.h @@ -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