Index: /trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usability/CMDgetTaskModelCrossCoverage.java
===================================================================
--- /trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usability/CMDgetTaskModelCrossCoverage.java	(revision 2034)
+++ /trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usability/CMDgetTaskModelCrossCoverage.java	(revision 2034)
@@ -0,0 +1,2144 @@
+//   Copyright 2012 Georg-August-Universität Göttingen, Germany
+//
+//   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.
+
+package de.ugoe.cs.autoquest.commands.usability;
+
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.IdentityHashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Stack;
+
+import de.ugoe.cs.autoquest.CommandHelpers;
+import de.ugoe.cs.autoquest.eventcore.IEventTarget;
+import de.ugoe.cs.autoquest.tasktrees.taskequality.TaskEquality;
+import de.ugoe.cs.autoquest.tasktrees.temporalrelation.TaskHandlingStrategy;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.DefaultTaskInstanceTraversingVisitor;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTask;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTaskInstance;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.IIteration;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.IOptional;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ISelection;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequence;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITask;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInstance;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskModel;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.IUserSession;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.TaskTreeUtils;
+import de.ugoe.cs.autoquest.usageprofiles.SymbolMap;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+import de.ugoe.cs.util.console.GlobalDataContainer;
+
+/**
+ * <p>
+ * compares a set of task models with each other and drops their similarity as results
+ * </p>
+ * 
+ * @author Patrick Harms
+ * @version 1.0
+ */
+public class CMDgetTaskModelCrossCoverage implements Command {
+    
+    /** */
+    private static Object EOF = new Object();
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.util.console.Command#help()
+     */
+    @Override
+    public String help() {
+        return "getTaskModelCrossCoverage <tasktree1> <tasktree2> ...";
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.util.console.Command#run(java.util.List)
+     */
+    @Override
+    public void run(List<Object> parameters) {
+        List<String> inputTaskTreeNames = new LinkedList<>();
+        
+        try {
+            for (Object param : parameters) {
+                inputTaskTreeNames.add((String) param);
+            }
+        }
+        catch (Exception e) {
+            throw new IllegalArgumentException("must provide valid input task tree names");
+        }
+
+        Map<ITaskModel, List<List<IEventTask>>> inputTaskModels = new IdentityHashMap<>();
+        ITaskModel firstModel = null;
+        final SymbolMap<ITask, IEventTask> eventTasksOfFirstModel =
+            new TaskHandlingStrategy(TaskEquality.SEMANTICALLY_EQUAL).createSymbolMap();
+
+        
+        for (String inputTaskTreeName : inputTaskTreeNames) {
+            Object dataObject = GlobalDataContainer.getInstance().getData(inputTaskTreeName);
+            if (dataObject == null) {
+                CommandHelpers.objectNotFoundMessage(inputTaskTreeName);
+                continue;
+            }
+            if (!(dataObject instanceof ITaskModel)) {
+                CommandHelpers.objectNotType(inputTaskTreeName, "ITaskModel");
+                continue;
+            }
+            
+            System.out.println("preparing sessions to check for " + dataObject);
+            List<List<IEventTask>> sessionsToCheck = new LinkedList<>();
+            final Map<IEventTask, IEventTask> identityMap = new IdentityHashMap<>();
+            
+            for (IUserSession session : ((ITaskModel) dataObject).getUserSessions()) {
+                final List<IEventTask> eventTasks = new ArrayList<>();
+                for (ITaskInstance instance : session) {
+                    instance.accept(new DefaultTaskInstanceTraversingVisitor() {
+                        @Override
+                        public void visit(IEventTaskInstance eventTaskInstance) {
+                            IEventTask eventTask = eventTaskInstance.getEventTask();
+                            IEventTask replacement = identityMap.get(eventTask);
+
+                            if (replacement == null) {
+                                replacement = eventTasksOfFirstModel.getValue(eventTask);
+                                
+                                if (replacement == null) {
+                                    replacement = eventTaskInstance.getEventTask();
+                                    eventTasksOfFirstModel.addSymbol(replacement, replacement);
+                                }
+                                
+                                identityMap.put(eventTask, replacement);
+                            }
+                            
+                            eventTasks.add(replacement);
+                        }
+                    });
+                }
+                
+                sessionsToCheck.add(eventTasks);
+            }
+            
+            inputTaskModels.put((ITaskModel) dataObject, sessionsToCheck);
+            
+            if (firstModel == null) {
+                firstModel = (ITaskModel) dataObject;
+            }
+        }
+        
+        getTaskModelCrossCoverage(firstModel, inputTaskModels);
+    }
+
+    /**
+     *
+     */
+    private void getTaskModelCrossCoverage(ITaskModel                              modelToCompare,
+                                           Map<ITaskModel, List<List<IEventTask>>> sessionsToCheck)
+    {
+        System.out.println("creating parsing tables for " + modelToCompare);
+        List<ParsingTable> parsingTables = getParsingTables(modelToCompare);
+        
+        for (Map.Entry<ITaskModel, List<List<IEventTask>>> entry : sessionsToCheck.entrySet()) {
+            if (entry.getKey() != modelToCompare) {
+                System.out.println("performing parsing against " + entry.getKey());
+                Statistics statistics = new Statistics(modelToCompare, entry.getKey());
+                
+                parse(entry.getValue(), parsingTables, statistics);
+                
+                statistics.dump(System.out);
+            }
+        }
+    }
+
+    /**
+     *
+     */
+    private List<ParsingTable> getParsingTables(ITaskModel forTaskModel) {
+        List<ProductionRule> rules = new LinkedList<>();
+        
+        for (ITask task : forTaskModel.getTasks()) {
+            if (task instanceof ISequence) {
+                // other task types will be handled implicitly
+                addRulesIfNotThere(rules, createProductionRules((ISequence) task));
+            }
+        }
+        
+        List<ParsingTable> tables = new ArrayList<ParsingTable>();
+        
+        int count = 0;
+        for (ITask task : forTaskModel.getTasks()) {
+            if (task instanceof ISequence) {
+                try {
+                    tables.add(createParsingTable((ISequence) task, getRelevantRules(task, rules)));
+                }
+                catch (IllegalArgumentException e) {
+                    Console.println(e.toString() + "  " + ++count);
+                }
+            }
+        }
+        
+        return tables;
+    }
+
+    /**
+     *
+     */
+    private List<ProductionRule> createProductionRules(ISequence sequence) {
+        //new TaskTreeEncoder().encode(sequence, System.out);
+        
+        List<ProductionRule> rules = new LinkedList<ProductionRule>();
+        
+        List<ExecutionVariants> executionVariants = new ArrayList<>(sequence.getChildren().size());
+        int noOfOptionalChildren = 0;
+        
+        for (ITask child : sequence.getChildren()) {
+            ExecutionVariants variants = getExecutionVariants(child);
+            if (variants.canBeOptional()) {
+                noOfOptionalChildren++;
+            }
+            executionVariants.add(variants);
+        }
+        
+        // for iterations and selections, we create non terminals
+        // for optional children, we create multiple production for the current sequence
+        // all other children are just added
+        
+        for (int i = 0; i < Math.pow(2, noOfOptionalChildren); i++) {
+            int childIndex = 0;
+            int optionalIndex = 0;
+            List<Object> rightHandSide = new ArrayList<>();
+            
+            for (ITask child : sequence.getChildren()) {
+                boolean leaveout = false;
+                if (child instanceof IEventTask) {
+                    rightHandSide.add(child);
+                    childIndex++;
+                    continue;
+                }
+                else if (executionVariants.get(childIndex).canBeOptional()) {
+                    // child is optional, check if to be left out
+                    if ((((int) Math.pow(2, optionalIndex)) & i) == 0) {
+                        leaveout = true;
+                    }
+                    
+                    optionalIndex++;
+                }
+                
+                if (leaveout) {
+                    childIndex++;
+                    continue;
+                }
+                
+                Object childSymbol = null;
+                
+                if (executionVariants.get(childIndex).size() > 1) {
+                    // create selective production rules
+                    childSymbol = sequence.getId() + "_sel" + childIndex;
+                    
+                    int variantIndex = 0;
+                    for (ExecutionVariant executionVariant : executionVariants.get(childIndex)) {
+                        if (!executionVariant.canBeIterated) {
+                            rules.add(new ProductionRule
+                                          ((String) childSymbol,
+                                           new Object[] { getSymbol(executionVariant.task) }));
+                        }
+                        else {
+                            // create iterative production rules
+                            String iterationSymbol = childSymbol + "_it" + variantIndex;
+                            
+                            rules.add(new ProductionRule
+                                          (iterationSymbol,
+                                           new Object[] { iterationSymbol, executionVariant.task }));
+                            rules.add(new ProductionRule(iterationSymbol,
+                                                         new Object[] { executionVariant.task }));
+                            
+                            rules.add(new ProductionRule((String) childSymbol,
+                                                         new Object[] { iterationSymbol }));
+                        }
+                        
+                        variantIndex++;
+                    }
+                }
+                else {
+                    childSymbol = getSymbol(executionVariants.get(childIndex).get(0).task);
+                }
+                
+                if (isIteration(child)) {
+                    // create iterative production rules
+                    String iterationSymbol = sequence.getId() + "_it" + childIndex;
+                    
+                    rules.add(new ProductionRule(iterationSymbol,
+                                                 new Object[] { iterationSymbol, childSymbol }));
+                    rules.add(new ProductionRule(iterationSymbol, new Object[] { childSymbol }));
+                    
+                    childSymbol = iterationSymbol;
+                }
+                
+                rightHandSide.add(childSymbol);
+                
+                childIndex++;
+            }
+            
+            rules.add(new ProductionRule(Integer.toString(sequence.getId()),
+                                         rightHandSide.toArray()));
+        
+        }
+        
+        return rules;
+    }
+
+    /**
+     * 
+     */
+    private boolean isIteration(ITask child) {
+        if (child instanceof IIteration) {
+            return true;
+        }
+        else if (child instanceof IOptional) {
+            return isIteration(((IOptional) child).getMarkedTask());
+        }
+        
+        return false;
+    }
+
+    /**
+     * 
+     */
+    private Object getSymbol(ITask executionVariant) {
+        if (executionVariant instanceof IEventTask) {
+            return executionVariant;
+        }
+        else {
+            return Integer.toString(executionVariant.getId());
+        }
+    }
+
+    /**
+     *
+     */
+    private ExecutionVariants getExecutionVariants(ITask task) {
+        if (task instanceof IOptional) {
+            ExecutionVariants result = getExecutionVariants(((IOptional) task).getMarkedTask());
+            result.setCanBeOptional();
+            return result;
+        }
+        else if (task instanceof ISelection) {
+            ExecutionVariants result = new ExecutionVariants();
+            for (ITask child : ((ISelection) task).getChildren()) {
+                result.addAll(getExecutionVariants(child));
+            }
+            return result;
+        }
+        else if (task instanceof IIteration) {
+            ExecutionVariants result = getExecutionVariants(((IIteration) task).getMarkedTask());
+            result.setCanBeIterated();
+            return result;
+        }
+        else {
+            ExecutionVariants result = new ExecutionVariants();
+            result.add(task);
+            return result;
+        }
+    }
+
+    /**
+     *
+     */
+    private void addRulesIfNotThere(List<ProductionRule> rules,
+                                    List<ProductionRule> newRules)
+    {
+        for (ProductionRule newRule : newRules) {
+            boolean found = false;
+            for (ProductionRule candidate : rules) {
+                if (candidate.equals(newRule)) {
+                    found = true;
+                    break;
+                }
+            }
+            
+            if (!found) {
+                rules.add(newRule);
+            }
+        }
+    }
+
+    /**
+     * 
+     */
+    private List<ProductionRule> getRelevantRules(ITask task, List<ProductionRule> rules) {
+        List<ProductionRule> relevantRules = new LinkedList<>();
+        Set<String> handledNonTerminals = new HashSet<>();
+
+        Stack<String> nonTerminals = new Stack<String>();
+        nonTerminals.push((String) getSymbol(task));
+        
+        // search for relevant rules that belong to the grammar initiated by the initial rule
+        do {
+            String nonTerminal = nonTerminals.pop();
+            handledNonTerminals.add(nonTerminal);
+            
+            for (ProductionRule candidate : rules) {
+                if (candidate.leftHandSide.equals(nonTerminal)) {
+                    relevantRules.add(candidate);
+                    for (int i = 0; i < candidate.rightHandSide.length; i++) {
+                        if ((candidate.rightHandSide[i] instanceof String) &&
+                            !handledNonTerminals.contains((String) candidate.rightHandSide[i]))
+                        {
+                            nonTerminals.push((String) candidate.rightHandSide[i]);
+                        }
+                    }
+                }
+            }
+        }
+        while (nonTerminals.size() > 0);
+        
+        return relevantRules;
+    }
+
+    /**
+     *
+     */
+    private ParsingTable createParsingTable(ISequence            task,
+                                            List<ProductionRule> rules)
+    {
+        ParsingTable table = new ParsingTable(task);
+        
+        List<ProductionRule> fullRuleSet = new LinkedList<>(rules);
+        ProductionRule initialRule = new ProductionRule("S'", new Object[] { getSymbol(task), EOF });
+        fullRuleSet.add(0, initialRule);
+        ProductionRulePosition initialPosition = new ProductionRulePosition(initialRule, 0);
+        State initialState = new State();
+        initialState.add(initialPosition);
+        
+        // create the state machine as typical for an LR(1) parser
+        List<State> states = new LinkedList<>();
+        states.add(closure(initialState, fullRuleSet));
+        
+        List<Edge> edges = new LinkedList<>();
+        
+        int noOfStates;
+        int noOfEdges;
+        
+        do {
+            noOfStates = states.size();
+            noOfEdges = edges.size();
+            
+            List<State> newStates = new LinkedList<State>();
+            List<Edge> newEdges = new LinkedList<Edge>();
+            
+            for (State state : states) {
+                for (ProductionRulePosition position : state) {
+                    if (position.getNextSymbol() != null) {
+                        State newState = gotoFunction(state, position.getNextSymbol(), fullRuleSet);
+                        if (newState.size() > 0) {
+                            State existing = null;
+                            
+                            for (State candidate : states) {
+                                if (candidate.equals(newState)) {
+                                    existing = candidate;
+                                    break;
+                                }
+                            }
+                            
+                            if (existing == null) {
+                                // check if the state is one of the new states
+                                for (State candidate : newStates) {
+                                    if (candidate.equals(newState)) {
+                                        existing = candidate;
+                                        break;
+                                    }
+                                }
+                            }
+                            
+                            if (existing != null) {
+                                newEdges.add(new Edge(state, existing, position.getNextSymbol()));
+                            }
+                            else {
+                                newStates.add(newState);
+                                newEdges.add(new Edge(state, newState, position.getNextSymbol()));
+                            }
+                        }
+                    }
+                }
+            }
+            
+            addStatesIfNotContained(states, newStates);
+            addEdgesIfNotContained(edges, newEdges);
+        }
+        while ((noOfStates != states.size()) && (noOfEdges != edges.size()));
+        
+        /*System.out.println("\n\n\n\n");
+        new TaskTreeEncoder().encode(task, System.out);
+
+        System.out.println("\n");
+        for (ProductionRule rule : fullRuleSet) {
+            System.out.println(rule);
+        }
+
+        System.out.println("\n");
+        for (State state : states) {
+            System.out.println(state);
+        }
+        
+        System.out.println("\n");
+        for (Edge edge : edges) {
+            System.out.println
+                ("edge from " + edge.source.id + "  to  " + edge.dest.id + "  on " + edge.symbol);
+        }*/
+        
+        // now create the table based on the states and edges
+        for (Edge edge : edges) {
+            if ((edge.symbol instanceof IEventTask) || (edge.symbol == EOF)) {
+                table.addShift(edge.dest, edge.source, edge.symbol);
+            }
+            else {
+                table.addGoto(edge.dest, edge.source, edge.symbol);
+            }
+        }
+        
+        for (State state : states) {
+            for (ProductionRulePosition position : state) {
+                if (position.getNextSymbol() == null) {
+                    Set<Object> followSet =
+                        getFollowSet(position.rule.leftHandSide, initialRule, fullRuleSet);
+                    
+                    for (Object symbol : followSet) {
+                        table.addReduce(state, symbol, position.rule);
+                    }
+                }
+            }
+        }
+        
+        /*System.out.println("\n");
+        System.out.println(task);
+        
+        System.out.println("\n");
+        System.out.println(table);
+        
+        List<List<IEventTask>> testinputs = new LinkedList<>();
+        for (ITaskInstance instance : task.getInstances()) {
+            final List<IEventTask> eventTasks = new ArrayList<>();
+            
+            instance.accept(new DefaultTaskInstanceTraversingVisitor() {
+                @Override
+                public void visit(IEventTaskInstance eventTaskInstance) {
+                    eventTasks.add(eventTaskInstance.getEventTask());
+                }
+            });
+            
+            testinputs.add(eventTasks);
+        }
+        
+        List<ParsingTable> tables = new LinkedList<>();
+        tables.add(table);
+        parse(testinputs, tables);*/
+        
+        return table;
+    }
+
+    /**
+     *
+     */
+    private Set<Object> getFollowSet(String               leftHandSide,
+                                     ProductionRule       initialRule,
+                                     List<ProductionRule> rules)
+    {
+        // search for all elements that may follow the given left hand side.
+        // consider also other non terminals whose production rules have a right hand side
+        // ending with the given left hand side
+        
+        Set<Object> result = new HashSet<>();
+        List<ProductionRulePosition> positions = new ArrayList<ProductionRulePosition>();
+        int index = 0;
+        positions.add
+            (new ProductionRulePosition(new ProductionRule(leftHandSide, new Object[] {}), 0));
+        
+        while (index < positions.size()) {
+            ProductionRulePosition productionRulePosition = positions.get(index++);
+            String nonTerminal = productionRulePosition.rule.leftHandSide;
+            
+            List<ProductionRulePosition> newPositions = new LinkedList<>();
+            
+            if (productionRulePosition.getNextSymbol() == null) {
+                // we are at the end of the production rule. Add further production rule positions
+                // for all parent production rules
+                for (ProductionRule candidate : rules) {
+                    for (int i = 0; i < candidate.rightHandSide.length; i++) {
+                        if (nonTerminal.equals(candidate.rightHandSide[i])) {
+                            newPositions.add(new ProductionRulePosition(candidate, i + 1));
+                        }
+                    }
+                }
+            }
+            else {
+                // we are in the middle of a rule. If the next symbol is a terminal, add it to
+                // the resulting set. If not, add all rules of the respective non terminal with
+                // position 0 to the stack
+                if ((productionRulePosition.getNextSymbol() instanceof IEventTask) ||
+                    (productionRulePosition.getNextSymbol() == EOF))
+                {
+                    result.add(productionRulePosition.getNextSymbol());
+                }
+                else {
+                    for (ProductionRule candidate : rules) {
+                        if (candidate.leftHandSide.equals(productionRulePosition.getNextSymbol())) {    
+                            newPositions.add(new ProductionRulePosition(candidate, 0));
+                        }
+                    }
+                }
+            }
+            
+            for (ProductionRulePosition newPosition : newPositions) {
+                boolean found = false;
+                
+                for (ProductionRulePosition posCandidate : positions) {
+                    if (posCandidate.equals(newPosition)) {
+                        found = true;
+                        break;
+                    }
+                }
+                
+                if (!found) {
+                    positions.add(newPosition);
+                }
+            }
+        }
+        
+        return result;
+    }
+
+    /**
+     *
+     */
+    private State closure(State state, List<ProductionRule> rules) {
+        State closure = new State(state);
+        int statesize;
+        
+        do {
+            statesize = closure.size();
+            
+            List<ProductionRulePosition> newPositions = new LinkedList<>();
+            
+            for (ProductionRulePosition position : closure) {
+                Object nextSymbol = position.getNextSymbol();
+                
+                if (nextSymbol != null) {
+                    for (ProductionRule candidate : rules) {
+                        if (candidate.leftHandSide.equals(nextSymbol)) {
+                            newPositions.add(new ProductionRulePosition(candidate, 0));
+                        }
+                    }
+                }
+            }
+            
+            for (ProductionRulePosition newPosition : newPositions) {
+                boolean alreadyContained = false;
+
+                for (ProductionRulePosition existing : closure) {
+                    if ((existing.rule == newPosition.rule) &&
+                        (existing.position == newPosition.position))
+                    {
+                        alreadyContained = true;
+                        break;
+                    }
+                }
+
+                if (!alreadyContained) {
+                    closure.add(newPosition);
+                }
+            }
+        }
+        while (statesize != closure.size());
+        
+        return closure;
+    }
+
+    /**
+     * 
+     */
+    private State gotoFunction(State state, Object nextSymbol, List<ProductionRule> rules) {
+        State result = new State();
+        
+        for (ProductionRulePosition position : state) {
+            if (nextSymbol.equals(position.getNextSymbol())) {
+                ProductionRulePosition nextPosition = position.advance();
+                if (nextPosition != null) {
+                    result.add(nextPosition);
+                }
+            }
+        }
+        
+        return closure(result, rules);
+    }
+
+
+    /**
+     * 
+     */
+    private void addStatesIfNotContained(List<State> states, List<State> newStates) {
+        
+        for (State newState : newStates) {
+            boolean found = false;
+            
+            for (State candidate : states) {
+                if (candidate.equals(newState)) {
+                    found = true;
+                    break;
+                }
+            }
+            
+            if (!found) {
+                states.add(newState);
+            }
+            else {
+                throw new IllegalStateException
+                    ("the algorithm should reuse existing states and not add them again");
+            }
+        }
+    }
+
+    /**
+     * 
+     */
+    private void addEdgesIfNotContained(List<Edge> edges, List<Edge> newEdges) {
+        
+        for (Edge newEdge : newEdges) {
+            boolean found = false;
+            
+            for (Edge candidate : edges) {
+                if (candidate.equals(newEdge)) {
+                    found = true;
+                    break;
+                }
+            }
+            
+            if (!found) {
+                edges.add(newEdge);
+            }
+        }
+    }
+    
+    /**
+     *
+     */
+    private int parse(List<List<IEventTask>> inputs,
+                      List<ParsingTable>     parsingTables,
+                      Statistics             statistics)
+    {
+        int sessionIndex = 0;
+        int allActions = 0;
+        
+        ParsingTableIndex tableIndex = new ParsingTableIndex(parsingTables, statistics);
+        
+        for (List<IEventTask> input : inputs) {
+            System.out.print("parsing session " + (sessionIndex + 1) + "/" + inputs.size() + ": ");
+            
+            List<Parser> currentParsers = new LinkedList<>();
+            
+            int index = 0;
+            for (IEventTask token : input) {
+                allActions++;
+                index++;
+                
+                for (ParsingTable table : tableIndex.getRelevantTables(token)) {
+                    currentParsers.add(new Parser(table));
+                }
+                
+                //System.out.println(currentParsers.size() + "  " + token);
+                
+                List<Parser> continuingParsers = new LinkedList<>();
+                for (Parser parser : currentParsers) {
+                    Parser.Result result = parser.handleToken(token);
+                    
+                    if (result == Parser.Result.CONTINUE) {
+                        Parser alternative = getParserInSameState(continuingParsers, parser);
+                        if ((alternative == null) ||
+                            (alternative.noOfParsedTokens < parser.noOfParsedTokens))
+                        {
+                            continuingParsers.add(parser);
+                            
+                            if (parser.checkSuccess()) {
+                                //System.out.println("parser is continuing and also successing");
+                                statistics.addCoveredActions
+                                    (parser.table.sequence, sessionIndex, input.size(),
+                                     index - parser.getNoOfParsedTokens(), index - 1);
+                            }
+                        }
+                    }
+                    else if (result == Parser.Result.SUCCESS) {
+                        statistics.addCoveredActions
+                            (parser.table.sequence, sessionIndex, input.size(),
+                             index - parser.getNoOfParsedTokens(), index - 2);
+                    }
+                }
+                
+                currentParsers = continuingParsers;
+            }
+            
+            for (Parser parser : currentParsers) {
+                if (parser.checkSuccess()) {
+                    statistics.addCoveredActions
+                        (parser.table.sequence, sessionIndex, input.size(),
+                         index - parser.getNoOfParsedTokens(), index - 1);
+                }
+            }
+            
+            System.out.println
+                ("parsed " + statistics.getCoveredActions(sessionIndex) + " of " + input.size() +
+                 " actions");
+        
+            sessionIndex++;
+        }
+        
+        System.out.println
+            ("parsed " + statistics.getRecalledActions() + " of " + allActions + " actions in " +
+             inputs.size() + " sessions (" + (100 * statistics.getRecalledActions() / allActions) +
+             "%)");
+
+        
+        return statistics.getRecalledActions();
+    }
+
+    /**
+     *
+     */
+    private Parser getParserInSameState(List<Parser> existingParsers, Parser parser) {
+        Stack<StackEntry> stack2 = parser.stack;
+        
+        PARSER:
+        for (Parser existing : existingParsers) {
+            if (existing.table != parser.table) {
+                continue;
+            }
+            
+            Stack<StackEntry> stack1 = existing.stack;
+            
+            if (stack1.size() == stack2.size()) {
+                Iterator<StackEntry> it1 = stack1.iterator();
+                Iterator<StackEntry> it2 = stack1.iterator();
+                
+                while (it1.hasNext()) {
+                    StackEntry entry1 = it1.next();
+                    StackEntry entry2 = it2.next();
+                    
+                    if (entry1.state != entry2.state) {
+                        continue PARSER;
+                    }
+                    
+                    if (entry1.symbol instanceof IEventTask) {
+                        if (entry1.symbol != entry2.symbol) {
+                            continue PARSER;
+                        }
+                    }
+                    else if ((entry1.symbol != null) && (!entry1.symbol.equals(entry2.symbol))) {
+                        continue PARSER;
+                    }
+                    else if (entry1.symbol != entry2.symbol) {
+                        continue PARSER;
+                    }
+                }
+                
+                // parsers are in same state
+                return existing;
+            }
+        }
+        
+        return null;
+    }
+
+    /**
+     *
+     */
+    private static class Parser {
+        
+        /** */
+        private enum Result {
+            CONTINUE, FAIL, SUCCESS
+        }
+        
+        /** */
+        private ParsingTable table;
+        
+        /** */
+        private Stack<StackEntry> stack = new Stack<>();
+        
+        /** */
+        private int noOfParsedTokens = 0;
+        
+        /**
+         * 
+         */
+        private Parser(ParsingTable table) {
+            this.table = table;
+            stack.push(new StackEntry(null, table.getInitialState()));
+        }
+        
+        /**
+         *
+         */
+        private int getNoOfParsedTokens() {
+            return noOfParsedTokens;
+        }
+        
+        /**
+         *
+         */
+        private boolean checkSuccess() {
+            // check the potential success of a copy of the stack to allow for parsing further
+            // tokens that are repetitions also allowed by the parser
+            
+            Stack<StackEntry> stackCopy = new Stack<>();
+            
+            for (StackEntry entry : stack) {
+                stackCopy.push(entry);
+            }
+            
+            Action action;
+            
+            do {
+                action = table.getAction(stackCopy.peek().state, EOF);
+                
+                if ((action != null) && (action.type == Action.Type.SHIFT)) {
+                    return true;
+                }
+                else if ((action == null) || (action.type != Action.Type.REDUCE)) {
+                    return false;
+                }
+
+                ProductionRule rule = action.rule;
+                //System.out.println("    reducing " + rule);
+                    
+                for (int i = rule.rightHandSide.length - 1; i >= 0; i--) {
+                    /*StackEntry entry = */stackCopy.pop();
+                    /*if (!entry.symbol.equals(rule.rightHandSide[i])) {
+                        throw new RuntimeException("reduction seems to be wrong");
+                    }*/
+                }
+               
+                Action gotoAct = table.getAction(stackCopy.peek().state, rule.leftHandSide);
+                State nextState = stackCopy.peek().state;
+                if (gotoAct != null) {
+                    //System.out.println("    performing goto to " + gotoAct.dest.id);
+                    nextState = gotoAct.dest;
+                }
+                
+                stackCopy.push(new StackEntry(rule.leftHandSide, nextState));
+            }
+            while (action != null);
+
+            return true;
+        }
+
+        /**
+         * 
+         */
+        private Result handleToken(IEventTask token) {
+            noOfParsedTokens ++;
+            
+            Action action = table.getAction(stack.peek().state, token);
+            
+            if (action == null) {
+                if (checkSuccess()) {
+                    return Result.SUCCESS;
+                }
+                else {
+                    return Result.FAIL;
+                }
+            }
+            
+            /*System.out.println("  " + stack);
+            System.out.println("  " + token);
+            System.out.println
+                ("    action for " + stack.peek().state.id + " on " + token + " is " + action);*/
+            
+            while (action.type == Action.Type.REDUCE) {
+                ProductionRule rule = action.rule;
+                //System.out.println("    reducing " + rule);
+                
+                for (int i = rule.rightHandSide.length - 1; i >= 0; i--) {
+                    /*StackEntry entry = */stack.pop();
+                    /*if (!entry.symbol.equals(rule.rightHandSide[i])) {
+                        throw new RuntimeException("reduction seems to be wrong");
+                    }*/
+                }
+                
+                Action gotoAct = table.getAction(stack.peek().state, rule.leftHandSide);
+                State nextState = stack.peek().state;
+                if (gotoAct != null) {
+                    //System.out.println("    performing goto to " + gotoAct.dest.id);
+                    nextState = gotoAct.dest;
+                }
+                
+                stack.push(new StackEntry(rule.leftHandSide, nextState));
+                action = table.getAction(stack.peek().state, token);
+                
+                if (action == null) {
+                    if (checkSuccess()) {
+                        return Result.SUCCESS;
+                    }
+                    else {
+                        return Result.FAIL;
+                    }
+                }
+            }
+            
+            if (action.type == Action.Type.SHIFT) {
+                //System.out.println("    shift to " + action.dest.id);
+                stack.push(new StackEntry(token, action.dest));
+            }
+            else if (action.type == Action.Type.GOTO) {
+                //System.out.println("    unexpected goto " + action.dest.id);
+                stack.peek().state = action.dest;
+            }
+            
+            return Result.CONTINUE;
+        }
+        
+    }
+
+    /**
+     *
+     */
+    private static class ParsingTable {
+        
+        /** */
+        private ISequence sequence;
+        
+        /** */
+        private Map<State, List<ParsingTableEntry>> table = new IdentityHashMap<>();
+        
+        /**
+         *
+         */
+        private ParsingTable(ISequence sequence) {
+            this.sequence = sequence;
+        }
+
+        /**
+         *
+         */
+        private void addShift(State dest, State source, Object symbol) {
+            List<ParsingTableEntry> row = table.get(source);
+            
+            if (row == null) {
+                row = new LinkedList<ParsingTableEntry>();
+                table.put(source, row);
+            }
+            
+            //System.out.println("adding shift " + dest.id + " for " + source.id + "," + symbol);
+            row.add(new ParsingTableEntry(symbol, new Action(Action.Type.SHIFT, dest)));
+        }
+
+        /**
+         *
+         */
+        private void addGoto(State dest, State source, Object symbol) {
+            List<ParsingTableEntry> row = table.get(source);
+            
+            if (row == null) {
+                row = new LinkedList<ParsingTableEntry>();
+                table.put(source, row);
+            }
+            
+            //System.out.println("adding goto " + dest.id + " for " + source.id + "," + symbol);
+            row.add(new ParsingTableEntry(symbol, new Action(Action.Type.GOTO, dest)));
+        }
+
+        /**
+         *
+         */
+        private void addReduce(State source, Object symbol, ProductionRule rule) {
+            List<ParsingTableEntry> row = table.get(source);
+            
+            if (row == null) {
+                row = new LinkedList<ParsingTableEntry>();
+                table.put(source, row);
+            }
+            
+            for (ParsingTableEntry entry : row) {
+                if (entry.symbol.equals(symbol)) {
+                    System.out.println("conflict for " + source.id + " and terminal " + symbol +
+                                       ": " + entry.action + " " + new Action(rule));
+                    throw new IllegalArgumentException("creating shift/reduce conflict");
+                }
+            }
+            
+            row.add(new ParsingTableEntry(symbol, new Action(rule)));
+        }
+
+        /**
+         *
+         */
+        private State getInitialState() {
+            for (State candidate : table.keySet()) {
+                for (ProductionRulePosition position : candidate) {
+                    if ((position.position == 0) && (position.rule.leftHandSide.equals("S'"))) {
+                        return candidate;
+                    }
+                }
+                
+            }
+            
+            return null;
+        }
+
+        /**
+         *
+         */
+        private List<IEventTask> getInitialTokens() {
+            State initialState = getInitialState();
+            
+            List<IEventTask> result = new LinkedList<>();
+            
+            for (ParsingTableEntry entry : table.get(initialState)) {
+                if (entry.symbol instanceof IEventTask) {
+                    result.add((IEventTask) entry.symbol);
+                }
+            }
+            
+            return result;
+        }
+
+        /**
+         *
+         */
+        private Action getAction(State state, Object symbol) {
+            List<ParsingTableEntry> row = table.get(state);
+            
+            if (row != null) {
+                if (symbol instanceof IEventTask) {
+                    for (ParsingTableEntry entry : row) {
+                        if (entry.symbol == symbol) {
+                            return entry.action;
+                        }
+                    }
+                }
+                else {
+                    for (ParsingTableEntry entry : row) {
+                        if (entry.symbol.equals(symbol)) {
+                            return entry.action;
+                        }
+                    }
+                }
+            }
+
+            return null;
+        }
+
+        /* (non-Javadoc)
+         * @see java.lang.Object#toString()
+         */
+        @Override
+        public String toString() {
+            Set<Object> eventTasks = new HashSet<>();
+            eventTasks.add(EOF);
+            
+            Set<String> symbols = new HashSet<>();
+            Set<State> states = new HashSet<>();
+            
+            for (Map.Entry<State, List<ParsingTableEntry>> entry : table.entrySet()) {
+                for (ParsingTableEntry tableEntry : entry.getValue()) {
+                    if (tableEntry.symbol instanceof IEventTask) {
+                        eventTasks.add((IEventTask) tableEntry.symbol);
+                    }
+                    else if (tableEntry.symbol instanceof String) {
+                        symbols.add((String) tableEntry.symbol);
+                    }
+                }
+                
+                states.add(entry.getKey());
+            }
+            
+            StringBuffer result = new StringBuffer();
+            
+            result.append("\t|");
+            for (Object task : eventTasks) {
+                if (task instanceof IEventTask) {
+                    result.append(((IEventTask) task).getId());
+                }
+                else {
+                    result.append("EOF");
+                }
+                result.append("\t|");
+            }
+            
+            for (String symbol : symbols) {
+                result.append(symbol);
+                result.append("\t|");
+            }
+            
+            result.append('\n');
+            result.append("--------------------------------------------------------------------\n");
+            
+            for (State state : states) {
+                result.append(state.id);
+                
+                result.append("\t|");
+                
+                for (Object task : eventTasks) {
+                    Action act = null;
+                    
+                    if (table.containsKey(state)) {
+                        act = getAction(state, task);
+                    }
+                    
+                    if (act != null) {
+                        result.append(act);
+                    }
+                    
+                    result.append("\t|");
+                }
+                
+                for (String symbol : symbols) {
+                    Action act = null;
+                    
+                    if (table.containsKey(state)) {
+                        act = getAction(state, symbol);
+                    }
+                    
+                    if (act != null) {
+                        result.append(act);
+                    }
+                    
+                    result.append("\t|");
+                }
+               
+                result.append('\n');
+            }
+            
+            return result.toString();
+        }
+        
+        
+    }
+
+    /**
+     *
+     */
+    private static class ParsingTableEntry {
+        
+        /** */
+        private Object symbol;
+        
+        /** */
+        private Action action;
+        
+        /**
+         *
+         */
+        private ParsingTableEntry(Object symbol, Action action) {
+            this.symbol = symbol;
+            this.action = action;
+        }
+    }
+
+    /**
+     *
+     */
+    private static class Action {
+
+        private enum Type {
+            SHIFT, GOTO, REDUCE
+        }
+        
+        /** */
+        private Type type;
+        
+        /** */
+        private State dest;
+        
+        /** */
+        private ProductionRule rule;
+        
+        /**
+         *
+         */
+        private Action(Type type, State dest) {
+            this.type = type;
+            this.dest = dest;
+            this.rule = null;
+        }
+        
+        /**
+         *
+         */
+        private Action(ProductionRule rule) {
+            this.type = Type.REDUCE;
+            this.dest = null;
+            this.rule = rule;
+        }
+
+        /* (non-Javadoc)
+         * @see java.lang.Object#toString()
+         */
+        @Override
+        public String toString() {
+            switch (type) {
+                case SHIFT: return "s" + dest.id;
+                case GOTO: return "g" + dest.id;
+                case REDUCE: return "r" + rule.id;
+            }
+            
+            return null;
+        }
+       
+    }
+
+    /**
+     *
+     */
+    private static class ParsingTableIndex {
+        
+        /** */
+        private Map<IEventTarget, List<ParsingTable>> index = new HashMap<>();
+        
+        /**
+         * 
+         */
+        private ParsingTableIndex(List<ParsingTable> allTables,
+                                  Statistics         statistics)
+        {
+            for (ParsingTable table : allTables) {
+                statistics.addCheckedSequence(table.sequence);
+                
+                for (IEventTask firstToken : table.getInitialTokens()) {
+                    IEventTaskInstance exampleinstance =
+                        (IEventTaskInstance) firstToken.getInstances().iterator().next();
+                    
+                    List<ParsingTable> group = index.get(exampleinstance.getEvent().getTarget());
+                    
+                    if (group == null) {
+                        group = new LinkedList<>();
+                        index.put(exampleinstance.getEvent().getTarget(), group);
+                    }
+                    
+                    group.add(table);
+                }
+            }
+        }
+        
+        /**
+         * 
+         */
+        private List<ParsingTable> getRelevantTables(IEventTask token) {
+            IEventTaskInstance exampleinstance =
+                (IEventTaskInstance) token.getInstances().iterator().next();
+            
+            List<ParsingTable> result = index.get(exampleinstance.getEvent().getTarget());
+            
+            if (result != null) {
+                return result;
+            }
+            else {
+                return new LinkedList<>();
+            }
+        }
+        
+    }
+
+    /**
+     *
+     */
+    private static class ProductionRule {
+        
+        /** */
+        private String leftHandSide;
+        
+        /** */
+        private Object[] rightHandSide;
+
+        /** */
+        private static int IDCOUNTER = 0;
+        
+        /** */
+        private final int id = IDCOUNTER++;
+        
+        /**
+         * 
+         */
+        private ProductionRule(String leftHandSide, Object[] rightHandSide) {
+            this.leftHandSide = leftHandSide;
+            this.rightHandSide = rightHandSide;
+        }
+
+        /* (non-Javadoc)
+         * @see java.lang.Object#toString()
+         */
+        @Override
+        public String toString() {
+            StringBuffer result = new StringBuffer();
+            result.append(id);
+            result.append(": ");
+            result.append(leftHandSide);
+            result.append("   --->   ");
+            
+            for (Object symbol : rightHandSide) {
+                result.append(symbol);
+                result.append("     ");
+            }
+            
+            return result.toString();
+        }
+
+        /* (non-Javadoc)
+         * @see java.lang.Object#equals(java.lang.Object)
+         */
+        @Override
+        public boolean equals(Object obj) {
+            if (obj == this) {
+                return true;
+            }
+            
+            if (!(obj instanceof ProductionRule)) {
+                return false;
+            }
+            
+            ProductionRule other = (ProductionRule) obj;
+            
+            if (!other.leftHandSide.equals(this.leftHandSide)) {
+                return false;
+            }
+            
+            if (other.rightHandSide.length != this.rightHandSide.length) {
+                return false;
+            }
+            
+            for (int i = 0; i < other.rightHandSide.length; i++) {
+                if (!other.rightHandSide[i].equals(this.rightHandSide[i])) {
+                    return false;
+                }
+            }
+            
+            return true;
+        }
+        
+    }
+
+
+    /**
+     *
+     */
+    private static class ProductionRulePosition {
+        
+        /** */
+        private ProductionRule rule;
+        
+        /** */
+        private int position;
+        
+        /**
+         * 
+         */
+        private ProductionRulePosition(ProductionRule rule, int position) {
+            this.rule = rule;
+            this.position = position;
+        }
+
+        /**
+         *
+         */
+        private ProductionRulePosition advance() {
+            if (position < rule.rightHandSide.length) {
+                return new ProductionRulePosition(rule, position + 1);
+            }
+            else {
+                return null;
+            }
+        }
+
+        /**
+         *
+         */
+        private Object getNextSymbol() {
+            if (position < rule.rightHandSide.length) {
+                return rule.rightHandSide[position];
+            }
+            else {
+                return null;
+            }
+        }
+
+        /* (non-Javadoc)
+         * @see java.lang.Object#equals(java.lang.Object)
+         */
+        @Override
+        public boolean equals(Object obj) {
+            return (obj instanceof ProductionRulePosition) &&
+                (this.rule.equals(((ProductionRulePosition) obj).rule)) &&
+                (this.position == ((ProductionRulePosition) obj).position);
+        }
+
+        /* (non-Javadoc)
+         * @see java.lang.Object#toString()
+         */
+        @Override
+        public String toString() {
+            return rule + " (position " + position + ")";
+        }
+    }
+
+    /**
+     *
+     */
+    private static class State extends ArrayList<ProductionRulePosition> {
+
+        /**  */
+        private static final long serialVersionUID = 1L;
+
+        /** */
+        private static int IDCOUNTER = 0;
+        
+        /** */
+        private final int id = IDCOUNTER++;
+        
+        /**
+         *
+         */
+        private State() {
+            super();
+        }
+
+        /**
+         *
+         */
+        private State(Collection<? extends ProductionRulePosition> c) {
+            super(c);
+        }
+
+        /* (non-Javadoc)
+         * @see java.util.AbstractList#equals(java.lang.Object)
+         */
+        @Override
+        public boolean equals(Object other) {
+            if (!(other instanceof State)) {
+                return false;
+            }
+            
+            State otherState = (State) other;
+            
+            if (otherState.size() != this.size()) {
+                return false;
+            }
+            
+            for (ProductionRulePosition position : this) {
+                boolean positionFound = false;
+                
+                for (ProductionRulePosition candidatePosition : otherState) {
+                    if (position.equals(candidatePosition)) {
+                        positionFound = true;
+                        break;
+                    }
+                }
+                
+                if (!positionFound) {
+                    return false;
+                }
+            }
+            
+            return true;
+        }
+
+        /* (non-Javadoc)
+         * @see java.util.AbstractCollection#toString()
+         */
+        @Override
+        public String toString() {
+            StringBuffer result = new StringBuffer();
+            result.append("State ");
+            result.append(id);
+            result.append('\n');
+            
+            for (ProductionRulePosition pos : this) {
+                result.append("  ");
+                result.append(pos);
+                result.append('\n');
+            }
+            
+            return result.toString();
+        }
+        
+    }
+
+
+    /**
+     *
+     */
+    private static class Edge {
+        
+        /** */
+        private State source;
+        
+        /** */
+        private State dest;
+        
+        /** */
+        private Object symbol;
+
+        /**
+         *
+         */
+        private Edge(State source, State dest, Object symbol) {
+            this.source = source;
+            this.dest = dest;
+            this.symbol = symbol;
+        }
+
+        /* (non-Javadoc)
+         * @see java.lang.Object#equals(java.lang.Object)
+         */
+        @Override
+        public boolean equals(Object obj) {
+            if (((Edge) obj).symbol == null) {
+                System.out.println("blöd");
+            }
+            
+            if (this == obj) {
+                return true;
+            }
+            else {
+                return (obj instanceof Edge) && (((Edge) obj).source.equals(this.source)) &&
+                    (((Edge) obj).dest.equals(this.dest)) &&
+                    (((Edge) obj).symbol.equals(this.symbol));
+            }
+        }
+    }
+    
+
+    /**
+     *
+     */
+    private static class ExecutionVariants extends ArrayList<ExecutionVariant> {
+
+        /**  */
+        private static final long serialVersionUID = 1L;
+
+        /**
+         *
+         */
+        private void setCanBeOptional() {
+            for (ExecutionVariant variant : this) {
+                variant.canBeOptional = true;
+            }
+        }
+
+        /**
+         *
+         */
+        private boolean canBeOptional() {
+            for (ExecutionVariant variant : this) {
+                if (variant.canBeOptional) {
+                    return true;
+                }
+            }
+            
+            return false;
+        }
+
+        /**
+         *
+         */
+        private void add(ITask task) {
+            super.add(new ExecutionVariant(task));
+        }
+
+        /**
+         *
+         */
+        private void setCanBeIterated() {
+            for (ExecutionVariant variant : this) {
+                variant.canBeIterated = true;
+            }
+        }
+
+    }
+
+    /**
+     *
+     */
+    private static class ExecutionVariant {
+        
+        /** */
+        private ITask task;
+        
+        /** */
+        private boolean canBeOptional = false;
+        
+        /** */
+        private boolean canBeIterated = false;
+
+        /**
+         * 
+         */
+        private ExecutionVariant(ITask task) {
+            this.task = task;
+        }
+    }
+    
+    /**
+     *
+     */
+    private static class StackEntry {
+
+        /** */
+        private Object symbol;
+        
+        /** */
+        private State state;
+
+        /**
+         *
+         */
+        private StackEntry(Object symbol, State state) {
+            super();
+            this.symbol = symbol;
+            this.state = state;
+        }
+
+        /* (non-Javadoc)
+         * @see java.lang.Object#toString()
+         */
+        @Override
+        public String toString() {
+            return "(" + symbol + "," + state.id + ")";
+        }
+    }
+    
+    /**
+     *
+     */
+    private static class Statistics {
+        
+        /** */
+        private ITaskModel comparedModel;
+        
+        /** */
+        private ITaskModel comparedWithModel;
+        
+        /** */
+        private Map<Integer, boolean[]> sessionCoverage = new HashMap<>();
+        
+        /** */
+        private Map<ISequence, Map<Integer, boolean[]>> sequenceCoverage = new HashMap<>();
+
+        /** */
+        private int allEventsOfComparedModel;
+        
+        /** */
+        private int allSequencesOfComparedModel;
+        
+        /** */
+        private int eventsCoveredBySequencesOfComparedModel;
+        
+        /** */
+        private int allEventsOfComparedWithModel;
+        
+        /** */
+        private int allSequencesOfComparedWithModel;
+        
+        /** */
+        private int eventsCoveredBySequencesOfComparedWithModel;
+
+        /**
+         * 
+         */
+        private Statistics(ITaskModel comparedModel, ITaskModel comparedWithModel) {
+            super();
+            this.comparedModel = comparedModel;
+            this.comparedWithModel = comparedWithModel;
+
+            this.allEventsOfComparedModel = getAllEvents(comparedModel);
+            Set<ISequence> sequences = getSequences(comparedModel);
+            this.allSequencesOfComparedModel = sequences.size();
+            this.eventsCoveredBySequencesOfComparedModel = getEventsCoveredBySequences(sequences);
+            
+            this.allEventsOfComparedWithModel = getAllEvents(comparedWithModel);
+            sequences = getSequences(comparedWithModel);
+            this.allSequencesOfComparedWithModel = sequences.size();
+            this.eventsCoveredBySequencesOfComparedWithModel =
+                getEventsCoveredBySequences(sequences);
+        }
+
+        /**
+         *
+         */
+        private Set<ISequence> getSequences(ITaskModel model) {
+            Set<ISequence> result = new HashSet<>();
+            
+            for (ITask candidate : model.getTasks()) {
+                if (candidate instanceof ISequence) {
+                    result.add((ISequence) candidate);
+                }
+            }
+            
+            return result;
+        }
+
+        /**
+         *
+         */
+        private int getAllEvents(ITaskModel model) {
+            int allEvents = 0;
+            
+            for (ITask task : model.getTasks()) {
+                if (task instanceof IEventTask) {
+                    allEvents += task.getInstances().size();
+                }
+            }
+            
+            return allEvents;
+        }
+
+        /**
+         *
+         */
+        private int getCoveredActions(int sessionIndex) {
+            boolean[] coveredActions = sessionCoverage.get(sessionIndex);
+            
+            if (coveredActions == null) {
+                return 0;
+            }
+            else {
+                int counter = 0;
+                
+                for (int i = 0; i < coveredActions.length; i++) {
+                    if (coveredActions[i]) {
+                        counter++;
+                    }
+                }
+                
+                return counter;
+            }
+        }
+
+        /**
+         *
+         */
+        private int getRecalledActions() {
+            int overallCoverage = 0;
+            
+            for (boolean[] coveredActions : sessionCoverage.values()) {
+                for (int i = 0; i < coveredActions.length; i++) {
+                    if (coveredActions[i]) {
+                        overallCoverage++;
+                    }
+                }
+            }
+            
+            return overallCoverage;
+        }
+
+        /**
+         *
+         */
+        private int getRecalledActionsOf(Set<ISequence> sequences) {
+            Map<Integer, boolean[]> combinedCoveredSessions = new HashMap<>();
+            
+            for (ISequence sequence : sequences) {
+                Map<Integer, boolean[]> coveredSessions = sequenceCoverage.get(sequence);
+            
+                if (coveredSessions != null) {
+                    for (Map.Entry<Integer, boolean[]> entry : coveredSessions.entrySet()) {
+                        boolean[] combinedSession = combinedCoveredSessions.get(entry.getKey());
+                        
+                        if (combinedSession == null) {
+                            combinedSession = new boolean[entry.getValue().length];
+                            combinedCoveredSessions.put(entry.getKey(), combinedSession);
+                        }
+                        
+                        for (int i = 0; i < combinedSession.length; i++) {
+                            if (entry.getValue()[i]) {
+                                combinedSession[i] = true;
+                            }
+                        }
+                    }
+                }
+            }
+
+            int overallCoverage = 0;
+            
+            for (boolean[] coveredActions : combinedCoveredSessions.values()) {
+                for (int i = 0; i < coveredActions.length; i++) {
+                    if (coveredActions[i]) {
+                        overallCoverage++;
+                    }
+                }
+            }
+            
+            return overallCoverage;
+        }
+
+        /**
+         * 
+         */
+        private void addCheckedSequence(ISequence coveringSequence) {
+            Map<Integer, boolean[]> coveredSessions = sequenceCoverage.get(coveringSequence);
+            
+            if (coveredSessions == null) {
+                coveredSessions = new HashMap<>();
+                sequenceCoverage.put(coveringSequence, coveredSessions);
+            }
+        }
+
+        /**
+         * 
+         */
+        private void addCoveredActions(ISequence coveringSequence,
+                                       int       sessionIndex,
+                                       int       sessionSize,
+                                       int       indexFirstAction,
+                                       int       indexLastAction)
+        {
+            boolean[] coveredActions = sessionCoverage.get(sessionIndex);
+            
+            if (coveredActions == null) {
+                coveredActions = new boolean[sessionSize];
+                sessionCoverage.put(sessionIndex, coveredActions);
+            }
+            
+            for (int i = indexFirstAction; i <= indexLastAction; i++) {
+                coveredActions[i] = true;
+            }
+            
+            Map<Integer, boolean[]> coveredSessions = sequenceCoverage.get(coveringSequence);
+            
+            if (coveredSessions == null) {
+                coveredSessions = new HashMap<>();
+                sequenceCoverage.put(coveringSequence, coveredSessions);
+            }
+            
+            coveredActions = coveredSessions.get(sessionIndex);
+            
+            if (coveredActions == null) {
+                coveredActions = new boolean[sessionSize];
+                coveredSessions.put(sessionIndex, coveredActions);
+            }
+            
+            for (int i = indexFirstAction; i <= indexLastAction; i++) {
+                coveredActions[i] = true;
+            }
+        }
+        
+        /**
+         * 
+         */
+        private void dump(PrintStream out) {
+            Set<ISequence> mostProminentSequences =
+                TaskTreeUtils.getMostProminentSequences(comparedModel, sequenceCoverage.keySet());
+            
+            int eventsCoveredByAllSequences =
+                getEventsCoveredBySequences(sequenceCoverage.keySet());
+            
+            int eventsCoveredByMostProminent = getEventsCoveredBySequences(mostProminentSequences);
+            
+            int recalledActions = getRecalledActions();
+            int recalledActionsMostProminent = getRecalledActionsOf(mostProminentSequences);
+           
+            out.println("\nComparison Statistics");
+            out.println("=======================================================================");
+            out.println("compared model: " + comparedModel);
+            out.println("                            all actions in sessions: " +
+                        allEventsOfComparedModel);
+            out.println("                             all sequences of model: " +
+                        allSequencesOfComparedModel);
+            out.println("                         checked sequences of model: " +
+                        formatPerc(sequenceCoverage.keySet().size(), allSequencesOfComparedModel));
+            out.println("          most prominent checked sequences of model: " +
+                        mostProminentSequences.size());
+            out.println("                   actions covered by all sequences: " +
+                        formatPerc(eventsCoveredBySequencesOfComparedModel, allEventsOfComparedModel));
+            out.println("               actions covered by checked sequences: " +
+                        formatPerc(eventsCoveredByAllSequences, allEventsOfComparedModel));
+            out.println("actions covered by most prominent checked sequences: " +
+                        formatPerc(eventsCoveredByMostProminent, allEventsOfComparedModel));
+            out.println();
+            out.println("compared with: " + comparedWithModel);
+            out.println("                            all actions in sessions: " +
+                        allEventsOfComparedWithModel);
+            out.println("                             all sequences of model: " +
+                        allSequencesOfComparedWithModel);
+            out.println("                   actions covered by all sequences: " +
+                        formatPerc(eventsCoveredBySequencesOfComparedWithModel,
+                                   allEventsOfComparedWithModel));
+            out.println();
+            out.println("               actions covered by checked sequences: " +
+                        formatPerc(recalledActions, allEventsOfComparedWithModel));
+            
+            out.println("        actions covered by most prominent sequences: " +
+                        formatPerc(recalledActionsMostProminent, allEventsOfComparedWithModel));
+            out.println();
+            
+            out.print("CSV: ");
+            out.print(allEventsOfComparedModel);
+            out.print(";");
+            out.print(allSequencesOfComparedModel);
+            out.print(";");
+            out.print(sequenceCoverage.keySet().size());
+            out.print(";");
+            out.print((double) sequenceCoverage.keySet().size() / allSequencesOfComparedModel);
+            out.print(";");
+            out.print(mostProminentSequences.size());
+            out.print(";");
+            out.print(eventsCoveredBySequencesOfComparedModel);
+            out.print(";");
+            out.print((double) eventsCoveredBySequencesOfComparedModel / allEventsOfComparedModel);
+            out.print(";");
+            out.print(eventsCoveredByAllSequences);
+            out.print(";");
+            out.print((double) eventsCoveredByAllSequences / allEventsOfComparedModel);
+            out.print(";");
+            out.print(eventsCoveredByMostProminent);
+            out.print(";");
+            out.print((double) eventsCoveredByMostProminent / allEventsOfComparedModel);
+            out.print(";");
+            out.print(allEventsOfComparedWithModel);
+            out.print(";");
+            out.print(allSequencesOfComparedWithModel);
+            out.print(";");
+            out.print(eventsCoveredBySequencesOfComparedWithModel);
+            out.print(";");
+            out.print((double) eventsCoveredBySequencesOfComparedWithModel / allEventsOfComparedWithModel);
+            out.print(";");
+            out.print(recalledActions);
+            out.print(";");
+            out.print((double) recalledActions / allEventsOfComparedWithModel);
+            out.print(";");
+            out.print(recalledActionsMostProminent);
+            out.print(";");
+            out.print((double) recalledActionsMostProminent / allEventsOfComparedWithModel);
+            out.println();
+            out.println();
+            
+            
+            /*for (final ISequence sequence : sequenceCoverage.keySet()) {
+                Set<ISequence> tmp = new HashSet<>();
+                tmp.add(sequence);
+                if (comparedModel.getTaskInfo(sequence).getMeasureValue(TaskMetric.EVENT_COVERAGE) >= getRecalledActionsOf(tmp)) {
+                    continue;
+                }
+                
+                System.out.println();
+                System.out.println(comparedModel.getTaskInfo(sequence).getMeasureValue(TaskMetric.EVENT_COVERAGE) +
+                                   "  " + getRecalledActionsOf(tmp));
+                new TaskTreeEncoder().encode(sequence, System.out);
+
+                final List<IEventTaskInstance> eventList = new LinkedList<>();
+                int sessionIndex = 0;
+                for (IUserSession session : comparedModel.getUserSessions()) {
+                    sessionIndex++;
+
+                    System.out.println("\ncoverage in session " + sessionIndex + " (" +
+                                       session.size() + ")");
+                    
+                    //new TaskTreeEncoder().encode(session, System.out);
+                    
+                    final int[] index = new int[1];
+                    index[0] = 0;
+                    
+                    for (ITaskInstance instance : session) {
+                        instance.accept(new DefaultTaskInstanceTraversingVisitor() {
+                            @Override
+                            public void visit(IEventTaskInstance eventTaskInstance) {
+                                eventList.add(eventTaskInstance);
+                            }
+
+                            @Override
+                            public void visit(ISequenceInstance sequenceInstance) {
+                                if (sequenceInstance.getTask() == sequence) {
+                                    eventList.clear();
+                                }
+                                super.visit(sequenceInstance);
+                                if (sequenceInstance.getTask() == sequence) {
+                                    System.out.println("source: " + index[0] + "  " + eventList);
+                                }
+                            }
+                        });
+                        index[0]++;
+                    }
+
+                    System.out.println("dest session " + comparedWithModel.getUserSessions().get(sessionIndex - 1).size());
+                    eventList.clear();
+                    final boolean[] covered = sequenceCoverage.get(sequence).get(sessionIndex - 1);
+
+                    if (covered == null) {
+                        System.out.println("no dest");
+                        continue;
+                    }
+
+                    final int[] counter = new int[1];
+                    index[0] = 0;
+
+                    for (ITaskInstance instance : comparedWithModel.getUserSessions().get(sessionIndex - 1)) {
+                        instance.accept(new DefaultTaskInstanceTraversingVisitor() {
+                            @Override
+                            public void visit(IEventTaskInstance eventTaskInstance) {
+                                if (covered[counter[0]]) {
+                                    eventList.add(eventTaskInstance);
+                                }
+                                else {
+                                    if (eventList.size() > 0) {
+                                        System.out.println("dest:   " + index[0] + "  " + eventList);
+                                    }
+                                    eventList.clear();
+                                }
+
+                                counter[0]++;
+                            }
+                        });
+                        
+                        index[0]++;
+                    }
+
+                    if (eventList.size() > 0) {
+                        System.out.println("dest:   " + eventList);
+                    }
+                }
+            }*/
+        }
+
+        /**
+         *
+         */
+        private int getEventsCoveredBySequences(Set<ISequence> sequences) {
+            final Set<IEventTaskInstance> events = new HashSet<>();
+            
+            for (ISequence task : sequences) {
+                for (ITaskInstance instance : task.getInstances()) {
+                    instance.accept(new DefaultTaskInstanceTraversingVisitor() {
+                        @Override
+                        public void visit(IEventTaskInstance eventTaskInstance) {
+                            events.add(eventTaskInstance);
+                        }
+                    });
+                }
+            }
+            
+            return events.size();
+        }
+
+        /**
+         *
+         */
+        private String formatPerc(int part, int of) {
+            return (100 * part / of) + "% (" + part + "/" + of + ")";
+        }
+    }
+}
