/* Copyright or (C) or Copr. GET / ENST, Telecom-Paris, Ludovic Apvrille * * ludovic.apvrille AT enst.fr * * This software is a computer program whose purpose is to allow the * edition of TURTLE analysis, design and deployment diagrams, to * allow the generation of RT-LOTOS or Java code from this diagram, * and at last to allow the analysis of formal validation traces * obtained from external tools, e.g. RTL from LAAS-CNRS and CADP * from INRIA Rhone-Alpes. * * This software is governed by the CeCILL license under French law and * abiding by the rules of distribution of free software. You can use, * modify and/ or redistribute the software under the terms of the CeCILL * license as circulated by CEA, CNRS and INRIA at the following URL * "http://www.cecill.info". * * As a counterpart to the access to the source code and rights to copy, * modify and redistribute granted by the license, users are provided only * with a limited warranty and the software's author, the holder of the * economic rights, and the successive licensors have only limited * liability. * * In this respect, the user's attention is drawn to the risks associated * with loading, using, modifying and/or developing or reproducing the * software by the user in light of its specific status of free software, * that may mean that it is complicated to manipulate, and that also * therefore means that it is reserved for developers and experienced * professionals having in-depth computer knowledge. Users are therefore * encouraged to load and test the software's suitability as regards their * requirements in conditions enabling the security of their systems and/or * data to be ensured and, more generally, to use and operate it in the * same conditions as regards security. * * The fact that you are presently reading this means that you have had * knowledge of the CeCILL license and that you accept its terms. */ package graph; import myutil.Conversion; import myutil.DijkstraState; import myutil.GraphAlgorithms; import myutil.TraceManager; import java.io.BufferedReader; import java.io.StringReader; import java.util.*; import rationals.*; import rationals.transformations.*; /** * Class AUTGraph * Creation : 16/09/2004 * * @version 1.0 16/09/2004 * * @author Ludovic APVRILLE */ public class AUTGraph implements myutil.Graph { // Graph is defined with this only protected ArrayList<AUTTransition> transitions; protected int nbState; // Can be built with computeStates() protected ArrayList<AUTState> states; protected BufferedReader br; protected long nbTransition; protected int percentage; protected boolean[] hasExitTransition; protected boolean[] hasEntryTransition; protected boolean statesComputed; public AUTGraph() { transitions = new ArrayList<>(); //buildGraph(dataAUT); } public AUTGraph(ArrayList<AUTState> _st, ArrayList<AUTTransition> _tr) { states = _st; transitions = _tr; nbState = states.size(); statesComputed = true; } public void stopBuildGraph() { br = null; } public int getPercentage() { return percentage; } public void buildGraph(String data) { if (data == null) { return; } StringReader sr = new StringReader(data); br = new BufferedReader(sr); String s, s1, s2; int index1; //int origin, destination; AUTTransition at; percentage = 0; int cpt, mod; /* read header */ //TraceManager.addDev("Building graph"); try { while ((s = br.readLine()) != null) { index1 = s.indexOf("des"); //TraceManager.addDev("Searching for des"); if (index1 == 0) { //TraceManager.addDev("des found"); s1 = s.substring(s.indexOf(',') + 1, s.length()); s1 = s1.substring(0, s1.indexOf(',')); s1 = Conversion.removeFirstSpaces(s1); nbTransition = new Integer(s1).intValue(); s2 = s.substring(s.indexOf(",") + 1, s.indexOf(')')); s2 = s2.substring(s2.indexOf(",") + 1, s2.length()); s2 = Conversion.removeFirstSpaces(s2); nbState = new Integer(s2).intValue(); break; } } } catch (Exception e) { TraceManager.addDev("Exception when reading graph information: " + e.getMessage() + "\n"); return; } String[] array; hasExitTransition = new boolean[nbState]; hasEntryTransition = new boolean[nbState]; TraceManager.addDev("NbState=" + nbState + " NbTransition=" + nbTransition + "\n"); /*for(cpt=0; cpt<nbState; cpt ++) { hasExitTransition[cpt] = false; hasEntryTransition[cpt] = false; }*/ /* read transitions */ try { cpt = 0; mod = Math.max(1, (int) (nbTransition / 100)); while ((s = br.readLine()) != null) { //TraceManager.addDev("realine:" + s); array = AUTGraph.decodeLine(s); at = new AUTTransition(array[0], array[1], array[2]); transitions.add(at); hasExitTransition[at.origin] = true; hasEntryTransition[at.destination] = true; cpt++; if ((cpt % mod) == 0) { percentage = (int) ((cpt * 100) / nbTransition); //TraceManager.addDev("percentage=" + percentage + "cpt=" + cpt + "nbTransition=" + nbTransition); } } } catch (Exception e) { TraceManager.addDev("Cancelled: " + e.getMessage() + "\n"); return; } } public static String[] decodeLine(String s) { int index1, index2; String s1, s2, s3; index1 = s.indexOf("("); index2 = s.indexOf(","); s1 = s.substring(index1 + 1, index2); s = s.substring(index2 + 1, s.length()); s = Conversion.removeFirstSpaces(s); // for of the action // , action, // "i(action<1,2,4>)", // "action<1,2,4>", // guillemets ? index1 = s.indexOf("\""); if (index1 > -1) { //TraceManager.addDev("Guillemets on " + s); s2 = s.substring(index1 + 1, s.length()); s2 = s2.substring(0, s2.indexOf("\"")); //TraceManager.addDev("Guillemets on " + s2); /*index2 = s2.indexOf("("); if (index2 > -1) { s2 = s2.substring(index2+1, s2.indexOf(")")); }*/ //TraceManager.addDev("Guillemets on " + s2); } else { //TraceManager.addDev("No Guillemets on " + s); index1 = s.indexOf(","); if ((index2 = s.indexOf("(")) >= 0) { s2 = s.substring(index2 + 1, index1 - 2); } else { if ((index2 = s.indexOf("\"t\"")) >= 0) { s2 = "t"; } else { s2 = s.substring(0, index1); } } } s = s.substring(s.indexOf(s2) + s2.length(), s.length()); //TraceManager.addDev("s=" + s); index1 = s.indexOf(","); //index2 = s.indexOf(")"); //s2 = s.substring(0, index1-1); s3 = s.substring(index1 + 1, s.length() - 1); s3 = Conversion.removeFirstSpaces(s3); //TraceManager.addDev("s1=" + s1 + " s2=" + s2 + " s3=" + s3); String[] array = new String[3]; array[0] = s1; array[1] = s2; array[2] = s3; return array; } public int getNbOfStates() { return nbState; } public void setNbOfStates(int _nb) { nbState = _nb; } public int getNbOfTransitions() { //return nbTransition; return transitions.size(); } public AUTTransition getAUTTransition(int index) { return transitions.get(index); } public ArrayList<AUTState> getStates() { return states; } public ArrayList<AUTTransition> getTransitions() { return transitions; } public void addTransition(AUTTransition _tr) { transitions.add(_tr); statesComputed = false; } public void addState(AUTState _st) { states.add(_st); } public int getNbPotentialDeadlocks() { int nb = 0; for (int i = 0; i < nbState; i++) { if (hasEntryTransition(i)) { if (!hasExitTransition(i)) { nb++; } } } return nb; } public String getActionTransition(int origin, int destination) { for (AUTTransition aut1 : transitions) { if ((aut1.origin == origin) && (aut1.destination == destination)) { return aut1.transition; } } return ""; } public boolean hasEntryTransition(int state) { if (hasEntryTransition == null) { computeEntryExitTransitions(); } return hasEntryTransition[state]; } public boolean hasExitTransition(int state) { if (hasExitTransition == null) { computeEntryExitTransitions(); } return hasExitTransition[state]; } public boolean hasExitTransitionTo(int state, int destination) { if (!hasExitTransition(state)) { return false; } for (AUTTransition aut1 : transitions) { if ((aut1.origin == state) && (aut1.destination == destination)) { return true; } } return false; } /* State numbers are return under the form of int */ /* Should be rewritten: not of high performance at all */ public int[] getVectorPotentialDeadlocks() { int nbPotentialDeadlock = getNbPotentialDeadlocks(); //TraceManager.addDev("nb of deadlocks: " + nbPotentialDeadlock); int[] states = new int[nbPotentialDeadlock]; int index = 0; for (int i = 0; i < nbState; i++) { if (hasEntryTransition(i)) { if (!hasExitTransition(i)) { states[index] = i; index++; } } } return states; } public int[] shortestPathTo(int fromState, int targetState) { return GraphAlgorithms.ShortestPathFrom(this, fromState)[targetState].path; } public boolean hasTransitionWithAction(String action) { for (AUTTransition aut1 : transitions) { if (aut1.transition.compareTo(action) == 0) { return true; } } return false; } // For Graph interface public int getWeightOfTransition(int originState, int destinationState) { if (statesComputed) { if (states.get(originState).hasTransitionTo(destinationState)) { return 1; } } else { if (hasExitTransitionTo(originState, destinationState)) { return 1; } } return 0; } public String toAUTStringFormat() { StringBuffer graph = new StringBuffer(""); graph.append("des(0," + nbTransition + "," + nbState + ")\n"); for (AUTTransition aut1 : transitions) { graph.append("(" + aut1.origin + ",\"" + aut1.transition + "\"," + aut1.destination + ")\n"); } return graph.toString(); } public String toFullString() { StringBuffer graph = new StringBuffer("Transitions:"); for (AUTTransition aut1 : transitions) { graph.append(aut1.toString()); } graph.append("\nstates:\n"); for (AUTState str : states) { graph.append(str.toString()); } return graph.toString(); } public void computeStates() { if (!statesComputed) { states = new ArrayList<AUTState>(nbState); AUTState state; for (int i = 0; i < nbState; i++) { state = new AUTState(i); states.add(state); } for (AUTTransition aut1 : transitions) { states.get(aut1.origin).addOutTransition(aut1); states.get(aut1.destination).addInTransition(aut1); } statesComputed = true; setNbOfStates(states.size()); } } public AUTState getState(int _id) { return states.get(_id); } public boolean areStateComputed() { return statesComputed; } public void setStates(ArrayList<AUTState> _states) { states = _states; nbState = states.size(); statesComputed = true; } public HashSet<String> getAllActions() { HashSet<String> hs = new HashSet<String>(); for (AUTTransition tr : transitions) { hs.add(tr.transition); } return hs; } public void reinitMet() { for (AUTState state : states) { state.met = false; } } public AUTState findFirstOriginState() { AUTState state; for (int i = 0; i < states.size(); i++) { state = states.get(i); //TraceManager.addDev("id=" + state.id + " transitions to me = " +state.inTransitions.size()); if (state.inTransitions.size() == 0) { return state; } } return null; } public void putTransitionsFromInitFirst() { ArrayList<AUTTransition> tmp = new ArrayList<AUTTransition>(); for (AUTTransition aut1 : transitions) { if (aut1.origin == 0) { tmp.add(aut1); } } for (AUTTransition aut2 : tmp) { transitions.remove(aut2); transitions.add(0, aut2); } } public void display() { display(false); } public void display(boolean exitOnClose) { AUTGraphDisplay display = new AUTGraphDisplay(this, exitOnClose); display.display(); } public AUTGraph cloneMe() { AUTGraph newGraph = new AUTGraph(); newGraph.setNbOfStates(getNbOfStates()); for (AUTTransition tr : transitions) { AUTTransition newTr = new AUTTransition(tr.origin, tr.transition, tr.destination); newGraph.addTransition(newTr); } newGraph.computeStates(); return newGraph; } public String getCompoundString(AUTTransition t, HashSet<AUTTransition> set) { String ret = ""; int cpt = 0; for(AUTTransition tr: transitions) { if ((tr.origin == t.origin) && (tr.destination == t.destination)) { set.add(tr); if (cpt > 0) { ret += " OR "; } else { cpt ++; } ret += tr.transition; } } return ret; } public void minimizeRemoveInternal(boolean tauOnly) { String s = "tau"; // mark all transitions as non tau for (AUTTransition tr : transitions) { tr.isTau = false; } // Mark all tau transitions as tau for (AUTTransition tr : transitions) { if (tr.transition.startsWith("i(")) { tr.isTau = true; tr.transition = s; } } minimizeTau(tauOnly); } public AUTGraph minimize(String[] tauTransitions, boolean tauOnly) { String s = "tau"; // mark all transitions as non tau for (AUTTransition tr : transitions) { tr.isTau = false; } // Mark all tau transitions as tau for (AUTTransition tr : transitions) { for (int i = 0; i < tauTransitions.length; i++) { if (tr.transition.compareTo(tauTransitions[i]) == 0) { tr.isTau = true; tr.transition = s; } } } //minimizeTau(tauOnly); //return this; return reduceGraph(); } public void minimizeTau(boolean tauOnly) { boolean modif = true; //TraceManager.addDev(toFullString()); factorizeNonTauTransitions(); if (tauOnly) { return; } TraceManager.addDev("RG before partition: " + toFullString()); partitionGraph(); } // Remove transition going from one state with only one tau transition as output private boolean removeOnlyOneTauTr() { AUTTransition tr; ArrayList<AUTState> toRemoveStates = new ArrayList<AUTState>(); // Remove in case state with one outgoing and outgoing is tau -> remove tr for (AUTState st : states) { if (st.outTransitions.size() == 1) { tr = st.outTransitions.get(0); if (tr.isTau) { transitions.remove(tr); st.outTransitions.clear(); AUTState st1 = states.get(tr.destination); if (st1 != st) { toRemoveStates.add(st1); //TraceManager.addDev("Removing state " + st1.id); // Must put all incoming transition to the first state st1.inTransitions.remove(tr); for (AUTTransition trM : st1.inTransitions) { trM.destination = st.id; st.inTransitions.add(trM); //TraceManager.addDev("New in transitions " + trM); } st1.inTransitions.clear(); // Out transitions st.outTransitions.clear(); for (AUTTransition trM : st1.outTransitions) { st.outTransitions.add(trM); trM.origin = st.id; // TraceManager.addDev("New out transitions " + trM); } st1.outTransitions.clear(); break; } } } } // Remove all states and adapt the id in the graph if (toRemoveStates.size() > 0) { removeStates(toRemoveStates); return true; } return false; } // Rework states with at least 2 tau transitions private boolean removeMultipleTauOutputTr() { AUTTransition tr1, tr2, trtmp; AUTState st1, st2, sttmp; ArrayList<AUTState> toRemoveStates = new ArrayList<AUTState>(); AUTTransition[] ret; boolean modif = false; // Remove in case state with one outgoing and outgoing is tau -> remove transition for (AUTState st : states) { ret = st.getAtLeastTwoOutTauTransitions(); if (ret != null) { tr1 = ret[0]; tr2 = ret[1]; tr2 = st.outTransitions.get(1); st1 = states.get(tr1.destination); st2 = states.get(tr2.destination); // Same states if (st1 == st2) { //We can simply remove the transition transitions.remove(tr2); st.outTransitions.remove(tr2); modif = true; } // We can merge st1 or st2 because one has no other incoming transition than // the tau transition else if ((st1.inTransitions.size() == 1) && (st2.inTransitions.size() == 1)) { //We can remove st2 and the tau transition toRemoveStates.add(st2); transitions.remove(tr2); st.outTransitions.remove(tr2); // All transitions leaving st2 must now leave from st1 as well for (AUTTransition trf : st2.outTransitions) { trf.origin = st1.id; st1.outTransitions.add(trf); } } } } // Remove all states and adapt the id in the graph if (toRemoveStates.size() > 0) { removeStates(toRemoveStates); modif = true; } return modif; } // Rework states with only one tau before, and only one action after private boolean removeTauWithOneFollower() { AUTTransition tr1, tr2; AUTState st1, st2; ArrayList<AUTState> toRemoveStates = new ArrayList<AUTState>(); boolean modif = false; // Remove stgate in case state with one outgoing and outgoing is tau for (AUTState st : states) { if (st.hasOneIncomingTauAndOneFollower()) { //We can remove the previous tau transaction, and the current state tr1 = st.inTransitions.get(0); st1 = states.get(tr1.origin); if (st1 != st) { tr2 = st.outTransitions.get(0); tr2.origin = st1.id; st1.outTransitions.remove(tr1); st1.outTransitions.add(tr2); transitions.remove(tr1); toRemoveStates.add(st); break; } } } // Remove all states and adapt the id in the graph if (toRemoveStates.size() > 0) { removeStates(toRemoveStates); modif = true; } return modif; } private boolean removeSimilarTransitions() { boolean modif = false; // Remove tr if it is duplicated for (AUTState st : states) { // May modify the outTransitions list, and result in exception. // The try .. catch clause protects from this try { if (st.outTransitions.size() > 1) { for (int i = 0; i < st.outTransitions.size(); i++) { for (int j = i + 1; j < st.outTransitions.size(); j++) { AUTTransition tri = st.outTransitions.get(i); AUTTransition trj = st.outTransitions.get(j); if (tri.destination == trj.destination) { if (tri.transition.compareTo(trj.transition) == 0) { modif = true; //We remove trj st.outTransitions.remove(trj); transitions.remove(trj); i--; j--; } } } } } } catch (Exception e) { } } return modif; } private void removeStates(ArrayList<AUTState> toRemoveStates) { if (toRemoveStates.size() > 0) { hasExitTransition = null; hasEntryTransition = null; } // Remove all states and adapt the id in the graph //TraceManager.addDev("nbState=" + nbState + " states size = " + states.size()); for (AUTState str : toRemoveStates) { // We need to remove all transitions of the removed state //TraceManager.addDev("Removing transitions of state:" + str.id + "\n" + toFullString()); for (AUTTransition trin : str.inTransitions) { transitions.remove(trin); } for (AUTTransition trout : str.outTransitions) { transitions.remove(trout); } for (AUTState state : states) { state.removeAllTransitionsWithId(str.id); } //TraceManager.addDev("Done removing transitions of state:" + str.id + "\n" + toFullString()); // Last state of the array? if (str.id == (nbState - 1)) { //TraceManager.addDev("Last state " + str.id); nbState--; states.remove(str.id); // str not at the end: we replace it with the last state // We need to accordingly update } else { AUTState moved = states.get(nbState - 1); //TraceManager.addDev("Moving state " + moved.id + " to index " + str.id); states.set(str.id, moved); states.remove(nbState - 1); nbState--; //TraceManager.addDev("nbState=" + nbState + " states size = " + states.size()); /*AUTTransition tt = findTransitionWithId(nbState); if (tt != null) { TraceManager.addDev("1) Transition with id not normal" + tt); }*/ //TraceManager.addDev("Update id\n" + toAUTStringFormat()); moved.updateID(str.id); /*tt = findTransitionWithId(nbState); if (tt != null) { TraceManager.addDev("2) Transition with id not normal" + tt); }*/ } } } // Removes all tau transition of a state, replacing them with reachable non tau transitions // A tau transition reaching an end state cannot be removed but can be replaced with a unique transition private void factorizeNonTauTransitions() { boolean modif = false; boolean endState = false; // met is used to specify states that have a tau-path to a termination state for (AUTState st1 : states) { st1.met = false; } for (AUTState st : states) { if ((st.id == 0) || (st.getNbInTransitions() > 0)) { //TraceManager.addDev(" 1. state " + st.id); if (st.hasOutputTauTransition()) { //TraceManager.addDev(" 2. state " + st.id); LinkedList<AUTTransition> nonTauTransitions = new LinkedList<AUTTransition>(); boolean canReachAnEndStateWithTau = getAllNonTauTransitionsFrom(st, nonTauTransitions); //TraceManager.addDev("State " + st.id + " has the following real transitions:"); /*for(AUTTransition tr: nonTauTransitions) { TraceManager.addDev("\t" + tr); }*/ //TraceManager.addDev("State " + st.id + " can reach an end state with tau tr only?" + canReachAnEndStateWithTau); st.met = canReachAnEndStateWithTau; endState = endState || canReachAnEndStateWithTau; } } } // Remove tr if it is duplicated for (AUTState st : states) { // We ignore states with no input tr apart from the start state (id 0) //TraceManager.addDev("0. state " + st.id); if ((st.id == 0) || (st.getNbInTransitions() > 0)) { //TraceManager.addDev(" 1. state " + st.id); if (st.hasOutputTauTransition()) { //TraceManager.addDev(" 2. state " + st.id); LinkedList<AUTTransition> nonTauTransitions = new LinkedList<AUTTransition>(); getAllNonTauTransitionsFrom(st, nonTauTransitions); // Create these transitions in st if not yet existing //TraceManager.addDev("Remove tau\n" + toFullString()); st.removeAllOutTauTransitions(transitions, states); //TraceManager.addDev("Done remove tau. create trans\n" + toFullString()); st.createTransitionsButNotDuplicate(nonTauTransitions, states, transitions); //TraceManager.addDev("Done create trans\n" + toFullString()); } } } // Remove all non reachable state //removeAllNonReachableStates(); // If end state: we must create a new end state, and all "met" states should have a tau transition // to this state if (endState) { // We must see if at least one met state has output transitions boolean hasEndState = false; for (AUTState st : states) { if (st.met) { if (st.outTransitions.size() > 0) { hasEndState = true; break; } } } if (hasEndState) { int newId = states.size(); AUTState endSt = new AUTState(newId); states.add(endSt); nbState = states.size(); for (AUTState st1 : states) { if (st1.met) { if (st1.outTransitions.size() > 0) { //TraceManager.addDev("Adding an end tau to state " + st1.id); AUTTransition tr = new AUTTransition(st1.id, "tau", endSt.id); tr.isTau = true; transitions.add(tr); st1.addOutTransition(tr); endSt.addInTransition(tr); } } st1.met = false; } } } //TraceManager.addDev(toFullString()); // Remove all non reachable state TraceManager.addDev("Remove all non reachable states"); removeAllNonReachableStates(); // Print graph in AUT //TraceManager.addDev(toAUTStringFormat()); } private boolean getAllNonTauTransitionsFrom(AUTState st, LinkedList<AUTTransition> nonTauTransitions) { LinkedList<AUTState> metStates = new LinkedList<AUTState>(); //metStates.add(st); return getAllNonTauTransitionsIterative(st, metStates, nonTauTransitions); //return getAllNonTauTransitionsRecursive(st, metStates, nonTauTransitions); } private boolean getAllNonTauTransitionsRecursive(AUTState st, LinkedList<AUTState> metStates, LinkedList<AUTTransition> nonTauTransitions) { if (metStates.contains(st)) { return false; } if (st.getNbOutTransitions() == 0) { return true; } boolean ret = false; for (AUTTransition at : st.outTransitions) { if (!(at.isTau)) { nonTauTransitions.add(at); } else { ret = ret || getAllNonTauTransitionsRecursive(states.get(at.destination), metStates, nonTauTransitions); } } return ret; } private boolean getAllNonTauTransitionsIterative(AUTState _st, LinkedList<AUTState> metStates, LinkedList<AUTTransition> nonTauTransitions) { boolean ret = false; LinkedList<AUTState> toExplore = new LinkedList<AUTState>(); LinkedList<AUTState> toExploreTmp = new LinkedList<AUTState>(); toExplore.add(_st); while (toExplore.size() > 0) { toExploreTmp.clear(); for (AUTState st : toExplore) { if (!(metStates.contains(st))) { metStates.add(st); if (st.getNbOutTransitions() == 0) { ret = true; } else { for (AUTTransition at : st.outTransitions) { if (!(at.isTau)) { nonTauTransitions.add(at); } else { toExploreTmp.add(states.get(at.destination)); } } } } } // for toExplore.clear(); toExplore.addAll(toExploreTmp); }// While return ret; } private AUTTransition findTransitionWithId(int id) { for (AUTTransition tr : transitions) { if ((tr.origin == id) || (tr.destination == id)) { return tr; } } return null; } private void removeAllNonReachableStates() { // reset met of states for (AUTState st1 : states) { st1.met = false; } //int cpt = 0; long startTime = System.nanoTime(); AUTState[] statesToConsider = new AUTState[1]; LinkedList<AUTState> nextStatesToConsider = new LinkedList<AUTState>(); statesToConsider[0] = states.get(0); states.get(0).met = true; for(int cpt=0; cpt<statesToConsider.length; cpt++){ nextStatesToConsider.clear(); for (AUTState st: statesToConsider) { //st.met = true; //cpt++; for (AUTTransition tr : st.outTransitions) { AUTState s = states.get(tr.destination); if (!(s.met)) { s.met = true; nextStatesToConsider.add(s); } } } statesToConsider = nextStatesToConsider.toArray(new AUTState[nextStatesToConsider.size()]); cpt = -1; //TraceManager.addDev("Size of states to consider:" + statesToConsider.size()); } //TraceManager.addDev("Found " + cpt + " reachable states"); ArrayList<AUTState> toRemoveStates = new ArrayList<AUTState>(); for (AUTState st2 : states) { if (!(st2.met)) { toRemoveStates.add(st2); //TraceManager.addDev("Removing state: " + st2.id); } } long time1 = System.nanoTime(); //TraceManager.addDev(toFullString()); removeStates(toRemoveStates); //TraceManager.addDev(toFullString()); long endTime = System.nanoTime(); TraceManager.addDev("First part: " + (time1-startTime)/1000000000 + " Second part: " + (endTime - time1)/1000000000); //statesComputed = false; //states = null; //computeStates(); } private void computeEntryExitTransitions() { hasExitTransition = new boolean[nbState]; hasEntryTransition = new boolean[nbState]; for (AUTTransition t : transitions) { hasExitTransition[t.origin] = true; hasEntryTransition[t.destination] = true; } } public void computeNbOfTransitions() { nbTransition = transitions.size(); } public AUTGraph fromAutomaton(Automaton a) { AUTGraph graph = new AUTGraph(); @SuppressWarnings("unchecked") Set<Transition<String>> trs = (Set<Transition<String>>) a.delta(); //Set<?> trs = a.delta(); @SuppressWarnings("unchecked") Set<State> sts = (Set<State>)(a.states()); Map<State, Integer> mapOfStates = new HashMap<>(); int cpt = 1; for(State st: sts) { if (st.isInitial()) { //@SuppressWarnings("unchecked") mapOfStates.put(st, new Integer(0)); } else { //@SuppressWarnings("unchecked") mapOfStates.put(st, new Integer(cpt)); cpt ++; } } graph.setNbOfStates(mapOfStates.size()); for(Transition<String> tr: trs) { State s1 = tr.start(); State s2 = tr.end(); String label = tr.label(); Integer i1 = mapOfStates.get(s1); Integer i2 = mapOfStates.get(s2); if ((i1 != null) && (i2 != null)) { AUTTransition trNew = new AUTTransition(i1.intValue(), label, i2.intValue()); graph.addTransition(trNew); } } graph.computeNbOfTransitions(); graph.computeStates(); return graph; } public Automaton toAutomaton() { Automaton<String, Transition<String>, TransitionBuilder<String>> a = new Automaton<>(); computeStates(); boolean initial = true; for(AUTState s: states) { State as = a.addState(initial, s.isTerminationState()); s.referenceObject = as; initial = false; } for(AUTTransition t: transitions) { try { String label = t.transition; if (t.isTau) { label = null; } a.addTransition(new Transition<String>((State) (states.get(t.origin).referenceObject), label, (State) (states.get(t .destination) .referenceObject))); } catch (NoSuchStateException nsse) { } } return a; } @SuppressWarnings("unchecked") public AUTGraph reduceGraph() { //TraceManager.addDev("Factorize"); //factorizeNonTauTransitions(); Automaton a = toAutomaton(); //TraceManager.addDev("Initial AUT:" + a.toString()); /*@SuppressWarnings("unchecked") Automaton<String, Transition<String>, TransitionBuilder<String>> newA = new EpsilonTransitionRemover<String, Transition<String>, TransitionBuilder<String>>().transform((Automaton<String, Transition<String>, TransitionBuilder<String>>)a); TraceManager.addDev("Aut with no tau / epsilon:" + newA.toString());*/ Automaton newA = a; TraceManager.addDev("Reduce"); newA = new Reducer<String, Transition<String>, TransitionBuilder<String>>().transform(newA); //TraceManager.addDev("Error in reduce graph:" + newA); //TraceManager.addDev("New Aut:" + newA.toString()); TraceManager.addDev("Reduce done"); return fromAutomaton(newA); } public void partitionGraph() { // Create the alphabet HashMap<String, AUTElement> alphabet = new HashMap<String, AUTElement>(); for (AUTTransition tr : transitions) { AUTElement tmp = alphabet.get(tr.transition); if (tmp == null) { AUTElement elt = new AUTElement(tr.transition); alphabet.put(tr.transition, elt); tr.elt = elt; } else { tr.elt = tmp; } //TraceManager.addDev("Transition "+ tr + " has element " + tr.elt); } List<AUTElement> sortedAlphabet = new ArrayList<AUTElement>(alphabet.values()); Collections.sort(sortedAlphabet); TraceManager.addDev("Alphabet size:" + sortedAlphabet.size()); for (int i=0; i<alphabet.size(); i++) { TraceManager.addDev("Letter #" + i + ": " + sortedAlphabet.get(i).toString()); } Map<Integer, AUTBlock> allBlocks = Collections.synchronizedMap(new HashMap<Integer, AUTBlock>()); // Create the first block that contains all states AUTBlock b0 = new AUTBlock(); for (AUTState st : states) { b0.addState(st); } b0.computeHash(); allBlocks.put(new Integer(b0.hashValue), b0); AUTBlock b0Test = new AUTBlock(); for (AUTState st : states) { b0Test.addState(st); } b0Test.computeHash(); AUTBlock B0Ret = allBlocks.get(new Integer(b0Test.hashValue)); if (B0Ret == null) { //TraceManager.addDev("ERROR: hash not working for blocks"); } else { //TraceManager.addDev("Hash working for blocks"); } // Create the first partition containing only block B0 AUTPartition partition = new AUTPartition(); partition.addBlock(b0); // Create the splitter that contains partitions AUTPartition partitionForSplitter = new AUTPartition(); partitionForSplitter.addBlock(b0); AUTSplitter w = new AUTSplitter(); w.addPartition(partitionForSplitter); //printConfiguration(partition, w); int maxIte = 10; // With same nb of partitions int nbOfPartitions = w.size(); int evolution = 20; int hashCode = -1; AUTPartition currentP; while ((w.size() > 0) && (maxIte > 0) && (evolution > 0)) { int newHash = w.getHashCode(); if (newHash == hashCode) { break; } if (w.size() == nbOfPartitions) { evolution--; } else { evolution = 20; } nbOfPartitions = w.size(); maxIte--; currentP = w.partitions.get(0); w.partitions.remove(0); // Simple splitter? if (currentP.blocks.size() == 1) { TraceManager.addDev("Simple splitter = " + currentP); AUTBlock currentBlock = currentP.blocks.get(0); //List<AUTElement> sortedAlphabet = new ArrayList<AUTElement>(alphabet.values()); //Collections.sort(sortedAlphabet); for (AUTElement elt : sortedAlphabet) { //TraceManager.addDev("\n*** Considering alphabet element = " + elt.value); //printConfiguration(partition, w); // Look for states of the leading to another state by a = T // Construct I = all blocks of P that have at least an element in T AUTBlock T_minus1_elt_B = currentBlock.getMinus1(elt, states); //TraceManager.addDev("T_minus1_elt_B=" + T_minus1_elt_B); LinkedList<AUTBlock> I = partition.getI(elt, T_minus1_elt_B); //printI(I); for (AUTBlock blockX : I) { AUTBlock blockX1 = blockX.getStateIntersectWith(T_minus1_elt_B); blockX1.computeHash(); AUTBlock blockX2 = blockX.getStateDifferenceWith(T_minus1_elt_B); blockX2.computeHash(); //TraceManager.addDev("X1=" + blockX1); //TraceManager.addDev("X2=" + blockX2); if (blockX1.isEmpty() || blockX2.isEmpty()) { //TraceManager.addDev("Nothing to do"); // Nothing to do! } else { boolean b = partition.removeBlock(blockX); if (!b) { //TraceManager.addDev("Block " + blockX + " could not be removed from partition"); } partition.addBlock(blockX1); partition.addBlock(blockX2); AUTPartition X_X1_X2 = new AUTPartition(); X_X1_X2.addBlock(blockX); X_X1_X2.addBlock(blockX1); X_X1_X2.addBlock(blockX2); //TraceManager.addDev("Test concat X1+X2=" + AUTBlock.concat(blockX1, blockX2)); w.addPartition(X_X1_X2); TraceManager.addDev("Modifying P and W:"); printConfiguration(partition, w); TraceManager.addDev("-----------------\n"); } } TraceManager.addDev("-----------------\n"); } } // Compound splitter else if (currentP.blocks.size() == 3) { TraceManager.addDev("Complex splitter (b, bi, bii) =" + currentP); AUTBlock b = currentP.blocks.get(0); AUTBlock bi = currentP.blocks.get(1); AUTBlock bii = currentP.blocks.get(2); if (bi.size() > bii.size()) { bi = currentP.blocks.get(2); bii = currentP.blocks.get(1); } TraceManager.addDev("B= " + b + " bi=" + bi + " bii=" + bii); for (AUTElement elt : sortedAlphabet) { //TraceManager.addDev("\n*** Considering alphabet element = " + elt.value); //printConfiguration(partition, w); AUTBlock T_minus1_elt_B = b.getMinus1(elt, states); //TraceManager.addDev("T_minus1_elt_B=" + T_minus1_elt_B); LinkedList<AUTBlock> I = partition.getI(elt, T_minus1_elt_B); //printI(I); for (AUTBlock blockX : I) { // Compute block X1 = set of states in blockX that goes to Bi, but not to Bii // with current action AUTBlock blockX1 = new AUTBlock(); // Compute block X2 = set of states in blockX that goes to Bii, but not to Bi // with current action AUTBlock blockX2 = new AUTBlock(); // Compute block X3 = set of states in blockX that goes to both Bi and Bii // with current action boolean b1, b2; AUTBlock blockX3 = new AUTBlock(); for (AUTState st : blockX.states) { b1 = blockX.leadsTo(bi, elt); b2 = blockX.leadsTo(bii, elt); if (b1 && !b2) { blockX1.addState(st); } else if (!b1 && b2) { blockX2.addState(st); } else { blockX3.addState(st); } } TraceManager.addDev("Block X = " + blockX + " Block1,2,3 computed\n\tX1 = " + blockX1 + "\n\tX2 = " + blockX2 + "\n\tX3 = " + blockX3); if ((blockX.compareTo(blockX1) == 0) || (blockX.compareTo(blockX2) == 0) || (blockX.compareTo(blockX2) == 0)) { // do nothing TraceManager.addDev("Identical blocks! X"); } else { TraceManager.addDev("Non Identical blocks! X"); // Modifying partition partition.removeBlock(blockX); partition.addIfNonEmpty(blockX1); partition.addIfNonEmpty(blockX2); partition.addIfNonEmpty(blockX3); // Add two splitter to W if all are non null if (!(blockX1.isEmpty()) && !(blockX2.isEmpty()) && !(blockX3.isEmpty())) { // Add splitter (X, X1, X23) && (X23, X2, X3) AUTPartition tmpP = new AUTPartition(); tmpP.addBlock(blockX); tmpP.addBlock(blockX1); tmpP.addBlock(AUTBlock.concat(blockX2, blockX3)); w.addPartition(tmpP); tmpP = new AUTPartition(); tmpP.addBlock(AUTBlock.concat(blockX2, blockX3)); tmpP.addBlock(blockX2); tmpP.addBlock(blockX3); w.addPartition(tmpP); } else { // Add non empty individual block to W AUTPartition tmpP; if (!(blockX1.isEmpty())) { tmpP = new AUTPartition(); tmpP.addBlock(blockX1); w.addPartition(tmpP); } if (!(blockX2.isEmpty())) { tmpP = new AUTPartition(); tmpP.addBlock(blockX2); w.addPartition(tmpP); } if (!(blockX3.isEmpty())) { tmpP = new AUTPartition(); tmpP.addBlock(blockX3); w.addPartition(tmpP); } } } } } } } TraceManager.addDev("\nAll done: it: " + maxIte + "\n---------"); printConfiguration(partition, w); //TraceManager.addDev("------------------"); // Generating new graph generateGraph(partition, alphabet); } // Assumes AUTElement have been added to transitions public void generateGraph(AUTPartition partition, HashMap<String, AUTElement> _alphabet) { ArrayList<AUTState> sts = new ArrayList<AUTState>(); ArrayList<AUTTransition> trs = new ArrayList<AUTTransition>(); HashMap<AUTBlock, AUTState> blockToNewStates = new HashMap<AUTBlock, AUTState>(); int stID = 1; // We create one state per block // We look to the block that contains state 0 and we create the state id = 0 for (AUTBlock bl : partition.blocks) { if (bl.hasState(0)) { AUTState st0 = new AUTState(0); blockToNewStates.put(bl, st0); sts.add(0, st0); } else { AUTState st = new AUTState(stID); stID++; blockToNewStates.put(bl, st); sts.add(st); } } // We now need to create the transitions // We parse all states in blocks, and consider their transition // We look for the destination and create a transition accordingly for (AUTBlock bl : partition.blocks) { AUTState newOrigin = blockToNewStates.get(bl); for (AUTState src : bl.states) { for (AUTTransition tr : src.outTransitions) { AUTState newDestination = blockToNewStates.get(partition.getBlockWithState(tr.destination)); boolean foundSimilar = false; AUTTransition newT = new AUTTransition(newOrigin.id, tr.transition, newDestination.id); newT.elt = tr.elt; for (AUTTransition testT : trs) { if (testT.compareTo(newT) == 0) { foundSimilar = true; break; } } if (!foundSimilar) { trs.add(newT); newOrigin.outTransitions.add(newT); newDestination.inTransitions.add(newT); } } } } states = sts; transitions = trs; nbState = sts.size(); //TraceManager.addDev("New graph: " + toFullString()); } public AUTGraph generateRefusalGraph() { computeStates(); AUTState currentState = new AUTState(0); ArrayList<AUTState> newStates = new ArrayList<AUTState>(); ArrayList<AUTTransition> newTransitions = new ArrayList<AUTTransition>(); newStates.add(currentState); // We go thru the graph, starting at state1. Each time we meet an already handled state, we stop. HashSet<AUTState> metStates = new HashSet<AUTState>(); // in origin Graph HashMap<AUTState, AUTState> toRefusalState = new HashMap<AUTState, AUTState>(); LinkedList<AUTState> toHandle = new LinkedList<AUTState>(); toHandle.add(states.get(0)); toRefusalState.put(states.get(0), currentState); while (toHandle.size() > 0) { AUTState current = toHandle.get(0); toHandle.remove(0); metStates.add(current); AUTState currentRefusal = toRefusalState.get(current); if (currentRefusal == null) { //TraceManager.addDev("NULL current Refusal"); } else { // For each possible transition, we create a new transition to a new destination state for (AUTTransition tr : current.outTransitions) { // Create new transition. Is a new state necessary? AUTState destState = states.get(tr.destination); AUTState stRefusal; stRefusal = new AUTState(newStates.size()); newStates.add(stRefusal); toRefusalState.put(destState, stRefusal); AUTTransition trRefusal = new AUTTransition(current.id, tr.transition, stRefusal.id); newTransitions.add(trRefusal); stRefusal.addInTransition(trRefusal); currentRefusal.addOutTransition(trRefusal); if (!metStates.contains(destState)) { if (!(toHandle.contains(destState))) { toHandle.add(destState); } } } } } AUTGraph refusalGraph = new AUTGraph(newStates, newTransitions); return refusalGraph; } public AUTGraph makeTestSequencesFromRefusalGraph() { ArrayList<AUTState> newStates = new ArrayList<AUTState>(); ArrayList<AUTTransition> newTransitions = new ArrayList<AUTTransition>(); AUTState firstState = new AUTState(0); newStates.add(firstState); // Take all termination states of the refusal graph and get the shortest path // from the first state to this termination state // Add this path as a new path of the new graph computeStates(); DijkstraState[] allPaths = GraphAlgorithms.LongestPathFrom(this, 0); for (AUTState state : states) { if (state.isTerminationState()) { int[] path = allPaths[state.id].path; if (path != null) { AUTState currentStateN = firstState; // We create a corresponding path in the new graph. String s = ""; for (int j = 0; j < path.length; j++) { s += path[j] + " "; } //TraceManager.addDev("path=" + s); for (int i = 1; i < path.length; i++) { AUTState currentState = states.get(path[i - 1]); AUTState nextState = states.get(path[i]); AUTTransition tr = currentState.getTransitionTo(nextState.id); //TraceManager.addDev("Looking for transition"); if (tr != null) { // We need to create the destination state AUTState newDest = new AUTState(newStates.size()); newStates.add(newDest); AUTTransition newTr = new AUTTransition(currentStateN.id, tr.transition, newDest.id); newTransitions.add(newTr); //TraceManager.addDev("Adding transition:" + newTr); currentStateN = newDest; } else { //TraceManager.addDev(" -> null transitions"); } } } } } // Making the graph AUTGraph testGraph = new AUTGraph(newStates, newTransitions); return testGraph; } private void printConfiguration(AUTPartition _part, AUTSplitter _w) { TraceManager.addDev("P={" + _part.toString() + "}"); TraceManager.addDev("W={" + _w.toString() + "}"); } private void printI(LinkedList<AUTBlock> I) { StringBuffer sb = new StringBuffer("I:"); for (AUTBlock b : I) { sb.append(" " + b); } TraceManager.addDev(sb.toString()); } public int getMinValue(String nameOfTransition) { int minValue = Integer.MAX_VALUE; //System.out.println("executing. min value"); for (AUTTransition tr: transitions) { //System.out.println("executing. Dealing with " + tr.transition); if (tr.transition.length() > 3) { String trans = tr.transition.substring(2, tr.transition.length()-1); //System.out.println("executing. trans " + trans); int index = trans.indexOf("<"); if (index > 0) { String trName = trans.substring(0, index); //System.out.println("executing. trName " + trName); if (trName.equals(nameOfTransition)) { String valS = trans.substring(index + 1); //System.out.println("executing. valS " + valS); int indexE = valS.indexOf(">"); if (indexE > 0) { valS = valS.substring(0, indexE); System.out.println("executing. max valS " + valS); int val = Integer.decode(valS); if (val < minValue) { minValue = val; } } } } } } return minValue; } public int getMaxValue(String nameOfTransition) { int maxValue = -1; //System.out.println("executing. min value"); for (AUTTransition tr: transitions) { //System.out.println("executing. Dealing with " + tr.transition); if (tr.transition.length() > 3) { String trans = tr.transition.substring(2, tr.transition.length()-1); //System.out.println("executing. trans " + trans); int index = trans.indexOf("<"); if (index > 0) { String trName = trans.substring(0, index); //System.out.println("executing. trName " + trName); if (trName.equals(nameOfTransition)) { String valS = trans.substring(index + 1); //System.out.println("executing. valS " + valS); int indexE = valS.indexOf(">"); if (indexE > 0) { valS = valS.substring(0, indexE); System.out.println("executing. min valS " + valS); int val = Integer.decode(valS); if (val > maxValue) { maxValue = val; } } } } } } return maxValue; } }