/* * glade-tsort.c: a topological sorting algorithm implementation * * Copyright (C) 2013 Juan Pablo Ugarte. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * Authors: * Juan Pablo Ugarte */ #include "glade-tsort.h" /** * _node_edge_prepend: * @node: a _NodeEdge pointer or NULL * @predecessor: * @successor: * * Adds a new node with the values @predecessor and @successor to the start of * @node list. * * Returns: the new start of the node list. */ GList * _node_edge_prepend (GList *list, gpointer predecessor, gpointer successor) { _NodeEdge *edge = g_slice_new (_NodeEdge); edge->predecessor = predecessor; edge->successor = successor; return g_list_prepend (list, edge); } static void _node_edge_free (gpointer data) { g_slice_free (_NodeEdge, data); } void _node_edge_list_free (GList *list) { g_list_free_full (list, _node_edge_free); } static inline void tsort_remove_non_start_nodes (GList **nodes, GList *edges) { GList *l; for (l = edges; l; l = g_list_next (l)) { _NodeEdge *edge = l->data; *nodes = g_list_remove (*nodes, edge->successor); } } static inline gboolean tsort_node_has_no_incoming_edge (gpointer node, GList *edges) { GList *l; for (l = edges; l; l = g_list_next (l)) { _NodeEdge *edge = l->data; if (node == edge->successor) return FALSE; } return TRUE; } /** * _glade_tsort: * @nodes: list of pointers to sort * @edges: pointer to the list of #_NodeEdge that conform the dependency * graph of @nodes. * * Topological sorting implementation. * After calling this funtion only graph cycles (circular dependencies) are left * in @edges list. So if @edges is NULL it means the returned list has all the * elements topologically sorted if not it means there are at least one * circular dependency. * * L ← Empty list that will contain the sorted elements * S ← Set of all nodes with no incoming edges * while S is non-empty do * remove a node n from S * insert n into L * for each node m with an edge e from n to m do * remove edge e from the graph * if m has no other incoming edges then * insert m into S * return L (a topologically sorted order if graph has no edges) * * see: http://en.wikipedia.org/wiki/Topological_sorting * * Returns: a new list sorted by dependency including nodes only present in @edges */ GList * _glade_tsort (GList **nodes, GList **edges) { GList *sorted_nodes; /* L ← Empty list that will contain the sorted elements */ sorted_nodes = NULL; /* S ← Set of all nodes with no incoming edges */ tsort_remove_non_start_nodes (nodes, *edges); /* while S is non-empty do */ while (*nodes) { GList *l, *next; gpointer n; /* remove a node n from S */ n = (*nodes)->data; *nodes = g_list_delete_link (*nodes, *nodes); /* insert n into L */ /*if (!glade_widget_get_parent (n)) this would be a specific optimization */ sorted_nodes = g_list_prepend (sorted_nodes, n); /* for each node m ... */ for (l = *edges; l; l = next) { _NodeEdge *edge = l->data; next = g_list_next (l); /* ... with an edge e from n to m do */ if (edge->predecessor == n) { /* remove edge e from the graph */ *edges = g_list_delete_link (*edges, l); /* if m has no other incoming edges then */ if (tsort_node_has_no_incoming_edge (edge->successor, *edges)) /* insert m into S */ *nodes = g_list_prepend (*nodes, edge->successor); g_slice_free (_NodeEdge, edge); } } } /* if graph has edges then return error (graph has at least one cycle) */ #if 0 /* We rather not return NULL, caller must check if edge */ if (*edges) { g_list_free (sorted_nodes); _node_edge_list_free (*edges); *edges = NULL; return NULL; } #endif /* return L (a topologically sorted order if edge is NULL) */ return g_list_reverse (sorted_nodes); }