Index: trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/temporalrelation/SequenceForTaskDetectionRule.java
===================================================================
--- trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/temporalrelation/DefaultTaskSequenceDetectionRule.java	(revision 1119)
+++ trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/temporalrelation/SequenceForTaskDetectionRule.java	(revision 1127)
@@ -21,11 +21,13 @@
 import de.ugoe.cs.autoquest.tasktrees.nodeequality.NodeEquality;
 import de.ugoe.cs.autoquest.tasktrees.nodeequality.NodeEqualityRuleManager;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.IIteration;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ISelection;
 import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequence;
 import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeBuilder;
 import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNode;
 import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNodeFactory;
-import de.ugoe.cs.autoquest.usageprofiles.SymbolComparator;
 import de.ugoe.cs.autoquest.usageprofiles.Trie;
 import de.ugoe.cs.autoquest.usageprofiles.TrieProcessor;
+import de.ugoe.cs.util.console.Console;
 
 /**
@@ -36,5 +38,5 @@
  * @author Patrick Harms
  */
-class DefaultTaskSequenceDetectionRule implements TemporalRelationshipRule {
+class SequenceForTaskDetectionRule implements TemporalRelationshipRule {
     
     /**
@@ -58,5 +60,5 @@
      * </p>
      */
-    private SymbolComparator<ITaskTreeNode> nodeComparator;
+    private TaskTreeNodeComparator nodeComparator;
 
     /**
@@ -66,8 +68,8 @@
      * </p>
      */
-    DefaultTaskSequenceDetectionRule(NodeEqualityRuleManager nodeEqualityRuleManager,
-                                     NodeEquality            minimalNodeEquality,
-                                     ITaskTreeNodeFactory    taskTreeNodeFactory,
-                                     ITaskTreeBuilder        taskTreeBuilder)
+    SequenceForTaskDetectionRule(NodeEqualityRuleManager nodeEqualityRuleManager,
+                                 NodeEquality            minimalNodeEquality,
+                                 ITaskTreeNodeFactory    taskTreeNodeFactory,
+                                 ITaskTreeBuilder        taskTreeBuilder)
     {
         this.taskTreeNodeFactory = taskTreeNodeFactory;
@@ -83,5 +85,5 @@
     @Override
     public String toString() {
-        return "DefaultTaskSequenceDetectionRule";
+        return "SequenceForTaskDetectionRule";
     }
 
@@ -101,67 +103,211 @@
             // the rule is always feasible as tasks may occur at any time
             RuleApplicationResult result = new RuleApplicationResult();
-            result.setRuleApplicationStatus(RuleApplicationStatus.RULE_APPLICATION_FEASIBLE);
+            result.setRuleApplicationStatus(RuleApplicationStatus.FEASIBLE);
             return result;
         }
 
-        RuleApplicationData<ITaskTreeNode> appData = new RuleApplicationData<ITaskTreeNode>(parent);
-        
-        appData.getStopWatch().start("whole rule application");
-        
+        List<ITaskTreeNode> children = parent.getChildren();
+        List<ISequence> sessions = new LinkedList<ISequence>();
+        
+        for (ITaskTreeNode child : children) {
+            if (child instanceof ISequence) {
+                sessions.add((ISequence) child);
+            }
+            else {
+                Console.println("provided parent is no parent of sessions");
+                return null;
+            }
+        }
+        
+        RuleApplicationData appData = new RuleApplicationData(sessions);
+
+        boolean finished = false;
+        
+        // this is the real rule application. Loop while something is replaced.
         do {
-            getSequencesOccuringMostOften(appData);
-            
-            if ((appData.getLastFoundSequences().size() > 0) &&
-                (appData.getLastFoundSequences().getOccurrenceCount() > 1))
-            {
-                System.out.println("found " + appData.getLastFoundSequences().size() +
-                                   " tasks occurring " +
-                                   appData.getLastFoundSequences().getOccurrenceCount() + " times");
-
-                for (List<ITaskTreeNode> task : appData.getLastFoundSequences()) {
-                    // only tasks occurring more often than once are of interest
-                    appData.stopWatch.start("creating task sequences");
-
-                    String taskId = "task " + RuleUtils.getNewId();
-                    System.out.println(taskId + ": " + task);
-                    
-                    appData.resetReplacementCounter();
-                    createSequenceForTaskOccurrences(taskId, task, parent, appData);
-                    
-                    if (appData.getReplacementCounter() <
-                        appData.getLastFoundSequences().getOccurrenceCount())
-                    {
-                        System.out.println(taskId + ": replaced task only " +
-                                           appData.getReplacementCounter() +
-                                           " times instead of expected " +
-                                           appData.getLastFoundSequences().getOccurrenceCount());
-                        
-                    }
-
-                    appData.stopWatch.stop("creating task sequences");
+            System.out.println();
+            
+            appData.getStopWatch().start("whole loop");
+            detectAndReplaceIterations(appData);
+            //mergeEqualTasks(appData);
+            appData.getStopWatch().start("task replacement");
+            detectAndReplaceTasks(appData);
+            appData.getStopWatch().stop("task replacement");
+            appData.getStopWatch().stop("whole loop");
+            
+            //((TaskTreeNodeComparator) nodeComparator).getStopWatch().dumpStatistics(System.out);
+            //((TaskTreeNodeComparator) nodeComparator).getStopWatch().reset();
+            
+            appData.getStopWatch().dumpStatistics(System.out);
+            appData.getStopWatch().reset();
+            
+            finished = (appData.getReplacementCounter() == 0);
+        }
+        while (!finished);
+        
+        System.out.println("created " + appData.getResult().getNewlyCreatedParentNodes().size() +
+                           " new parent nodes\n");
+        
+        if (appData.getResult().getNewlyCreatedParentNodes().size() > 0) {
+            appData.getResult().setRuleApplicationStatus(RuleApplicationStatus.FINISHED);
+        }
+        
+        return appData.getResult();
+    }
+
+    /**
+     * <p>
+     * TODO: comment
+     * </p>
+     *
+     * @param appData
+     */
+    private void detectAndReplaceIterations(RuleApplicationData appData) {
+        System.out.print("detecting iterations");
+        appData.getStopWatch().start("detecting iterations");
+        
+        List<ISequence> sessions = appData.getSessions();
+        int foundIterations = 0;
+        
+        for (ISequence session : sessions) {
+            foundIterations += detectAndReplaceIterations(session, appData);
+        }
+        
+        appData.getStopWatch().stop("detecting iterations");
+        System.out.println(" --> found " + foundIterations);
+    }
+
+    /**
+     * <p>
+     * TODO: comment
+     * </p>
+     *
+     * @param appData
+     */
+    private int detectAndReplaceIterations(ISequence           session,
+                                           RuleApplicationData appData)
+    {
+        int count = 0;
+        
+        TemporalRelationshipRule rule = new SimpleIterationDetectionRule
+            (nodeComparator, taskTreeNodeFactory, taskTreeBuilder);
+
+        RuleApplicationResult result = rule.apply(session, true);
+            
+        if ((result != null) && (result.getNewlyCreatedParentNodes() != null)) {
+            for (ITaskTreeNode newParent : result.getNewlyCreatedParentNodes()) {
+                appData.getResult().addNewlyCreatedParentNode(newParent);
+                if (newParent instanceof IIteration) {
+                    count++;
                 }
             }
-            
-        }
-        while (appData.getLastFoundSequences().getOccurrenceCount() > 2);
-        
-        appData.getStopWatch().stop("whole rule application");
-        appData.getStopWatch().dumpStatistics(System.out);
-        
-        if (appData.getResult().getNewlyCreatedParentNodes().size() > 0) {
-            appData.getResult().setRuleApplicationStatus
-                (RuleApplicationStatus.RULE_APPLICATION_FINISHED);
-        }
-        
-        System.out.println(appData.getResult().getNewlyCreatedParentNodes().size());
-        
-        for (ITaskTreeNode node : appData.getResult().getNewlyCreatedParentNodes()) {
-            for (ITaskTreeNode child : node.getChildren()) {
-                System.out.println(child);
-            }
-            System.out.println();
-        }
-        
-        return appData.getResult();
+        }
+        
+        return count;
+    }
+
+//    /**
+//     * <p>
+//     * TODO: comment
+//     * </p>
+//     *
+//     * @param appData
+//     */
+//    private void mergeEqualTasks(RuleApplicationData appData) {
+//        System.out.println("merging equal tasks");
+//        appData.getStopWatch().start("merging equal tasks");
+//        
+//        int replacements = 0;
+//        List<ISequence> sessions = appData.getSessions();
+//        
+//        IdentityHashMap<ITaskTreeNode, ITaskTreeNode> replacedChildren =
+//            new IdentityHashMap<ITaskTreeNode, ITaskTreeNode>();
+//        
+//        for (int sessionIdx1 = 0; sessionIdx1 < sessions.size(); sessionIdx1++) {
+//            List<ITaskTreeNode> children1 = appData.getSessions().get(sessionIdx1).getChildren();
+//            for (int childIdx1 = 0; childIdx1 < children1.size(); childIdx1++) {
+//                // this is the child of which we search equal other children to merge and to
+//                // replace with one single unique node
+//                ITaskTreeNode child1 = children1.get(childIdx1);
+//                
+//                if (replacedChildren.containsKey(child1)) {
+//                    continue;
+//                }
+//                
+//                // now search for all other children that are equal. Also record the session they
+//                // belong to as well as the index in that session
+//                List<ITaskTreeNode> equalChildren = new LinkedList<ITaskTreeNode>();
+//                List<Integer> sessionIndexes = new LinkedList<Integer>();
+//                List<Integer> childIndexes = new LinkedList<Integer>();
+//
+//                // add all information about the current child
+//                equalChildren.add(child1);
+//                sessionIndexes.add(sessionIdx1);
+//                childIndexes.add(childIdx1);
+//                
+//                for (int sessionIdx2 = sessionIdx1; sessionIdx2 < sessions.size(); sessionIdx2++) {
+//                    List<ITaskTreeNode> children2 =
+//                          appData.getSessions().get(sessionIdx2).getChildren();
+//                    
+//                    int startIndex = (sessionIdx1 == sessionIdx2) ? childIdx1 + 1 : 0;
+//                    
+//                    for (int childIdx2 = startIndex; childIdx2 < children2.size(); childIdx2++) {
+//                        ITaskTreeNode child2 = children2.get(childIdx2);
+//                        
+//                        if ((child1 != child2) && (nodeComparator.equals(child1, child2))) {
+//                            // this is an equal child --> record its occurrence
+//                            equalChildren.add(child2);
+//                            sessionIndexes.add(sessionIdx2);
+//                            childIndexes.add(childIdx2);
+//                        }
+//                    }
+//                }
+//                
+//                // now merge the found children
+//                if (equalChildren.size() > 1) {
+//                    ITaskTreeNode replacement =
+//                        mergeVariantsOfTasks(child1.toString(), equalChildren);
+//
+//                    for (int i = 0; i < sessionIndexes.size(); i++) {
+//                        taskTreeBuilder.setChild(appData.getSessions().get(sessionIndexes.get(i)),
+//                                                 childIndexes.get(i), replacement);
+//                    
+//                        replacements++;
+//                    }
+//                    
+//                    // remember the replacement to prevent comparison of merged nodes
+//                    replacedChildren.put(replacement, replacement);
+//                    
+//                    System.out.println
+//                        ("replaced " + sessionIndexes.size() + " occurrences of " + child1);
+//                }
+//            }
+//        }
+//
+//        appData.getStopWatch().stop("merging equal tasks");
+//        
+//        System.out.println("replaced " + replacements + " equal tasks with unique replacements");
+//    }
+//
+    /**
+     * <p>
+     * TODO: comment
+     * </p>
+     *
+     * @param appData
+     */
+    private void detectAndReplaceTasks(RuleApplicationData appData) {
+        System.out.println("detecting and replacing tasks");
+        appData.getStopWatch().start("detecting tasks");
+        
+        getSequencesOccuringMostOften(appData);
+
+        appData.getStopWatch().stop("detecting tasks");
+        appData.getStopWatch().start("replacing tasks");
+        
+        replaceSequencesOccurringMostOften(appData);
+        
+        appData.getStopWatch().stop("replacing tasks");
+        System.out.println("detected and replaced " + appData.getLastFoundTasks().size() + " tasks");
     }
 
@@ -174,10 +320,8 @@
      * @return
      */
-    private void getSequencesOccuringMostOften(RuleApplicationData<ITaskTreeNode> appData) {
-        System.out.println("determining most prominent tasks with a maximum of " +
-                           (appData.getLastFoundSequences().getOccurrenceCount() - 1) +
-                           " occurrences");
-
-        Sequences<ITaskTreeNode> sequences;
+    private void getSequencesOccuringMostOften(RuleApplicationData appData) {
+        System.out.println("determining most prominent tasks");
+
+        Tasks tasks;
         boolean createNewTrie = (appData.getLastTrie() == null) ||
             appData.getReplacementCounter() > 0; // tree has changed
@@ -188,20 +332,12 @@
             }
             
-            /*PathDumper dumper = new PathDumper();
-            trie.process(dumper);
-        
-            dumper.dump();*/
-        
-            appData.getStopWatch().start("determining tasks");
-
-            MaxCountAndLongestSequenceFinder finder = new MaxCountAndLongestSequenceFinder
-                  (appData.getLastFoundSequences().getOccurrenceCount() - 1);
+            MaxCountAndLongestTasksFinder finder = new MaxCountAndLongestTasksFinder();
             appData.getLastTrie().process(finder);
         
-            sequences = finder.getFoundSequences();
+            tasks = finder.getFoundTasks();
             
             createNewTrie = false;
             
-            for (List<ITaskTreeNode> task : sequences) {
+            for (List<ITaskTreeNode> task : tasks) {
                 if (task.size() >= appData.getTrainedSequenceLength()) {
                     // Trie must be recreated with a longer sequence length to be sure that
@@ -212,10 +348,11 @@
                 }
             }
-            
-            appData.getStopWatch().stop("determining tasks");
         }
         while (createNewTrie);
         
-        appData.setLastFoundSequences(sequences);
+        appData.setLastFoundTasks(tasks);
+
+        System.out.println("found " + appData.getLastFoundTasks().size() + " tasks occurring " +
+                           appData.getLastFoundTasks().getOccurrenceCount() + " times");
     }
 
@@ -228,5 +365,5 @@
      * @return
      */
-    private void createNewTrie(RuleApplicationData<ITaskTreeNode> appData) {
+    private void createNewTrie(RuleApplicationData appData) {
         System.out.println("training trie with a maximum sequence length of " +
                            appData.getTrainedSequenceLength());
@@ -235,5 +372,10 @@
         appData.setLastTrie(new Trie<ITaskTreeNode>(nodeComparator));
     
-        trainTrie(appData.getTree(), appData);
+        List<ISequence> sessions = appData.getSessions();
+        
+        for (ISequence session : sessions) {
+            trainTrie(session, appData);
+        }
+        
         appData.getStopWatch().stop("training trie");
     }
@@ -247,22 +389,135 @@
      * @param parent
      */
-    private void trainTrie(ITaskTreeNode parent, RuleApplicationData<ITaskTreeNode> appData) {
-        // prevent training of already replaces sequences as those shall not be replaced anymore
-        if (!appData.getResult().getNewlyCreatedParentNodes().contains(parent)) {
-            List<ITaskTreeNode> children = parent.getChildren();
-        
-            if ((children != null) && (children.size() > 0)) {
+    private void trainTrie(ISequence session, RuleApplicationData appData) {
+        List<ITaskTreeNode> children = session.getChildren();
+        
+        if ((children != null) && (children.size() > 0)) {
+            appData.getLastTrie().train(children, appData.getTrainedSequenceLength());
+        }
+    }
+
+    /**
+     * <p>
+     * TODO: comment
+     * </p>
+     *
+     * @param appData
+     */
+    private void replaceSequencesOccurringMostOften(RuleApplicationData appData) {
+        appData.resetReplacementCounter();
+
+        if ((appData.getLastFoundTasks().size() > 0) &&
+            (appData.getLastFoundTasks().getOccurrenceCount() > 1))
+        {
+            System.out.println("replacing tasks occurrences with merged variants of all versions");
+
+            for (List<ITaskTreeNode> task : appData.getLastFoundTasks()) {
+                String taskId = "task " + RuleUtils.getNewId();
+                System.out.println("replacing " + taskId + ": " + task);
+
+                appData.clearTaskOccurrences();
+                determineVariantsOfTaskOccurrences(task, appData.getSessions(), appData);
                 
-                /*System.out.println();
-                for (int i = 0; i < children.size(); i++) {
-                    System.out.println(children.get(i));
-                }*/
+                appData.getStopWatch().start("merging task nodes");
+                ITaskTreeNode taskReplacement = mergeVariantsOfTaskOccurrence(taskId, appData);
+                appData.getStopWatch().stop("merging task nodes");
+
+                appData.resetReplacementCounter();
+                replaceTaskOccurrences(task, taskReplacement, appData.getSessions(), appData);
+
+                if (appData.getReplacementCounter() > 0) {
+                    appData.getResult().addNewlyCreatedParentNode(taskReplacement);
+                }
+
+                if (appData.getReplacementCounter() <
+                    appData.getLastFoundTasks().getOccurrenceCount())
+                {
+                    System.out.println(taskId + ": replaced task only " +
+                                       appData.getReplacementCounter() +
+                                       " times instead of expected " +
+                                       appData.getLastFoundTasks().getOccurrenceCount());
+                }
+            }
+        }
+        
+    }
+
+    /**
+     * <p>
+     * TODO: comment
+     * </p>
+     *
+     * @param tree
+     */
+    private void determineVariantsOfTaskOccurrences(List<ITaskTreeNode> task,
+                                                    List<ISequence>     sessions,
+                                                    RuleApplicationData appData)
+    {
+        for (ISequence session : sessions) {
+            int index = -1;
                 
-                appData.getLastTrie().train(children, appData.getTrainedSequenceLength());
-            
-                for (ITaskTreeNode child : children) {
-                    trainTrie(child, appData);
+            List<ITaskTreeNode> children = session.getChildren();
+
+            do {
+                index = getSubListIndex(children, task, ++index);
+
+                if (index > -1) {
+                    ISequence taskOccurrence = RuleUtils.getSubSequenceInRange
+                            (session, index, index + task.size() - 1, null,
+                             taskTreeNodeFactory, taskTreeBuilder);
+
+                    appData.addTaskOccurrence(taskOccurrence);
+
+                    // let the index point to the last element the belongs the identified occurrence
+                    index += task.size() - 1;
                 }
             }
+            while (index > -1);
+        }
+    }
+
+    /**
+     * <p>
+     * TODO: comment
+     * </p>
+     *
+     * @param appData
+     * @return
+     */
+    private ITaskTreeNode mergeVariantsOfTaskOccurrence(String              taskId,
+                                                        RuleApplicationData appData)
+    {
+        return mergeVariantsOfTasks(taskId, appData.getTaskOccurrences());
+    }
+
+    /**
+     * <p>
+     * TODO: comment
+     * </p>
+     *
+     * @param appData
+     * @return
+     */
+    private ITaskTreeNode mergeVariantsOfTasks(String description, List<ITaskTreeNode> variants) {
+        // merge but preserve lexically distinct variants
+        TaskTreeNodeMerger merger = new TaskTreeNodeMerger
+            (taskTreeNodeFactory, taskTreeBuilder, nodeComparator);
+        
+        merger.mergeTaskNodes(variants);
+        
+        if (variants.size() == 1) {
+            ITaskTreeNode replacement = variants.get(0);
+            taskTreeBuilder.setDescription(replacement, description);
+            return replacement;
+        }
+        else {
+            ISelection selection = taskTreeNodeFactory.createNewSelection();
+            taskTreeBuilder.setDescription(selection, "variants of task " + description);
+            
+            for (ITaskTreeNode variant : variants) {
+                taskTreeBuilder.addChild(selection, variant);
+            }
+            
+            return selection;
         }
     }
@@ -279,55 +534,44 @@
      * @param result
      */
-    private void createSequenceForTaskOccurrences(String                             taskId,
-                                                  List<ITaskTreeNode>                task,
-                                                  ITaskTreeNode                      parent,
-                                                  RuleApplicationData<ITaskTreeNode> appData)
+    private void replaceTaskOccurrences(List<ITaskTreeNode> task,
+                                        ITaskTreeNode       replacement,
+                                        List<ISequence>     sessions,
+                                        RuleApplicationData appData)
     {
-        List<ITaskTreeNode> children = parent.getChildren();
-        
-        if ((children == null) || (children.size() == 0)) {
-            return;
-        }
-        
-        // first traverse the children
-        for (ITaskTreeNode child : children) {
-            createSequenceForTaskOccurrences(taskId, task, child, appData);
-        }
-        
         // now check the children themselves for an occurrence of the task
-        int index = -1;
-        
-        do {
-            index = getSubListIndex(children, task, ++index);
-            
-            if (index > -1) {
-                if (task.size() < children.size()) {
-                    ISequence sequence = RuleUtils.createNewSubSequenceInRange
-                        (parent, index, index + task.size() - 1, taskId,
-                         taskTreeNodeFactory, taskTreeBuilder);
-                    
-                    appData.getResult().addNewlyCreatedParentNode(sequence);
-                    appData.incrementReplacementCounter();
-                 
-                    children = parent.getChildren();
-                }
-                else {
-                    // the whole list of children is an occurrence of this task. Just set the
-                    // description of the parent and break up
-                    String description = parent.getDescription();
-                    
-                    if ((description != null) && (description.length() > 0)) {
-                        description += "; " + taskId;
+        for (int i = 0; i < sessions.size(); i++) {
+            ISequence session = sessions.get(i);
+            
+            int index = -1;
+        
+            List<ITaskTreeNode> children = session.getChildren();
+
+            do {
+                index = getSubListIndex(children, task, ++index);
+
+                if (index > -1) {
+                    if ((!(replacement instanceof ISequence)) ||
+                        (task.size() < children.size()))
+                    {
+                        for (int j = index; j < index + task.size(); j++) {
+                            taskTreeBuilder.removeChild(session, index);
+                        }
+
+                        taskTreeBuilder.addChild(session, index, replacement);
+                        appData.incrementReplacementCounter();
+
+                        children = session.getChildren();
                     }
                     else {
-                        description = taskId;
+                        // the whole list of children is an occurrence of this task. So ask the
+                        // caller of the method to replace the whole node
+                        sessions.set(i, (ISequence) replacement);
+                        appData.incrementReplacementCounter();
+                        break;
                     }
-                    
-                    taskTreeBuilder.setDescription(parent, description);
-                    break;
                 }
             }
-        }
-        while (index > -1);
+            while (index > -1);
+        }
     }
 
@@ -356,7 +600,4 @@
                     break;
                 }
-                else if (!nodeComparator.equals(subList.get(j), list.get(i + j))){
-                    throw new RuntimeException("comparator not commutative");
-                }
             }
             
@@ -377,10 +618,5 @@
      * @author Patrick Harms
      */
-    private class MaxCountAndLongestSequenceFinder implements TrieProcessor<ITaskTreeNode> {
-        
-        /**
-         * 
-         */
-        private int maxCount;
+    private class MaxCountAndLongestTasksFinder implements TrieProcessor<ITaskTreeNode> {
         
         /**
@@ -392,5 +628,5 @@
          * 
          */
-        private List<List<ITaskTreeNode>> foundSequences = new LinkedList<List<ITaskTreeNode>>();
+        private List<List<ITaskTreeNode>> foundTasks = new LinkedList<List<ITaskTreeNode>>();
 
         /**
@@ -401,7 +637,6 @@
          * @param maxCount
          */
-        public MaxCountAndLongestSequenceFinder(int maxCount) {
+        public MaxCountAndLongestTasksFinder() {
             super();
-            this.maxCount = maxCount;
             this.currentCount = 0;
         }
@@ -411,6 +646,6 @@
          */
         @Override
-        public TrieProcessor.Result process(List<ITaskTreeNode> sequence, int count) {
-            if (sequence.size() < 2) {
+        public TrieProcessor.Result process(List<ITaskTreeNode> task, int count) {
+            if (task.size() < 2) {
                 // ignore single nodes
                 return TrieProcessor.Result.CONTINUE;
@@ -422,30 +657,25 @@
             }
 
-            if (count > this.maxCount) {
-                // ignore sequences that occur too often
-                return TrieProcessor.Result.CONTINUE;
-            }
-            
             if (this.currentCount > count) {
                 // ignore this children of this node, as they may have only smaller counts than
-                // the already found sequences
+                // the already found tasks
                 return TrieProcessor.Result.SKIP_NODE;
             }
             
             if (this.currentCount < count) {
-                // the provided sequence occurs more often that all sequences found so far.
-                // clear all found sequences and use the new count as the one searched for
-                foundSequences.clear();
+                // the provided task occurs more often that all tasks found so far.
+                // clear all found tasks and use the new count as the one searched for
+                foundTasks.clear();
                 this.currentCount = count;
             }
             
             if (this.currentCount == count) {
-                // the sequence is of interest. Sort it into the other found sequences so that
+                // the task is of interest. Sort it into the other found tasks so that
                 // the longest come first
                 boolean added = false;
-                for (int i = 0; i < foundSequences.size(); i++) {
-                    if (foundSequences.get(i).size() < sequence.size()) {
+                for (int i = 0; i < foundTasks.size(); i++) {
+                    if (foundTasks.get(i).size() < task.size()) {
                         // defensive copy
-                        foundSequences.add(i, new LinkedList<ITaskTreeNode>(sequence)); // defensive copy
+                        foundTasks.add(i, new LinkedList<ITaskTreeNode>(task)); // defensive copy
                         added = true;
                         break;
@@ -454,5 +684,5 @@
                 
                 if (!added) {
-                    foundSequences.add(new LinkedList<ITaskTreeNode>(sequence)); // defensive copy
+                    foundTasks.add(new LinkedList<ITaskTreeNode>(task)); // defensive copy
                 }
             }
@@ -468,7 +698,7 @@
          * @return
          */
-        public Sequences<ITaskTreeNode> getFoundSequences() {
+        public Tasks getFoundTasks() {
             removePermutationsOfShorterTasks();
-            return new Sequences<ITaskTreeNode>(currentCount, foundSequences);
+            return new Tasks(currentCount, foundTasks);
         }
 
@@ -482,14 +712,14 @@
             // now iterate the sorted list and for each task remove all other tasks that are shorter
             // (i.e. come later in the sorted list) and that represent a subpart of the task
-            for (int i = 0; i < foundSequences.size(); i++) {
-                for (int j = i + 1; j < foundSequences.size();) {
-                    if (foundSequences.get(j).size() < foundSequences.get(i).size()) {
+            for (int i = 0; i < foundTasks.size(); i++) {
+                for (int j = i + 1; j < foundTasks.size();) {
+                    if (foundTasks.get(j).size() < foundTasks.get(i).size()) {
                         // found a task that is a potential subtask. Check for this and remove the
                         // subtask if needed
-                        List<ITaskTreeNode> longTask = foundSequences.get(i);
-                        List<ITaskTreeNode> shortTask = foundSequences.get(j);
+                        List<ITaskTreeNode> longTask = foundTasks.get(i);
+                        List<ITaskTreeNode> shortTask = foundTasks.get(j);
                         
                         if (getSubListIndex(longTask, shortTask, 0) > -1) {
-                            foundSequences.remove(j);
+                            foundTasks.remove(j);
                         }
                         else {
@@ -509,10 +739,35 @@
      * 
      */
-    private class RuleApplicationData<T> {
-        
-        /**
-         * 
-         */
-        private T tree;
+    private class RuleApplicationData {
+        
+        /**
+         * 
+         */
+        private List<ISequence> sessions;
+        
+        /**
+         * 
+         */
+        private Trie<ITaskTreeNode> lastTrie;
+        
+        /**
+         * default and minimum trained sequence length is 3
+         */
+        private int trainedSequenceLength = 3;
+        
+        /**
+         * 
+         */
+        private Tasks lastFoundTasks = new Tasks(Integer.MAX_VALUE, null);
+        
+        /**
+         * 
+         */
+        private List<ITaskTreeNode> taskOccurrences = new LinkedList<ITaskTreeNode>();
+        
+        /**
+         * 
+         */
+        private int replacementCounter;
         
         /**
@@ -524,24 +779,4 @@
          * 
          */
-        private Sequences<T> lastFoundSequences = new Sequences<T>(Integer.MAX_VALUE, null);
-        
-        /**
-         * 
-         */
-        private Trie<T> lastTrie;
-        
-        /**
-         * default and minimum trained sequence length is 3
-         */
-        private int trainedSequenceLength = 3;
-        
-        /**
-         * 
-         */
-        private int replacementCounter;
-        
-        /**
-         * 
-         */
         private StopWatch stopWatch = new StopWatch();
         
@@ -549,20 +784,20 @@
          * 
          */
-        private RuleApplicationData(T tree) {
-            this.tree = tree;
-        }
-
-        /**
-         * @return the lastFoundSequences
-         */
-        private Sequences<T> getLastFoundSequences() {
-            return lastFoundSequences;
-        }
-
-        /**
-         * @param lastFoundSequences the lastFoundSequences to set
-         */
-        private void setLastFoundSequences(Sequences<T> lastFoundSequences) {
-            this.lastFoundSequences = lastFoundSequences;
+        private RuleApplicationData(List<ISequence> sessions) {
+            this.sessions = sessions;
+        }
+
+        /**
+         * @return the tree
+         */
+        private List<ISequence> getSessions() {
+            return sessions;
+        }
+
+        /**
+         * @param lastTrie the lastTrie to set
+         */
+        private void setLastTrie(Trie<ITaskTreeNode> lastTrie) {
+            this.lastTrie = lastTrie;
         }
 
@@ -570,6 +805,13 @@
          * @return the lastTrie
          */
-        private Trie<T> getLastTrie() {
+        private Trie<ITaskTreeNode> getLastTrie() {
             return lastTrie;
+        }
+
+        /**
+         * @param trainedSequenceLength the trainedSequenceLength to set
+         */
+        private void setTrainedSequenceLength(int trainedSequenceLength) {
+            this.trainedSequenceLength = trainedSequenceLength;
         }
 
@@ -582,24 +824,59 @@
 
         /**
-         * @param trainedSequenceLength the trainedSequenceLength to set
-         */
-        private void setTrainedSequenceLength(int trainedSequenceLength) {
-            this.trainedSequenceLength = trainedSequenceLength;
-        }
-
-        /**
-         * @param lastTrie the lastTrie to set
-         */
-        private void setLastTrie(Trie<T> lastTrie) {
-            this.lastTrie = lastTrie;
-        }
-
-        /**
-         * @return the tree
-         */
-        private T getTree() {
-            return tree;
-        }
-
+         * @param lastFoundSequences the lastFoundSequences to set
+         */
+        private void setLastFoundTasks(Tasks lastFoundSequences) {
+            this.lastFoundTasks = lastFoundSequences;
+        }
+
+        /**
+         * @return the lastFoundSequences
+         */
+        private Tasks getLastFoundTasks() {
+            return lastFoundTasks;
+        }
+
+        /**
+         * @return the taskOccurrences
+         */
+        private void clearTaskOccurrences() {
+            taskOccurrences.clear();
+        }
+
+        /**
+         * @return the taskOccurrences
+         */
+        private void addTaskOccurrence(ITaskTreeNode taskOccurrence) {
+            taskOccurrences.add(taskOccurrence);
+        }
+
+        /**
+         * @return the taskOccurrences
+         */
+        private List<ITaskTreeNode> getTaskOccurrences() {
+            return taskOccurrences;
+        }
+
+        /**
+         *
+         */
+        private void resetReplacementCounter() {
+            replacementCounter = 0;
+        }
+
+        /**
+         *
+         */
+        private void incrementReplacementCounter() {
+            replacementCounter++;
+        }
+
+        /**
+         *
+         */
+        private int getReplacementCounter() {
+            return replacementCounter;
+        }
+        
         /**
          * @return the result
@@ -616,24 +893,4 @@
         }
 
-        /**
-         *
-         */
-        private void resetReplacementCounter() {
-            replacementCounter = 0;
-        }
-
-        /**
-         *
-         */
-        private void incrementReplacementCounter() {
-            replacementCounter++;
-        }
-
-        /**
-         *
-         */
-        private int getReplacementCounter() {
-            return replacementCounter;
-        }
     }
     
@@ -646,5 +903,5 @@
      * @author Patrick Harms
      */
-    private class Sequences<T> implements Iterable<List<T>> {
+    private class Tasks implements Iterable<List<ITaskTreeNode>> {
         
         /**
@@ -656,5 +913,5 @@
          * 
          */
-        private List<List<T>> sequences;
+        private List<List<ITaskTreeNode>> sequences;
 
         /**
@@ -666,5 +923,5 @@
          * @param sequences
          */
-        private Sequences(int occurrenceCount, List<List<T>> sequences) {
+        private Tasks(int occurrenceCount, List<List<ITaskTreeNode>> sequences) {
             super();
             this.occurrenceCount = occurrenceCount;
@@ -702,5 +959,5 @@
          */
         @Override
-        public Iterator<List<T>> iterator() {
+        public Iterator<List<ITaskTreeNode>> iterator() {
             return this.sequences.iterator();
         }
