/* * Copyright (C) 2016 Mostafa M.A. (mostafa.amin93@gmail.com) * Copyright (C) 2014-2016 University of Freiburg * * This file is part of the ULTIMATE Automata Library. * * The ULTIMATE Automata Library is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * The ULTIMATE Automata Library 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the ULTIMATE Automata Library. If not, see . * * Additional permission under GNU GPL version 3 section 7: * If you modify the ULTIMATE Automata Library, or any covered work, by linking * or combining it with Eclipse RCP (or a modified version of Eclipse RCP), * containing parts covered by the terms of the Eclipse Public License, the * licensors of the ULTIMATE Automata Library grant you additional permission * to convey the resulting work. */ package de.uni_freiburg.informatik.ultimate.automata.tree; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import de.uni_freiburg.informatik.ultimate.automata.AutomataLibraryServices; import de.uni_freiburg.informatik.ultimate.automata.AutomataOperationCanceledException; import de.uni_freiburg.informatik.ultimate.core.model.models.IElement; /** * A Bottom-up TreeAutomaton. The rules have the form f(q1,...,qn) ~> q * * * @param * is the type of the alphabet. * @param * is the type of the states. * * @author Mostafa M.A. (mostafa.amin93@gmail.com) * @author Daniel Tischner {@literal } */ public class TreeAutomatonBU implements ITreeAutomatonBU { private final Set mAlphabet; private final Map>>> mChildrenMap; private final Set mFinalStates; private final Map>> mLettersMap; private final Map, Map>>> mParentsMap; private final Set> mRules; private final Map>> mSourceMap; private final Set mStates; /** * Create a TreeAutomaton. */ public TreeAutomatonBU() { mChildrenMap = new HashMap<>(); mParentsMap = new HashMap<>(); mAlphabet = new HashSet<>(); mLettersMap = new HashMap<>(); mSourceMap = new HashMap<>(); mRules = new HashSet<>(); mFinalStates = new HashSet<>(); mStates = new HashSet<>(); } /*** * Add Final state * * @param state */ public void addFinalState(final STATE state) { mFinalStates.add(state); addState(state); } /*** * Add a letter * * @param letter */ public void addLetter(final LETTER letter) { mAlphabet.add(letter); } /** * Add a rule to the automaton. * * @param rule */ @Override public void addRule(final TreeAutomatonRule rule) { if (mRules.contains(rule)) { // If rule already exists, do nothing return; } mRules.add(rule); final LETTER letter = rule.getLetter(); final STATE dest = rule.getDest(); final List src = rule.getSource(); // f(q1,...,qn) -> q assert letter.getRank() == rule.getSource().size(); if (letter.getRank() != rule.getSource().size()) { System.err.println(letter + " " + rule); } addLetter(letter); addState(dest); for (final STATE state : src) { addState(state); } // children(q)[f] = if (!mChildrenMap.containsKey(dest)) { mChildrenMap.put(dest, new HashMap>>()); } final Map>> childLetter = mChildrenMap.get(dest); if (!childLetter.containsKey(letter)) { childLetter.put(letter, new HashSet>()); } final HashSet> children = (HashSet>) childLetter .get(letter); children.add(rule); // parents(q1, ..., qn)[f] = q if (!mParentsMap.containsKey(src)) { mParentsMap.put(src, new HashMap>>()); } final Map>> parentLetter = mParentsMap.get(src); if (!parentLetter.containsKey(letter)) { parentLetter.put(letter, new HashSet>()); } final Set> parents = (Set>) parentLetter .get(letter); parents.add(rule); // lettersMap[f] = [f(p) -> q] if (!mLettersMap.containsKey(letter)) { mLettersMap.put(letter, new HashSet<>()); } final HashSet> rulesByLetter = (HashSet>) mLettersMap .get(letter); rulesByLetter.add(rule); // sourceRules[q] = < f(p1, ..., q, ... pn) -> p0 > for (final STATE st : src) { if (!mSourceMap.containsKey(st)) { mSourceMap.put(st, new HashSet<>()); } final HashSet> rulesBySource = (HashSet>) mSourceMap .get(st); rulesBySource.add(rule); } } /*** * Add a state * * @param state */ public void addState(final STATE state) { mStates.add(state); } /*** * Complement the set of final states. */ @Override public void complementFinals() { final Set newFinals = new HashSet<>(); for (final STATE st : mStates) { if (!isFinalState(st)) { newFinals.add(st); } } mFinalStates.clear(); mFinalStates.addAll(newFinals); } /*** * A debug string representation * * @return */ public String DebugString() { final StringBuilder statesString = new StringBuilder(); for (final STATE state : mStates) { statesString.append(stateString(state)); statesString.append(" "); } final StringBuilder rulesString = new StringBuilder(); for (final TreeAutomatonRule rule : mRules) { rulesString.append( String.format("%s%s ~~> %s\n", rule.getLetter(), rule.getSource(), stateString(rule.getDest()))); } return statesString + "\n" + rulesString; } /*** * Extend the alphabet. * * @param sigma */ public void extendAlphabet(final Collection sigma) { mAlphabet.addAll(sigma); } @Override public Set getAlphabet() { return mAlphabet; } /* * (non-Javadoc) * * @see de.uni_freiburg.informatik.ultimate.automata.tree.ITreeAutomatonBU# * getAmountOfRules() */ @Override public int getAmountOfRules() { return this.mRules.size(); } public Set getFinalStates() { return mFinalStates; } //@Override public Map>> getPredecessors(final STATE state) { if (!mChildrenMap.containsKey(state)) { return new HashMap<>(); } final Map>> result = new HashMap<>(); for (final LETTER letter : mChildrenMap.get(state).keySet()) { final Set> st = new HashSet<>(); for (final TreeAutomatonRule rule : mChildrenMap.get(state).get(letter)) { st.add(rule.getSource()); } result.put(letter, st); } return result; } //@Override public Iterable> getPredecessors(final STATE state, final LETTER letter) { if (!mChildrenMap.containsKey(state) || !mChildrenMap.get(state).containsKey(letter)) { return new ArrayList<>(); } final Set> result = new HashSet<>(); for (final TreeAutomatonRule rule : mChildrenMap.get(state).get(letter)) { result.add(rule.getSource()); } return result; } public Iterable> getRules() { return mRules; } /*** * Get rules that use a specific character. * * @param letter * @return */ @Override public Iterable> getSuccessors(final LETTER letter) { final Set> res = new HashSet<>(); for (final List src : getSourceCombinations()) { for (final STATE dest : getSuccessors(src, letter)) { res.add(new TreeAutomatonRule(letter, src, dest)); } } return res; //return mLettersMap.get(letter); } /*** * Get rules by source. * * @param src * @return */ public Iterable> getRulesBySource(final STATE src) { return mSourceMap.get(src); } @Override public Set getStates() { return mStates; } @Override public Iterable> getSuccessors(final List states) { final Set> successors = new HashSet<>(); for (final LETTER letter : mParentsMap.get(states).keySet()) { for (final TreeAutomatonRule rule : mParentsMap.get(states).get(letter)) { successors.add(rule); } } return successors; } @Override public Iterable getSuccessors(final List states, final LETTER letter) { if (!mParentsMap.containsKey(states) || !mParentsMap.get(states).containsKey(letter)) { return new HashSet<>(); } final Set result = new HashSet<>(); for (final TreeAutomatonRule rule : mParentsMap.get(states).get(letter)) { result.add(rule.getDest()); } return result; } @Override public boolean isFinalState(final STATE state) { return mFinalStates.contains(state); } public TreeAutomatonBU reconstruct(final Map mp) { final TreeAutomatonBU res = new TreeAutomatonBU<>(); res.extendAlphabet(mAlphabet); for (final STATE s : mStates) { res.addState(mp.get(s)); if (isFinalState(s)) { res.addFinalState(mp.get(s)); } } for (final TreeAutomatonRule rule : mRules) { final List src = new ArrayList<>(); for (final STATE s : rule.getSource()) { src.add(mp.get(s)); } final ST dest = mp.get(rule.getDest()); res.addRule(new TreeAutomatonRule(rule.getLetter(), src, dest)); } return res; } /*** * Remove a given state from all the rules * * @param st */ public void removeState(final STATE st) { final Set> badRules = new HashSet<>(); for (final TreeAutomatonRule rule : mRules) { if (ruleContains(rule, st)) { badRules.add(rule); } } for (final Entry, Map>>> e : mParentsMap .entrySet()) { for (final Entry>> e2 : e.getValue().entrySet()) { e2.getValue().removeAll(badRules); } } for (final Entry>>> e : mChildrenMap .entrySet()) { for (final Entry>> e2 : e.getValue().entrySet()) { e2.getValue().removeAll(badRules); } } for (final Entry>> e : mLettersMap.entrySet()) { e.getValue().removeAll(badRules); } for (final Entry>> e : mSourceMap.entrySet()) { e.getValue().removeAll(badRules); } mRules.removeAll(badRules); mStates.remove(st); mFinalStates.remove(st); } @Override public int size() { return mStates.size(); } @Override public String sizeInformation() { return size() + " nodes"; } @Override public String toString() { final StringBuilder alphabet = new StringBuilder(); for (final LETTER letter : this.mAlphabet) { if (alphabet.length() > 0) { alphabet.append(" "); } alphabet.append('"'); alphabet.append(letter); alphabet.append('"'); } final StringBuilder states = new StringBuilder(); for (final STATE state : this.mStates) { if (states.length() > 0) { states.append(" "); } states.append('"'); states.append(state); states.append('"'); } final StringBuilder finalStates = new StringBuilder(); for (final STATE state : this.mFinalStates) { if (finalStates.length() > 0) { finalStates.append(" "); } finalStates.append('"'); finalStates.append(state); finalStates.append('"'); } final StringBuilder transitionTable = new StringBuilder(); for (final TreeAutomatonRule rule : getRules()) { if (transitionTable.length() > 0) { transitionTable.append("\n"); } final StringBuilder src = new StringBuilder(); for (final STATE st : rule.getSource()) { if (src.length() > 0) { src.append(" "); } src.append('"'); src.append(st); src.append('"'); } final StringBuilder dest = new StringBuilder(); dest.append('"'); dest.append(rule.getDest()); dest.append('"'); final StringBuilder letter = new StringBuilder(); letter.append('"'); letter.append(rule.getLetter()); letter.append('"'); transitionTable.append(String.format("\t\t((%s) %s %s)", src, letter, dest)); } return String.format( // "TreeAutomaton %s = " + "TreeAutomaton(\n" + "\talphabet = {%s},\n" + "\tstates = {%s},\n" + "\tfinalStates = {%s},\n" + "\ttransitionTable = {\n%s\n\t}\n)", // "ta" + hashCode() % 1000000, alphabet, states, finalStates, transitionTable); } private boolean ruleContains(final TreeAutomatonRule rule, final STATE st) { if (rule.getDest().equals(st)) { return true; } for (final STATE state : rule.getSource()) { if (state.equals(st)) { return true; } } return false; } private String stateString(final STATE state) { final StringBuilder res = new StringBuilder(state.toString()); res.append('"'); if (isFinalState(state)) { res.append("*"); } res.append('"'); return res.toString(); } @Override public IElement transformToUltimateModel(final AutomataLibraryServices services) throws AutomataOperationCanceledException { // TODO Auto-generated method stub return null; } @Override public Iterable> getSourceCombinations() { return mParentsMap.keySet(); } }