summaryrefslogtreecommitdiffstats
path: root/pyanaconda/storage/tsort.py
diff options
context:
space:
mode:
Diffstat (limited to 'pyanaconda/storage/tsort.py')
-rw-r--r--pyanaconda/storage/tsort.py105
1 files changed, 105 insertions, 0 deletions
diff --git a/pyanaconda/storage/tsort.py b/pyanaconda/storage/tsort.py
new file mode 100644
index 000000000..52a871f56
--- /dev/null
+++ b/pyanaconda/storage/tsort.py
@@ -0,0 +1,105 @@
+#!/usr/bin/python
+# tsort.py
+# Topological sorting.
+#
+# Copyright (C) 2010 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions of
+# the GNU General Public License v.2, or (at your option) any later version.
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY expressed or implied, including the implied warranties 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. Any Red Hat trademarks that are incorporated in the
+# source code or documentation are not subject to the GNU General Public
+# License and may only be used or replicated with the express permission of
+# Red Hat, Inc.
+#
+# Red Hat Author(s): Dave Lehman <dlehman@redhat.com>
+#
+
+class CyclicGraphError(Exception):
+ pass
+
+def tsort(graph):
+ order = [] # sorted list of items
+
+ if not graph or not graph['items']:
+ return order
+
+ # determine which nodes have no incoming edges
+ roots = [n for n in graph['items'] if graph['incoming'][n] == 0]
+ if not roots:
+ raise CyclicGraphError("no root nodes")
+
+ visited = [] # list of nodes visited, for cycle detection
+ while roots:
+ # remove a root, add it to the order
+ root = roots.pop()
+ if root in visited:
+ raise CyclicGraphError("graph contains cycles")
+
+ visited.append(root)
+ i = graph['items'].index(root)
+ order.append(root)
+ # remove each edge from the root to another node
+ for (parent, child) in [e for e in graph['edges'] if e[0] == root]:
+ graph['incoming'][child] -= 1
+ graph['edges'].remove((parent, child))
+ # if destination node is now a root, add it to roots
+ if graph['incoming'][child] == 0:
+ roots.append(child)
+
+ if len(graph['items']) != len(visited):
+ raise CyclicGraphError("graph contains cycles")
+
+
+ return order
+
+def create_graph(items, edges):
+ """ Create a graph based on a list of items and a list of edges.
+
+ Arguments:
+
+ items - an iterable containing (hashable) items to sort
+ edges - an iterable containing (parent, child) edge pair tuples
+
+ Return Value:
+
+ The return value is a dictionary representing the directed graph.
+ It has three keys:
+
+ items is the same as the input argument of the same name
+ edges is the same as the input argument of the same name
+ incoming is a dict of incoming edge count hashed by item
+
+ """
+ graph = {'items': [], # the items to sort
+ 'edges': [], # partial order info: (parent, child) pairs
+ 'incoming': {}} # incoming edge count for each item
+
+ graph['items'] = items
+ graph['edges'] = edges
+ for item in items:
+ graph['incoming'][item] = 0
+
+ for (parent, child) in edges:
+ graph['incoming'][child] += 1
+
+ return graph
+
+
+if __name__ == "__main__":
+
+ items = [5, 2, 3, 4, 1]
+ edges = [(1, 2), (2, 4), (4, 5), (3, 2)]
+
+ print items
+ print edges
+
+ graph = create_graph(items, edges)
+ print tsort(graph)
+