/** * Copyright (c) 2009-2011, The HATS Consortium. All rights reserved. * This file is licensed under the terms of the Modified BSD License. */ package abs.frontend.delta; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import com.google.common.collect.ArrayTable; import com.google.common.collect.Table; /* Sorting object that computes a list of elements sorted according to a given partial order * Usage: * 1. instantiate, giving all elements to be sorted * 2. define partial order by repeatedly calling addEdge(e1, e2), where e1 > e2 * 3. call sort() * 4. obtain a valid order with getPreferredOrder() or getAnOrder() */ public class TopologicalSorting<T> { private final Set<T> nodes; private final Table<T, T, Boolean> incidence; private List<Set<T>> partition; private List<T> preferredOrder; private boolean isSorted = false; public TopologicalSorting(Set<T> nodes) { this.nodes = nodes; if (nodes.size() == 0) { partition = Collections.emptyList(); preferredOrder = Collections.emptyList(); incidence = null; return; } incidence = ArrayTable.create(nodes, nodes); for (T cn : nodes) for (T rn : nodes) incidence.put(cn, rn, false); partition = new ArrayList<Set<T>>(); } public void addEdge(T high, T low) throws DeltaModellingException { if (incidence == null) throw new DeltaModellingException("Sorting: nodes not found [" + high.toString() + "; " + low.toString() + "] -- graph is empty"); if (! incidence.containsColumn(high)) throw new DeltaModellingException("Sorting: node not found [" + high.toString() + "]"); if (! incidence.containsRow(low)) throw new DeltaModellingException("Sorting: node not found [" + low.toString() + "]"); incidence.put(high, low, true); isSorted = false; } public void sort() throws DeltaModellingException { boolean rootNode; Set<T> nodes = new HashSet<T>(this.nodes); int currentSet = 0; while (nodes.size() > 0) { partition.add(new HashSet<T>()); for (T node : nodes) { rootNode = true; for (T cn : nodes) { if (incidence.get(cn, node) == true) { rootNode = false; break; // not a root node } } if (rootNode) partition.get(currentSet).add(node); } // no nodes in set means there is a cycle among the remaining nodes if (partition.get(currentSet).isEmpty()) throw new DeltaModellingException("Sorting: cycle detected among the following nodes: " + nodes.toString()); // for all nodes in set: remove these nodes from node set for (T node : partition.get(currentSet)) nodes.remove(node); currentSet++; } isSorted = true; } /** * Get a single, valid order * * TODO: eventually this should compute an implication-determined order (cf. Damiani and Schaefer 2012), * which yields a PFGT with a minimal number of nodes * * @return A (possibly empty) list of elements */ public List<T> getPreferredOrder() { if (preferredOrder != null) // only compute once return preferredOrder; checkSorted(); preferredOrder = new ArrayList<T>(nodes.size()); for (Set<T> set : partition) preferredOrder.addAll(set); return preferredOrder; } /** * Returns a single, valid order * * @return A (possibly empty) list of elements */ public List<T> getAnOrder() { return getPreferredOrder(); } /** * The delta partition is an ordered list of sets of deltas. All deltas in a * certain set have the same precedence, that is, they can be applied in any order. * The partition is initially an empty list and is computed by calling sort(). * * @return A (possibly empty) list of sets. A set cannot be empty. */ public List<Set<T>> getPartition() { checkSorted(); return partition; } /* * Make sure we called sort() before we access the results */ private void checkSorted() { if (! isSorted) throw new DeltaModellingException("Set has not yet been sorted."); } }