package net.certware.argument.sfp.util; import java.util.Vector; import net.certware.argument.sfp.semiFormalProof.Justification; import net.certware.argument.sfp.semiFormalProof.Statement; import net.certware.core.ui.log.CertWareLog; /** * Sparse multi-graph. * @author nachi, original for TTU applications * @author mrb, migration to Eclipse for CertWare; refactored to eliminate static references * @since 1.0.3 */ public class Graph { /** vertex count */ private int vertexCount; /** edge count */ private int edgeCount; /** whether the graph is directed */ private boolean digraph; /** adjacency vector */ Vector<Vector<?>> adjacency; // TODO is there a reason statement identifiers have to be integers? /** * Sets graph vertex count to argument; sets edge count to zero. Allocates * array vector to vertex count plus one. * @param vc vertex count * @param flag true if digraph */ public Graph(int vc, boolean flag) { vertexCount = vc; edgeCount = 0; digraph = flag; allocateElements(vc + 1); } private void allocateElements(int n) { adjacency = new Vector<Vector<?>>(); for (int i = 0; i < n; i++) adjacency.addElement(new Vector<Object>()); } @SuppressWarnings("unchecked") public void addArc(int i, int j) { ((Vector<Integer>) adjacency.elementAt(i)).addElement(new Integer(j)); } public Vector<?> neighbors(int i) { return (Vector<?>) ((Vector<?>) adjacency.elementAt(i)).clone(); } int getVertexCount() { return vertexCount; } int getEdgeCount() { return edgeCount; } boolean isDirected() { return digraph; } /** * Insert statement. * Traps arc addition exceptions and writes to CertWare log. * @param s statement * @throws {@code ArrayIndexOutOfBoundsException} on statement identifiers * @throws {@code NumberFormatException} on identifier translation from string, or on entailment tail null */ public void insert(Statement s) throws ArrayIndexOutOfBoundsException, NumberFormatException { // check incoming if ( s == null || s.getJustification() == null ) { return; } // statement identifier int statementNumber = Integer.parseInt( s.getId() ); // get the statement's justification list, if numerical references // add an arc from the statement node to the justification reference number // catches number format exceptions, logs them, moves on for ( Justification j : s.getJustification().getJustifications() ) { // skip assertions /* if ( j.getAssertion() != null && j.getAssertion().getText().isEmpty() == false) continue; */ // reference numerals same line if ( j.getNumeral() != null && j.getNumeral().isEmpty() == false ) { int n = Integer.parseInt( j.getNumeral() ); addArc(statementNumber, n); } // entailment tail if ( j.getEntailment() != null ) { String tail = j.getEntailment().getTail(); if ( tail != null ) { int n = Integer.parseInt( tail ); addArc(statementNumber, n); } else { String message = String.format("%s %s", "Entailment tail found null for statement", s.getStatement() ); CertWareLog.logWarning(message); throw new NumberFormatException(message); } } } // for /* } catch (Exception e) { CertWareLog.logError( String.format("Graph insertion error for statement %s", s.getStatement() ), e); edgeInsertionClean = false; } */ } /** * Whether the edge insertions were clean, without warnings or errors. * @return {@code edgeInsertionClean} flag value, true if clean */ /* public boolean isClean() { return edgeInsertionClean; } */ /** * Private counters for DFS search * @author nachi */ private class CountPair { int count1, count2; /** * Increment count 1. * @return count 1 incremented by 1 */ int inc1() { return ++count1; } /** * Increment count 2. * @return count 2 incremented by 1 */ int inc2() { return ++count2; } }; /** * Depth-first search on graph execution. * @param v vertex to start * @param preOrder pre-order array * @param postOrder post-order array * @param countPair counters */ private void doDFS(int v, int[] preOrder, int[] postOrder, CountPair countPair) { preOrder[v] = countPair.inc1(); Vector<?> neighbors = neighbors(v); for (int i = 0; i < neighbors.size(); i++) { int u = ((Integer) neighbors.elementAt(i)).intValue(); try { if (preOrder[u] == 0) // Have we not seen vertex u before? { doDFS(u, preOrder, postOrder, countPair); } } catch( ArrayIndexOutOfBoundsException ae ) { // ignore, validation catches invalid statement numbers // TODO change pre order and post order to lists rather than arrays } } postOrder[v] = countPair.inc2(); return; } /** * Depth-first search on graph, setup and execute. * @param v starting vertex * @param preOrder preorder array * @param postOrder post-order array * @return post-order array value at vertex */ public int DFS(int v, int[] preOrder, int[] postOrder) { int n = getVertexCount(); for (int i = 0; i < n; i++) { preOrder[i] = postOrder[i] = 0; } // returns number of nodes reached CountPair cnt = new CountPair(); doDFS(v, preOrder, postOrder, cnt); return postOrder[v]; } /** * Whether the graph is acyclic. * Sets incoming message to null, but if cycle found loads it with message identifying cycle. * See {@code SemiFormalProofJavaValidator} for companion marker generation for validation. * @return message location if cycle found, null if no cycles */ public String isAcyclic() { int vertexCount = getVertexCount(); int[] preOrder = new int[vertexCount]; int[] postOrder = new int[vertexCount]; boolean[] span = new boolean[vertexCount]; String message = null; for (int i = 1; i < vertexCount; i++) { // check if any vertex is on a cycle if (span[i]) { continue; // try next component } int count = DFS(i, preOrder, postOrder); for (int j = 1; j < vertexCount; j++) { if (preOrder[j] > 0) { span[j] = true; } else { continue; } Vector<?> neighbors = neighbors(j); for (int k = 0; k < neighbors.size(); k++) { int u = ((Integer) neighbors.elementAt(k)).intValue(); try { if (postOrder[u] >= postOrder[j]) { message = String.format("%s %d and %d", "A cycle exists between proof statements", postOrder[u], postOrder[j]); return message; } } catch ( ArrayIndexOutOfBoundsException obe) { // ignore, move on } // note: PostOrder[u] > 0 since u is reachable from j } // for } if (count == vertexCount) { break; // all vertices spanned } } return message; } /** * Insert object. * @param infObj */ /* void insert(Inference infObj) { try { int v = infObj.number; for (int i = 0; i < infObj.arrayofnumbers.length; i++) { int w = infObj.arrayofnumbers[i]; addArc(v, w); } if (infObj.entailmentHead.length > 0) { addArc(v, infObj.entailmentTail); } } catch (Exception e) { // System.out.println("Exception" + e.getMessage()); CertWareLog.logError("Argument graph validation", e); } } */ }