edu.buaa.satla.analysis.core.arg.ARGUtils.java Source code

Java tutorial

Introduction

Here is the source code for edu.buaa.satla.analysis.core.arg.ARGUtils.java

Source

/*
 *  CPAchecker is a tool for configurable software verification.
 *  This file is part of CPAchecker.
 *
 *  Copyright (C) 2007-2014  Dirk Beyer
 *  All rights reserved.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *
 *  CPAchecker web page:
 *    http://cpachecker.sosy-lab.org
*/
package edu.buaa.satla.analysis.core.arg;

import static com.google.common.base.Preconditions.*;
import static com.google.common.collect.FluentIterable.from;
import static edu.buaa.satla.analysis.util.AbstractStates.*;
import static edu.buaa.satla.analysis.util.CFAUtils.leavingEdges;

import java.io.IOException;
import java.util.AbstractCollection;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Deque;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.sosy_lab.common.Pair;
import org.sosy_lab.cpachecker.cfa.model.AssumeEdge;
import org.sosy_lab.cpachecker.cfa.model.CFAEdge;
import org.sosy_lab.cpachecker.cfa.model.CFANode;
import org.sosy_lab.cpachecker.cfa.model.FunctionEntryNode;
import org.sosy_lab.cpachecker.cfa.model.FunctionExitNode;
import org.sosy_lab.cpachecker.cfa.model.MultiEdge;
import org.sosy_lab.cpachecker.core.CounterexampleInfo;
import org.sosy_lab.cpachecker.core.counterexample.CFAEdgeWithAssignments;
import org.sosy_lab.cpachecker.core.counterexample.CFAPathWithAssignments;
import org.sosy_lab.cpachecker.core.counterexample.Model;
import org.sosy_lab.cpachecker.core.interfaces.AbstractState;
import org.sosy_lab.cpachecker.core.reachedset.ReachedSet;

import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimaps;
import com.google.common.collect.SetMultimap;
import com.google.common.collect.Sets;
import com.google.common.collect.UnmodifiableIterator;

import edu.buaa.satla.analysis.util.AbstractStates;
import edu.buaa.satla.analysis.util.GraphUtils;
import edu.buaa.satla.analysis.util.predicates.interpolation.CounterexampleTraceInfo;

/**
 * Helper class with collection of ARG related utility methods.
 */
/**
 *
 */
public class ARGUtils {

    private ARGUtils() {
    }

    /**
     * Get all elements on all paths from the ARG root to a given element.
     *
     * @param pLastElement The last element in the paths.
     * @return A set of elements, all of which have pLastElement as their (transitive) child.
     */
    public static Set<ARGState> getAllStatesOnPathsTo(ARGState pLastElement) {

        Set<ARGState> result = new HashSet<>();
        Deque<ARGState> waitList = new ArrayDeque<>();

        result.add(pLastElement);
        waitList.add(pLastElement);

        while (!waitList.isEmpty()) {
            ARGState currentElement = waitList.poll();
            for (ARGState parent : currentElement.getParents()) {
                if (result.add(parent)) {
                    waitList.push(parent);
                }
            }
        }

        return result;
    }

    /**
     * Get all abstract states without parents.
     */
    public static Set<AbstractState> getRootStates(ReachedSet pReached) {

        Set<AbstractState> result = new HashSet<>();

        Iterator<AbstractState> it = pReached.iterator();
        while (it.hasNext()) {
            AbstractState e = it.next();
            ARGState state = AbstractStates.extractStateByType(e, ARGState.class);

            if (state.getParents().isEmpty()) {
                result.add(state);
            }
        }

        return result;
    }

    /**
     * Create a path in the ARG from root to the given element.
     * If there are several such paths, one is chosen randomly.
     *
     * @param pLastElement The last element in the path.
     * @return A path from root to lastElement.
     */
    public static ARGPath getOnePathTo(ARGState pLastElement) {
        List<ARGState> states = new ArrayList<>(); // reversed order
        Set<ARGState> seenElements = new HashSet<>();

        // each element of the path consists of the abstract state and the outgoing
        // edge to its successor

        ARGState currentARGState = pLastElement;
        states.add(currentARGState);
        seenElements.add(currentARGState);

        while (!currentARGState.getParents().isEmpty()) {
            Iterator<ARGState> parents = currentARGState.getParents().iterator();

            ARGState parentElement = parents.next();
            while (!seenElements.add(parentElement) && parents.hasNext()) {
                // while seenElements already contained parentElement, try next parent
                parentElement = parents.next();
            }

            states.add(parentElement);

            currentARGState = parentElement;
        }
        return new ARGPath(Lists.reverse(states));
    }

    /**
     * Create a path in the ARG from root to the given element.
     * If there are several such paths, one is chosen randomly.
     *
     * This is a copy of {@link #getOnePathTo(ARGState)}
     * that should be used only if a {@link MutableARGPath}
     * is strictly required as the return object
     * (we hope we can remove {@link MutableARGPath} and this method
     * sometime in the future).
     *
     * @param pLastElement The last element in the path.
     * @return A path from root to lastElement.
     */
    public static MutableARGPath getOneMutablePathTo(ARGState pLastElement) {
        MutableARGPath path = new MutableARGPath();
        Set<ARGState> seenElements = new HashSet<>();

        // each element of the path consists of the abstract state and the outgoing
        // edge to its successor

        ARGState currentARGState = pLastElement;
        // add the error node and its -first- outgoing edge
        // that edge is not important so we pick the first even
        // if there are more outgoing edges
        CFANode loc = extractLocation(currentARGState);
        CFAEdge lastEdge = leavingEdges(loc).first().orNull();
        path.addFirst(Pair.of(currentARGState, lastEdge));
        seenElements.add(currentARGState);

        while (!currentARGState.getParents().isEmpty()) {
            Iterator<ARGState> parents = currentARGState.getParents().iterator();

            ARGState parentElement = parents.next();
            while (!seenElements.add(parentElement) && parents.hasNext()) {
                // while seenElements already contained parentElement, try next parent
                parentElement = parents.next();
            }

            CFAEdge edge = parentElement.getEdgeToChild(currentARGState);
            path.addFirst(Pair.of(parentElement, edge));

            currentARGState = parentElement;
        }
        return path;
    }

    /**
     * Get one random path from the ARG root to an ARG leaf.
     * @param root The root state of an ARG (may not have any parents)
     */
    public static ARGPath getRandomPath(final ARGState root) {
        checkArgument(root.getParents().isEmpty());

        List<ARGState> states = new ArrayList<>();
        ARGState currentElement = root;
        while (currentElement.getChildren().size() > 0) {
            states.add(currentElement);
            currentElement = currentElement.getChildren().iterator().next();
        }
        states.add(currentElement);
        return new ARGPath(states);
    }

    public static final Function<ARGState, Collection<ARGState>> CHILDREN_OF_STATE = new Function<ARGState, Collection<ARGState>>() {
        @Override
        public Collection<ARGState> apply(ARGState pInput) {
            return pInput.getChildren();
        }
    };

    public static final Function<ARGState, Collection<ARGState>> PARENTS_OF_STATE = new Function<ARGState, Collection<ARGState>>() {
        @Override
        public Collection<ARGState> apply(ARGState pInput) {
            return pInput.getParents();
        }
    };

    public static final Predicate<AbstractState> AT_RELEVANT_LOCATION = Predicates
            .compose(new Predicate<CFANode>() {
                @Override
                public boolean apply(CFANode pInput) {
                    return pInput.isLoopStart() || pInput instanceof FunctionEntryNode
                            || pInput instanceof FunctionExitNode;
                }
            }, AbstractStates.EXTRACT_LOCATION);

    @SuppressWarnings("unchecked")
    public static final Predicate<ARGState> RELEVANT_STATE = Predicates.or(AbstractStates.IS_TARGET_STATE,
            AT_RELEVANT_LOCATION, new Predicate<ARGState>() {
                @Override
                public boolean apply(ARGState pInput) {
                    return !pInput.wasExpanded();
                }
            }, new Predicate<ARGState>() {
                @Override
                public boolean apply(ARGState pInput) {
                    return pInput.shouldBeHighlighted();
                }
            });

    /**
     * Project the ARG to a subset of "relevant" states.
     * The result is a SetMultimap containing the successor relationships between all relevant states.
     * A pair of states (a, b) is in the SetMultimap,
     * if there is a path through the ARG from a to b which does not pass through
     * any other relevant state.
     *
     * To get the predecessor relationship, you can use {@link Multimaps#invertFrom(com.google.common.collect.Multimap, com.google.common.collect.Multimap)}.
     *
     * @param root The start of the subgraph of the ARG to project (always considered relevant).
     * @param isRelevant The predicate determining which states are in the resulting relationship.
     */
    public static SetMultimap<ARGState, ARGState> projectARG(final ARGState root,
            final Function<? super ARGState, ? extends Iterable<ARGState>> successorFunction,
            Predicate<? super ARGState> isRelevant) {

        return GraphUtils.projectARG(root, successorFunction, isRelevant);
    }

    /**
     * Writes the ARG with the root state pRootState to pSb as a graphviz dot file
     *
     */
    public static void writeARGAsDot(Appendable pSb, ARGState pRootState) throws IOException {
        ARGToDotWriter.write(pSb, pRootState, ARGUtils.CHILDREN_OF_STATE, Predicates.alwaysTrue(),
                Predicates.alwaysFalse());
    }

    /**
     * Find a path in the ARG. The necessary information to find the path is a
     * boolean value for each branching situation that indicates which of the two
     * AssumeEdges should be taken.
     *
     * @param root The root element of the ARG (where to start the path)
     * @param arg All elements in the ARG or a subset thereof (elements outside this set will be ignored).
     * @param branchingInformation A map from ARG state ids to boolean values indicating the outgoing direction.
     * @return A path through the ARG from root to target.
     * @throws IllegalArgumentException If the direction information doesn't match the ARG or the ARG is inconsistent.
     */
    public static ARGPath getPathFromBranchingInformation(ARGState root, Set<? extends AbstractState> arg,
            Map<Integer, Boolean> branchingInformation) throws IllegalArgumentException {

        checkArgument(arg.contains(root));

        List<ARGState> states = new ArrayList<>();
        List<CFAEdge> edges = new ArrayList<>();
        ARGState currentElement = root;
        while (!currentElement.isTarget()) {
            Collection<ARGState> children = currentElement.getChildren();

            ARGState child;
            CFAEdge edge;
            switch (children.size()) {

            case 0:
                throw new IllegalArgumentException("ARG target path terminates without reaching target state!");

            case 1: // only one successor, easy
                child = Iterables.getOnlyElement(children);
                edge = currentElement.getEdgeToChild(child);
                break;

            case 2: // branch
                // first, find out the edges and the children
                CFAEdge trueEdge = null;
                CFAEdge falseEdge = null;
                ARGState trueChild = null;
                ARGState falseChild = null;

                CFANode loc = AbstractStates.extractLocation(currentElement);
                if (!leavingEdges(loc).allMatch(Predicates.instanceOf(AssumeEdge.class))) {
                    Set<ARGState> candidates = Sets.intersection(Sets.newHashSet(children), arg).immutableCopy();
                    if (candidates.size() != 1) {
                        throw new IllegalArgumentException("ARG branches where there is no AssumeEdge!");
                    }
                    child = Iterables.getOnlyElement(candidates);
                    edge = currentElement.getEdgeToChild(child);
                    break;
                }

                for (ARGState currentChild : children) {
                    CFAEdge currentEdge = currentElement.getEdgeToChild(currentChild);
                    if (((AssumeEdge) currentEdge).getTruthAssumption()) {
                        trueEdge = currentEdge;
                        trueChild = currentChild;
                    } else {
                        falseEdge = currentEdge;
                        falseChild = currentChild;
                    }
                }
                if (trueEdge == null || falseEdge == null) {
                    throw new IllegalArgumentException("ARG branches with non-complementary AssumeEdges!");
                }
                assert trueChild != null;
                assert falseChild != null;

                // search first idx where we have a predicate for the current branching
                Boolean predValue = branchingInformation.get(currentElement.getStateId());
                if (predValue == null) {
                    throw new IllegalArgumentException("ARG branches without direction information!");
                }

                // now select the right edge
                if (predValue) {
                    edge = trueEdge;
                    child = trueChild;
                } else {
                    edge = falseEdge;
                    child = falseChild;
                }
                break;

            default:
                Set<ARGState> candidates = Sets.intersection(Sets.newHashSet(children), arg).immutableCopy();
                if (candidates.size() != 1) {
                    throw new IllegalArgumentException("ARG splits with more than two branches!");
                }
                child = Iterables.getOnlyElement(candidates);
                edge = currentElement.getEdgeToChild(child);
                break;
            }

            if (!arg.contains(child)) {
                throw new IllegalArgumentException("ARG and direction information from solver disagree!");
            }

            states.add(currentElement);
            edges.add(edge);
            currentElement = child;
        }

        // need to add another pair with target state and one (arbitrary) outgoing edge
        CFANode loc = extractLocation(currentElement);
        CFAEdge lastEdge = leavingEdges(loc).first().orNull();
        states.add(currentElement);
        edges.add(lastEdge);

        return new ARGPath(states, edges);
    }

    /**
     * Find a path in the ARG. The necessary information to find the path is a
     * boolean value for each branching situation that indicates which of the two
     * AssumeEdges should be taken.
     * This method checks that the path ends in a certain element.
     *
     * @param root The root element of the ARG (where to start the path)
     * @param target The target state (where to end the path, needs to be a target state)
     * @param arg All elements in the ARG or a subset thereof (elements outside this set will be ignored).
     * @param branchingInformation A map from ARG state ids to boolean values indicating the outgoing direction.
     * @return A path through the ARG from root to target.
     * @throws IllegalArgumentException If the direction information doesn't match the ARG or the ARG is inconsistent.
     */
    public static ARGPath getPathFromBranchingInformation(ARGState root, ARGState target,
            Set<? extends AbstractState> arg, Map<Integer, Boolean> branchingInformation)
            throws IllegalArgumentException {

        checkArgument(arg.contains(target));
        checkArgument(target.isTarget());

        ARGPath result = getPathFromBranchingInformation(root, arg, branchingInformation);

        if (result.getLastState() != target) {
            throw new IllegalArgumentException("ARG target path reached the wrong target state!");
        }

        return result;
    }

    /**
     * This method gets all children from an ARGState,
     * but replaces all covered states by their respective covering state.
     * It can be seen as giving a view of the ARG where the covered states are
     * transparently replaced by their covering state.
     *
     * The returned collection is unmodifiable and a live view of the children of
     * the given state.
     *
     * @param s an ARGState
     * @return The children with covered states transparently replaced.
     */
    public static final Collection<ARGState> getUncoveredChildrenView(final ARGState s) {
        return new AbstractCollection<ARGState>() {

            @Override
            public Iterator<ARGState> iterator() {

                return new UnmodifiableIterator<ARGState>() {
                    private final Iterator<ARGState> children = s.getChildren().iterator();

                    @Override
                    public boolean hasNext() {
                        return children.hasNext();
                    }

                    @Override
                    public ARGState next() {
                        ARGState child = children.next();
                        if (child.isCovered()) {
                            return checkNotNull(child.getCoveringState());
                        }
                        return child;
                    }
                };
            }

            @Override
            public int size() {
                return s.getChildren().size();
            }
        };
    }

    /**
     * Check consistency of ARG, and consistency between ARG and reached set.
     *
     * Checks we do here currently:
     * - child-parent relationship of ARG states
     * - states in ARG are also in reached set and vice versa (as far as possible to check)
     * - no destroyed states present
     *
     * This method is potentially expensive,
     * and should be called only from an assert statement.
     * @return <code>true</code>
     * @throws AssertionError If any consistency check is violated.
     */
    public static boolean checkARG(ReachedSet pReached) {
        // Not all states in ARG might be reachable from a single root state
        // in case of multiple initial states and disjoint ARGs.

        for (ARGState e : from(pReached).transform(toState(ARGState.class))) {
            assert e != null : "Reached set contains abstract state without ARGState.";
            assert !e.isDestroyed() : "Reached set contains destroyed ARGState, which should have been removed.";

            for (ARGState parent : e.getParents()) {
                assert parent.getChildren().contains(e) : "Reference from parent to child is missing in ARG";
                assert pReached.contains(parent) : "Referenced parent is missing in reached";
            }

            for (ARGState child : e.getChildren()) {
                assert child.getParents().contains(e) : "Reference from child to parent is missing in ARG";

                // Usually, all children should be in reached set, with two exceptions.
                // 1) Covered states need not be in the reached set (this depends on cpa.arg.keepCoveredStatesInReached),
                // but if they are not in the reached set, they may not have children.
                // 2) If the state is the sibling of the target state, it might have not
                // been added to the reached set if CPAAlgorithm stopped before.
                // But in this case its parent is in the waitlist.

                if (!pReached.contains(child)) {
                    assert (child.isCovered() && child.getChildren().isEmpty()) // 1)
                            || pReached.getWaitlist().containsAll(child.getParents()) // 2)
                    : "Referenced child is missing in reached set.";
                }
            }
        }

        return true;
    }

    public static void produceTestGenPathAutomaton(Appendable sb, String name,
            CounterexampleTraceInfo pCounterExampleTrace) throws IOException {

        Model model = pCounterExampleTrace.getModel();
        CFAPathWithAssignments assignmentCFAPath = model.getCFAPathWithAssignments();

        int stateCounter = 1;

        sb.append("CONTROL AUTOMATON " + name + "\n\n");
        sb.append("INITIAL STATE STATE" + stateCounter + ";\n\n");

        for (Iterator<CFAEdgeWithAssignments> it = assignmentCFAPath.iterator(); it.hasNext();) {
            CFAEdgeWithAssignments edge = it.next();

            sb.append("STATE USEFIRST STATE" + stateCounter + " :\n");

            sb.append("    MATCH \"");
            escape(edge.getCFAEdge().getRawStatement(), sb);
            sb.append("\" -> ");

            if (it.hasNext()) {
                String code = edge.getAsCode();
                String assumption = code == null ? "" : "ASSUME {" + code + "}";
                sb.append(assumption + "GOTO STATE" + ++stateCounter);
            } else {
                sb.append("GOTO EndLoop");
            }

            sb.append(";\n");
            sb.append("    TRUE -> STOP;\n\n");

        }

        //sb.append("    TRUE -> STOP;\n\n");
        sb.append("STATE USEFIRST EndLoop" + " :\n");
        sb.append("    MATCH EXIT -> BREAK;\n");
        sb.append("    TRUE -> GOTO EndLoop;\n\n");

        sb.append("END AUTOMATON\n");
    }

    /**
     * Produce an automaton in the format for the AutomatonCPA from
     * a given path. The automaton matches exactly the edges along the path.
     * If there is a target state, it is signaled as an error state in the automaton.
     * @param sb Where to write the automaton to
     * @param pRootState The root of the ARG
     * @param pPathStates The states along the path
     * @param pCounterExample Given to try to write exact variable assignment values
     * into the automaton
     * @throws IOException
     */
    public static void produceTestGenPathAutomaton(Appendable sb, ARGState pRootState, Set<ARGState> pPathStates,
            String name, CounterexampleInfo pCounterExample, boolean generateAssumes) throws IOException {
        checkNotNull(pCounterExample);

        Map<ARGState, CFAEdgeWithAssignments> valueMap = null;

        Model model = pCounterExample.getTargetPathModel();
        CFAPathWithAssignments cfaPath = model.getCFAPathWithAssignments();
        if (cfaPath != null) {
            ARGPath targetPath = pCounterExample.getTargetPath();
            valueMap = model.getExactVariableValues(targetPath);
        }

        sb.append("CONTROL AUTOMATON " + name + "\n\n");
        sb.append("INITIAL STATE ARG" + pRootState.getStateId() + ";\n\n");

        int multiEdgeCount = 0; // see below
        final ARGState lastState = pCounterExample.getTargetPath().getLastState();
        for (ARGState s : pPathStates) {

            CFANode loc = AbstractStates.extractLocation(s);
            sb.append("STATE USEFIRST ARG" + s.getStateId() + " :\n");
            for (ARGState child : s.getChildren()) {
                if (child.isCovered()) {
                    child = child.getCoveringState();
                    assert !child.isCovered();
                }

                if (pPathStates.contains(child)) {
                    CFANode childLoc = AbstractStates.extractLocation(child);
                    CFAEdge edge = loc.getEdgeTo(childLoc);
                    if (edge instanceof MultiEdge) {
                        // The successor state might have several incoming MultiEdges.
                        // In this case the state names like ARG<successor>_0 would occur
                        // several times.
                        // So we add this counter to the state names to make them unique.
                        multiEdgeCount++;

                        // Write out a long linear chain of pseudo-states
                        // because the AutomatonCPA also iterates through the MultiEdge.
                        List<CFAEdge> edges = ((MultiEdge) edge).getEdges();

                        // first, write edge entering the list
                        int i = 0;
                        sb.append("    MATCH \"");
                        escape(edges.get(i).getRawStatement(), sb);
                        sb.append("\" -> ");
                        sb.append("GOTO ARG" + child.getStateId() + "_" + (i + 1) + "_" + multiEdgeCount);
                        sb.append(";\n");

                        // inner part (without first and last edge)
                        for (; i < edges.size() - 1; i++) {
                            sb.append("STATE USEFIRST ARG" + child.getStateId() + "_" + i + "_" + multiEdgeCount
                                    + " :\n");
                            sb.append("    MATCH \"");
                            escape(edges.get(i).getRawStatement(), sb);
                            sb.append("\" -> ");
                            sb.append("GOTO ARG" + child.getStateId() + "_" + (i + 1) + "_" + multiEdgeCount);
                            sb.append(";\n");
                        }

                        // last edge connecting it with the real successor
                        edge = edges.get(i);
                        sb.append("STATE USEFIRST ARG" + child.getStateId() + "_" + i + "_" + multiEdgeCount
                                + " :\n");
                        // remainder is written by code below
                    }

                    sb.append("    MATCH \"");
                    escape(edge.getRawStatement(), sb);
                    sb.append("\" -> ");

                    if (child.isTarget()) {
                        sb.append("ERROR");
                    } else {
                        String assumption = "";
                        if (generateAssumes) {
                            assumption = getAssumption(valueMap, s);
                        }
                        sb.append(assumption + "GOTO ARG" + child.getStateId());
                    }
                    sb.append(";\n");
                }
            }
            if (!s.equals(lastState)) {
                sb.append("    TRUE -> STOP;\n\n");
            }
        }

        CFAEdge lastEdge = Iterables.getLast(pCounterExample.getTargetPath().asEdgesList());
        if (lastEdge != null) {
            sb.append("    MATCH \"");
            escape(lastEdge.getRawStatement(), sb);
            sb.append("\" -> ");
            sb.append("GOTO EndLoop");
            sb.append(";\n");
            sb.append("    TRUE -> STOP;\n\n");
            //        lastElement.getSecond().getRawStatement()
            sb.append("STATE USEFIRST EndLoop" + " :\n");
            sb.append("    MATCH EXIT -> BREAK;\n");
            sb.append("    TRUE -> GOTO EndLoop;\n\n");

        } else {
            sb.append("    TRUE -> STOP;\n\n");
        }
        sb.append("END AUTOMATON\n");
    }

    /**
     * Produce an automaton in the format for the AutomatonCPA from
     * a given path. The automaton matches exactly the edges along the path.
     * If there is a target state, it is signaled as an error state in the automaton.
     * @param sb Where to write the automaton to
     * @param pRootState The root of the ARG
     * @param pPathStates The states along the path
     * @param pCounterExample Given to try to write exact variable assignment values
     * into the automaton, may be null
     * @throws IOException
     */
    public static void producePathAutomaton(Appendable sb, ARGState pRootState, Set<ARGState> pPathStates,
            String name, CounterexampleInfo pCounterExample) throws IOException {

        Map<ARGState, CFAEdgeWithAssignments> valueMap = null;

        if (pCounterExample != null) {
            Model model = pCounterExample.getTargetPathModel();
            CFAPathWithAssignments cfaPath = model.getCFAPathWithAssignments();
            if (cfaPath != null) {
                ARGPath targetPath = pCounterExample.getTargetPath();
                valueMap = model.getExactVariableValues(targetPath);
            }
        }

        sb.append("CONTROL AUTOMATON " + name + "\n\n");
        sb.append("INITIAL STATE ARG" + pRootState.getStateId() + ";\n\n");

        int multiEdgeCount = 0; // see below

        for (ARGState s : pPathStates) {

            CFANode loc = AbstractStates.extractLocation(s);
            sb.append("STATE USEFIRST ARG" + s.getStateId() + " :\n");

            for (ARGState child : s.getChildren()) {
                if (child.isCovered()) {
                    child = child.getCoveringState();
                    assert !child.isCovered();
                }

                if (pPathStates.contains(child)) {
                    CFANode childLoc = AbstractStates.extractLocation(child);
                    CFAEdge edge = loc.getEdgeTo(childLoc);
                    if (edge instanceof MultiEdge) {
                        // The successor state might have several incoming MultiEdges.
                        // In this case the state names like ARG<successor>_0 would occur
                        // several times.
                        // So we add this counter to the state names to make them unique.
                        multiEdgeCount++;

                        // Write out a long linear chain of pseudo-states
                        // because the AutomatonCPA also iterates through the MultiEdge.
                        List<CFAEdge> edges = ((MultiEdge) edge).getEdges();

                        // first, write edge entering the list
                        int i = 0;
                        sb.append("    MATCH \"");
                        escape(edges.get(i).getRawStatement(), sb);
                        sb.append("\" -> ");
                        sb.append("GOTO ARG" + child.getStateId() + "_" + (i + 1) + "_" + multiEdgeCount);
                        sb.append(";\n");

                        // inner part (without first and last edge)
                        for (; i < edges.size() - 1; i++) {
                            sb.append("STATE USEFIRST ARG" + child.getStateId() + "_" + i + "_" + multiEdgeCount
                                    + " :\n");
                            sb.append("    MATCH \"");
                            escape(edges.get(i).getRawStatement(), sb);
                            sb.append("\" -> ");
                            sb.append("GOTO ARG" + child.getStateId() + "_" + (i + 1) + "_" + multiEdgeCount);
                            sb.append(";\n");
                        }

                        // last edge connecting it with the real successor
                        edge = edges.get(i);
                        sb.append("STATE USEFIRST ARG" + child.getStateId() + "_" + i + "_" + multiEdgeCount
                                + " :\n");
                        // remainder is written by code below
                    }

                    sb.append("    MATCH \"");
                    escape(edge.getRawStatement(), sb);
                    sb.append("\" -> ");

                    if (child.isTarget()) {
                        sb.append("ERROR");
                    } else {
                        String assumption = getAssumption(valueMap, s);
                        sb.append(assumption + "GOTO ARG" + child.getStateId());
                    }
                    sb.append(";\n");
                }
            }
            sb.append("    TRUE -> STOP;\n\n");
        }
        sb.append("END AUTOMATON\n");
    }

    private static String getAssumption(Map<ARGState, CFAEdgeWithAssignments> pValueMap, ARGState pState) {

        String assumption = "";

        if (pValueMap != null && pValueMap.containsKey(pState)) {

            CFAEdgeWithAssignments cfaEdgeWithAssignments = pValueMap.get(pState);

            String code = cfaEdgeWithAssignments.getAsCode();

            if (code != null) {
                assumption = "ASSUME {" + code + "}";
            }
        }

        return assumption;
    }

    private static void escape(String s, Appendable appendTo) throws IOException {
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            switch (c) {
            case '\n':
                appendTo.append("\\n");
                break;
            case '\r':
                appendTo.append("\\r");
                break;
            case '\"':
                appendTo.append("\\\"");
                break;
            case '\\':
                appendTo.append("\\\\");
                break;
            default:
                appendTo.append(c);
                break;
            }
        }
    }
}