38 g_hash_table_insert(by_id, (gpointer)
n->id,
n);
58 GHashTable *by_id = g_hash_table_new(g_str_hash, g_str_equal);
62 for(GList *it = g_list_first(input_nodes); it; it = g_list_next(it))
69 if(!canon->
tag && in->
tag) canon->
tag = g_strdup(in->
tag);
73 if(((!strcmp(canon->
tag,
"dst") && !strcmp(in->
tag,
"src"))
74 || (!strcmp(canon->
tag,
"src") && !strcmp(in->
tag,
"dst"))))
77 canon->
tag = g_strdup(
"dst+src");
79 else if(strcmp(canon->
tag,
"dst+src"))
82 canon->
tag = g_strdup(
"mixed");
88 for(GList *it = g_list_first(input_nodes); it; it = g_list_next(it))
95 for(GList *
p = g_list_first(in->
previous);
p;
p = g_list_next(
p))
103 gboolean found =
FALSE;
104 for(GList *q = g_list_first(self->
previous); q; q = g_list_next(q))
106 if(q->data == cpred) { found =
TRUE;
break; }
113 GHashTable *added = g_hash_table_new(g_str_hash, g_str_equal);
116 g_hash_table_destroy(by_id);
120 for(GList *it = g_list_first(input_nodes); it; it = g_list_next(it))
125 if(g_hash_table_contains(added, in->
id))
continue;
130 *out_nodes = g_list_append(*out_nodes, canon);
131 g_hash_table_add(added, (gpointer)canon->
id);
137 gpointer
key = NULL, val = NULL;
138 g_hash_table_iter_init(&iter, by_id);
139 while(g_hash_table_iter_next(&iter, &
key, &val))
141 const char *
id = (
const char *)
key;
143 if(!g_hash_table_contains(added,
id)) *out_nodes = g_list_append(*out_nodes, canon);
146 g_hash_table_destroy(added);
147 g_hash_table_destroy(by_id);
175 GList *lst = (GList *)g_hash_table_lookup(outgoing, from);
176 lst = g_list_prepend(lst, to);
177 g_hash_table_replace(outgoing, from, lst);
199 for(
const GList *it = dfs_stack; it; it = g_list_next(it))
203 cycle = g_list_prepend(cycle,
n);
206 return g_list_reverse(cycle);
210 GHashTable *outgoing,
221 fprintf(stderr,
"[toposort] Cycle detected visiting node '%s'\n",
n->id);
227 fprintf(stderr,
"[toposort] Already finished node '%s'\n",
n->id);
231 *dfs_count = *dfs_count + 1;
234 if(dfs_stack) *dfs_stack = g_list_prepend(*dfs_stack,
n);
238 GList *nbrs = (GList *)g_hash_table_lookup(outgoing,
n);
239 for(GList *it = nbrs; it; it = g_list_next(it))
245 if(dfs_stack && *dfs_stack) *dfs_stack = g_list_delete_link(*dfs_stack, *dfs_stack);
251 *result = g_list_prepend(*result,
n);
254 if(dfs_stack && *dfs_stack) *dfs_stack = g_list_delete_link(*dfs_stack, *dfs_stack);
262 if(cycle_out) *cycle_out = NULL;
265 fprintf(stderr,
"[toposort] Initial node list and constraints:\n");
266 for(GList *it = g_list_first(nodes); it; it = g_list_next(it))
269 fprintf(stderr,
"[toposort] node '%s'",
n->id);
270 if(
n->tag) fprintf(stderr,
" [%s]",
n->tag);
271 fprintf(stderr,
" (predecessors:");
272 for(GList *
p = g_list_first(
n->previous);
p;
p = g_list_next(
p))
275 fprintf(stderr,
" '%s'", pred->
id);
277 fprintf(stderr,
")\n");
281 fprintf(stderr,
"[toposort] Edges:\n");
282 for(GList *it = g_list_first(nodes); it; it = g_list_next(it))
285 for(GList *
p = g_list_first(
n->previous);
p;
p = g_list_next(
p))
288 fprintf(stderr,
"[toposort] '%s' -> '%s'\n", pred->
id,
n->id);
292 GHashTable *outgoing = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, NULL);
293 GHashTable *
color = g_hash_table_new(g_direct_hash, g_direct_equal);
296 if(outgoing) g_hash_table_destroy(outgoing);
302 for(GList *it = g_list_first(nodes); it; it = g_list_next(it))
307 if(!g_hash_table_contains(outgoing,
n)) g_hash_table_insert(outgoing,
n, NULL);
312 for(GList *it = g_list_first(nodes); it; it = g_list_next(it))
317 for(GList *
p = g_list_first(self->
previous);
p;
p = g_list_next(
p))
322 if(!g_hash_table_contains(outgoing, pred)) g_hash_table_insert(outgoing, pred, NULL);
323 if(!g_hash_table_contains(
color, pred))
331 GList *result = NULL;
333 GList *dfs_stack = NULL;
338 gpointer
key = NULL, val = NULL;
339 g_hash_table_iter_init(&iter, outgoing);
340 while(g_hash_table_iter_next(&iter, &
key, &val))
352 g_list_free(dfs_stack);
355 g_hash_table_destroy(outgoing);
356 g_hash_table_destroy(
color);
358 if(cycle_out) *cycle_out = cycle;
359 else if(cycle) g_list_free(cycle);
368 g_list_free(dfs_stack);
377 g_hash_table_destroy(outgoing);
378 g_hash_table_destroy(
color);
383 fprintf(stderr,
"[toposort] Solution order:\n");
385 for(GList *it = g_list_first(result); it; it = g_list_next(it), idx++)
388 fprintf(stderr,
"[toposort] %d: '%s'", idx,
n->id);
389 if(
n->tag) fprintf(stderr,
" [%s]",
n->tag);
390 fprintf(stderr,
"\n");
393 fprintf(stderr,
"[toposort] DFS visits performed: %d\n", dfs_count);
431 for(GList *it = g_list_first(nodes); it; it = g_list_next(it))
445 g_hash_table_iter_init(&iter, ht);
446 while(g_hash_table_iter_next(&iter, &
key, &val))
452 g_hash_table_destroy(ht);
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
static dt_digraph_node_t * _get_or_create_node(GHashTable *by_id, const char *id)
static void _dt_digraph_nodes_hashtable_free_full(GHashTable *ht, dt_node_user_data_destroy_t user_destroy)
static GList * _toposort_extract_cycle_from_stack(const GList *dfs_stack, const dt_digraph_node_t *gray)
static void dt_digraph_node_free_full(dt_digraph_node_t *node, dt_node_user_data_destroy_t user_destroy)
static void _dt_digraph_nodes_free_full(GList *nodes, dt_node_user_data_destroy_t user_destroy)
static gboolean _add_edge(GHashTable *outgoing, dt_digraph_node_t *from, dt_digraph_node_t *to)
void dt_digraph_cleanup_full(GList *nodes, GHashTable *node_ht, dt_node_user_data_destroy_t user_destroy)
Free a canonical graph (nodes, constraints, ids) in one call.
static gboolean _dfs_visit(dt_digraph_node_t *n, GHashTable *outgoing, GHashTable *color, GList **result, int *dfs_count, GList **dfs_stack, GList **cycle_out)
int flatten_nodes(GList *input_nodes, GList **out_nodes)
Canonicalize / merge duplicated nodes by id.
dt_digraph_node_t * dt_digraph_node_new(const char *id)
Allocate and initialize a new digraph node with the given id.
int topological_sort(GList *nodes, GList **sorted, GList **cycle_out)
Perform a topological sort using depth-first search (DFS).
Small directed-graph helper for constraint aggregation and topological sorting.
void(* dt_node_user_data_destroy_t)(void *data)
Optional destructor for node payloads.