Index: /branches/2886_SymRegGrammarEnumeration/HeuristicLab.Algorithms.DataAnalysis.SymRegGrammarEnumeration/Analysis/IGrammarEnumerationAnalyzer.cs
===================================================================
--- /branches/2886_SymRegGrammarEnumeration/HeuristicLab.Algorithms.DataAnalysis.SymRegGrammarEnumeration/Analysis/IGrammarEnumerationAnalyzer.cs	(revision 15821)
+++ /branches/2886_SymRegGrammarEnumeration/HeuristicLab.Algorithms.DataAnalysis.SymRegGrammarEnumeration/Analysis/IGrammarEnumerationAnalyzer.cs	(revision 15821)
@@ -0,0 +1,8 @@
+﻿using HeuristicLab.Core;
+
+namespace HeuristicLab.Algorithms.DataAnalysis.SymRegGrammarEnumeration {
+  public interface IGrammarEnumerationAnalyzer : IItem {
+    void Register(GrammarEnumerationAlgorithm g);
+    void Deregister(GrammarEnumerationAlgorithm algorithm);
+  }
+}
Index: /branches/2886_SymRegGrammarEnumeration/HeuristicLab.Algorithms.DataAnalysis.SymRegGrammarEnumeration/Analysis/SearchGraphVisualizer.cs
===================================================================
--- /branches/2886_SymRegGrammarEnumeration/HeuristicLab.Algorithms.DataAnalysis.SymRegGrammarEnumeration/Analysis/SearchGraphVisualizer.cs	(revision 15821)
+++ /branches/2886_SymRegGrammarEnumeration/HeuristicLab.Algorithms.DataAnalysis.SymRegGrammarEnumeration/Analysis/SearchGraphVisualizer.cs	(revision 15821)
@@ -0,0 +1,75 @@
+﻿using System;
+using System.Diagnostics;
+using System.IO;
+using HeuristicLab.Algorithms.DataAnalysis.SymRegGrammarEnumeration.GrammarEnumeration;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+
+namespace HeuristicLab.Algorithms.DataAnalysis.SymRegGrammarEnumeration {
+  class SearchGraphVisualizer : Item, IGrammarEnumerationAnalyzer {
+    private readonly string dotFileName = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory) + @"\searchgraph.dot";
+
+    private TextWriterTraceListener dotFileTrace;
+
+    public SearchGraphVisualizer() { }
+
+    protected SearchGraphVisualizer(SearchGraphVisualizer original, Cloner cloner) { }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new SearchGraphVisualizer(this, cloner);
+    }
+
+    public void Register(GrammarEnumerationAlgorithm algorithm) {
+      algorithm.Started += GrammarEnumerationAlgorithmOnStarted;
+      algorithm.Stopped += GrammarEnumerationAlgorithmOnStopped;
+      algorithm.ExceptionOccurred += GrammarEnumerationAlgorithmOnStopped;
+
+      algorithm.PhraseFetched += PhraseFetched;
+      algorithm.PhraseDerived += PhraseDerived;
+      algorithm.SentenceGenerated += SentenceGenerated;
+    }
+
+    public void Deregister(GrammarEnumerationAlgorithm algorithm) {
+      algorithm.Started -= GrammarEnumerationAlgorithmOnStarted;
+      algorithm.Stopped -= GrammarEnumerationAlgorithmOnStopped;
+      algorithm.ExceptionOccurred -= GrammarEnumerationAlgorithmOnStopped;
+
+      algorithm.PhraseFetched -= PhraseFetched;
+      algorithm.PhraseDerived -= PhraseDerived;
+      algorithm.SentenceGenerated -= SentenceGenerated;
+    }
+
+    private void GrammarEnumerationAlgorithmOnStarted(object sender, EventArgs eventArgs) {
+      dotFileTrace = new TextWriterTraceListener(new FileStream(dotFileName, FileMode.Create));
+      ((StreamWriter)dotFileTrace.Writer).AutoFlush = true;
+      dotFileTrace.WriteLine("digraph searchgraph { pad=0.02; nodesep=0.3; ranksep=0.02;ratio=0.5625;");
+    }
+
+    private void GrammarEnumerationAlgorithmOnStopped(object sender, EventArgs eventArgs) {
+      try {
+        var alg = (GrammarEnumerationAlgorithm)sender;
+        var phrase0 = new SymbolString(new[] { alg.Grammar.StartSymbol });
+        var phrase0Hash = alg.Grammar.CalcHashCode(phrase0);
+
+        dotFileTrace.WriteLine($"{phrase0Hash} [label=\"{phrase0}\", shape=doublecircle]; }}");
+        dotFileTrace.Flush();
+      } catch (Exception) {
+        dotFileTrace.Close();
+        throw;
+      }
+    }
+
+    private void PhraseFetched(object sender, PhraseEventArgs phraseEventArgs) {
+      dotFileTrace.WriteLine($"{phraseEventArgs.Hash} [label=\"{phraseEventArgs.Phrase}\"];");
+    }
+
+    private void PhraseDerived(object sender, PhraseAddedEventArgs args) {
+      dotFileTrace.WriteLine($"{args.ParentHash} -> {args.NewHash} [label=\"{args.ExpandedSymbol.StringRepresentation} + &rarr; {args.ExpandedProduction}\"];");
+    }
+
+    private void SentenceGenerated(object sender, PhraseAddedEventArgs args) {
+      var alg = (GrammarEnumerationAlgorithm)sender;
+      dotFileTrace.WriteLine($"{args.NewHash} [label=\"{alg.Grammar.ToInfixString(args.NewPhrase)}\", style=\"filled\", fillcolor=\"#f79423\", color=\"#f79423\"];");
+    }
+  }
+}
Index: /branches/2886_SymRegGrammarEnumeration/HeuristicLab.Algorithms.DataAnalysis.SymRegGrammarEnumeration/Analysis/SentenceLogger.cs
===================================================================
--- /branches/2886_SymRegGrammarEnumeration/HeuristicLab.Algorithms.DataAnalysis.SymRegGrammarEnumeration/Analysis/SentenceLogger.cs	(revision 15821)
+++ /branches/2886_SymRegGrammarEnumeration/HeuristicLab.Algorithms.DataAnalysis.SymRegGrammarEnumeration/Analysis/SentenceLogger.cs	(revision 15821)
@@ -0,0 +1,76 @@
+﻿using System;
+using System.Diagnostics;
+using System.IO;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+
+namespace HeuristicLab.Algorithms.DataAnalysis.SymRegGrammarEnumeration {
+  class SentenceLogger : Item, IGrammarEnumerationAnalyzer {
+    private readonly string distinctSentencesFileName = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory) + @"\distinctSentences.csv";
+    private readonly string allSentencesFileName = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory) + @"\allSentences.csv";
+    private readonly string columnDelimiter = ";";
+    private readonly string header = "infix;postfix;hash";
+
+    private TextWriterTraceListener distinctSentencesFileTrace;
+    private TextWriterTraceListener allSentencesFileTrace;
+
+    public SentenceLogger() { }
+
+    protected SentenceLogger(SentenceLogger original, Cloner cloner) { }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new SentenceLogger(this, cloner);
+    }
+
+    public void Register(GrammarEnumerationAlgorithm algorithm) {
+      algorithm.Started += GrammarEnumerationAlgorithmOnStarted;
+      algorithm.Stopped += GrammarEnumerationAlgorithmOnStopped;
+      algorithm.ExceptionOccurred += GrammarEnumerationAlgorithmOnStopped;
+
+      algorithm.SentenceGenerated += SentenceGenerated;
+      algorithm.DistinctSentenceGenerated += DistinctSentenceGenerated;
+    }
+
+    public void Deregister(GrammarEnumerationAlgorithm algorithm) {
+      algorithm.Started -= GrammarEnumerationAlgorithmOnStarted;
+      algorithm.Stopped -= GrammarEnumerationAlgorithmOnStopped;
+      algorithm.ExceptionOccurred -= GrammarEnumerationAlgorithmOnStopped;
+
+      algorithm.SentenceGenerated -= SentenceGenerated;
+      algorithm.DistinctSentenceGenerated -= DistinctSentenceGenerated;
+    }
+
+    private void GrammarEnumerationAlgorithmOnStarted(object sender, EventArgs eventArgs) {
+      distinctSentencesFileTrace = new TextWriterTraceListener(new FileStream(distinctSentencesFileName, FileMode.Create));
+      allSentencesFileTrace = new TextWriterTraceListener(new FileStream(allSentencesFileName, FileMode.Create));
+      ((StreamWriter)distinctSentencesFileTrace.Writer).AutoFlush = true;
+      ((StreamWriter)allSentencesFileTrace.Writer).AutoFlush = true;
+
+      distinctSentencesFileTrace.WriteLine(header);
+      allSentencesFileTrace.WriteLine(header);
+    }
+
+    private void GrammarEnumerationAlgorithmOnStopped(object sender, EventArgs eventArgs) {
+      distinctSentencesFileTrace.Close();
+      allSentencesFileTrace.Close();
+    }
+
+    private void DistinctSentenceGenerated(object sender, PhraseAddedEventArgs phraseAddedEventArgs) {
+      distinctSentencesFileTrace.WriteLine(ToCsvLine(
+        ((GrammarEnumerationAlgorithm)sender).Grammar.ToInfixString(phraseAddedEventArgs.NewPhrase),
+        phraseAddedEventArgs.NewPhrase.ToString(),
+        phraseAddedEventArgs.NewHash.ToString()));
+    }
+
+    private void SentenceGenerated(object sender, PhraseAddedEventArgs phraseAddedEventArgs) {
+      allSentencesFileTrace.WriteLine(ToCsvLine(
+        ((GrammarEnumerationAlgorithm)sender).Grammar.ToInfixString(phraseAddedEventArgs.NewPhrase),
+        phraseAddedEventArgs.NewPhrase.ToString(),
+        phraseAddedEventArgs.NewHash.ToString()));
+    }
+
+    private string ToCsvLine(params string[] cols) {
+      return string.Join(columnDelimiter, cols);
+    }
+  }
+}
Index: /branches/2886_SymRegGrammarEnumeration/HeuristicLab.Algorithms.DataAnalysis.SymRegGrammarEnumeration/GrammarEnumeration/Grammar.cs
===================================================================
--- /branches/2886_SymRegGrammarEnumeration/HeuristicLab.Algorithms.DataAnalysis.SymRegGrammarEnumeration/GrammarEnumeration/Grammar.cs	(revision 15820)
+++ /branches/2886_SymRegGrammarEnumeration/HeuristicLab.Algorithms.DataAnalysis.SymRegGrammarEnumeration/GrammarEnumeration/Grammar.cs	(revision 15821)
@@ -45,5 +45,4 @@
 
     #region HL Symbols for Parsing ExpressionTrees
-    private TypeCoherentExpressionGrammar symbolicExpressionGrammar;
 
     private ISymbol constSy;
@@ -57,7 +56,9 @@
     private ISymbol sinSy;
     private ISymbol cosSy;
-    
+
     private ISymbol rootSy;
     private ISymbol startSy;
+
+    private InfixExpressionFormatter infixExpressionFormatter;
     #endregion
 
@@ -73,5 +74,5 @@
       SinFactor = new NonterminalSymbol("SinFactor");
       CosFactor = new NonterminalSymbol("CosFactor");
-         
+
       SimpleExpr = new NonterminalSymbol("SimpleExpr");
       SimpleTerm = new NonterminalSymbol("SimpleTerm");
@@ -127,5 +128,5 @@
 
       #region Parsing to SymbolicExpressionTree
-      symbolicExpressionGrammar = new TypeCoherentExpressionGrammar();
+      var symbolicExpressionGrammar = new TypeCoherentExpressionGrammar();
       symbolicExpressionGrammar.ConfigureAsDefaultRegressionGrammar();
 
@@ -143,4 +144,5 @@
       startSy = symbolicExpressionGrammar.Symbols.OfType<StartSymbol>().First();
 
+      infixExpressionFormatter = new InfixExpressionFormatter();
       #endregion
     }
@@ -274,9 +276,15 @@
 
     #region Parse to SymbolicExpressionTree
+
+    public string ToInfixString(SymbolString sentence) {
+      Debug.Assert(sentence.Any(), "Trying to evaluate empty sentence!");
+      Debug.Assert(sentence.All(s => s is TerminalSymbol), "Trying to evaluate symbol sequence with nonterminalsymbols!");
+
+      return infixExpressionFormatter.Format(ParseSymbolicExpressionTree(sentence));
+    }
+
     public SymbolicExpressionTree ParseSymbolicExpressionTree(SymbolString sentence) {
       Debug.Assert(sentence.Any(), "Trying to evaluate empty sentence!");
       Debug.Assert(sentence.All(s => s is TerminalSymbol), "Trying to evaluate symbol sequence with nonterminalsymbols!");
-
-      symbolicExpressionGrammar.ConfigureAsDefaultRegressionGrammar();     // TODO: not necessary to call this for each sentence
 
       var rootNode = rootSy.CreateTreeNode();
@@ -339,42 +347,4 @@
     }
     #endregion
-
-    #region Parse to Infix string
-
-    public SymbolString PostfixToInfixParser(SymbolString phrase) {
-      Stack<Symbol> parseStack = new Stack<Symbol>(phrase);
-
-      return PostfixToInfixSubtreeParser(parseStack);
-    }
-
-    private SymbolString PostfixToInfixSubtreeParser(Stack<Symbol> parseStack) {
-      Symbol head = parseStack.Pop();
-
-      SymbolString result = new SymbolString(parseStack.Count);
-
-      if (ReferenceEquals(head, Addition) || ReferenceEquals(head, Multiplication)) {
-        // right part
-        SymbolString rightPart = PostfixToInfixSubtreeParser(parseStack);
-        SymbolString leftPart = PostfixToInfixSubtreeParser(parseStack);
-
-        result.AddRange(leftPart);
-        result.Add(head);
-        result.AddRange(rightPart);
-
-      } else if (ReferenceEquals(head, Log) || ReferenceEquals(head, Exp)
-              || ReferenceEquals(head, Sin) || ReferenceEquals(head, Cos) 
-              || ReferenceEquals(head, Inv)) {
-        result.Add(head);
-        result.Add(OpeningBracket);
-        result.AddRange(PostfixToInfixSubtreeParser(parseStack));
-        result.Add(ClosingBracket);
-
-      } else {
-        result.Add(head);
-      }
-      return result;
-    }
-
-    #endregion
   }
 }
Index: /branches/2886_SymRegGrammarEnumeration/HeuristicLab.Algorithms.DataAnalysis.SymRegGrammarEnumeration/GrammarEnumeration/GrammarEnumerationAlgorithm.cs
===================================================================
--- /branches/2886_SymRegGrammarEnumeration/HeuristicLab.Algorithms.DataAnalysis.SymRegGrammarEnumeration/GrammarEnumeration/GrammarEnumerationAlgorithm.cs	(revision 15820)
+++ /branches/2886_SymRegGrammarEnumeration/HeuristicLab.Algorithms.DataAnalysis.SymRegGrammarEnumeration/GrammarEnumeration/GrammarEnumerationAlgorithm.cs	(revision 15821)
@@ -1,9 +1,8 @@
 ﻿using System;
 using System.Collections.Generic;
-using System.Diagnostics;
-using System.IO;
 using System.Linq;
 using System.Threading;
 using HeuristicLab.Algorithms.DataAnalysis.SymRegGrammarEnumeration.GrammarEnumeration;
+using HeuristicLab.Collections;
 using HeuristicLab.Common;
 using HeuristicLab.Core;
@@ -30,6 +29,7 @@
     private readonly string DistinctSentencesName = "Distinct Sentences";
     private readonly string PhraseExpansionsName = "Phrase Expansions";
-    private readonly string AverageTreeLengthName = "Avg. Sentence Length among Distinct";
-    private readonly string GeneratedEqualSentencesName = "Generated equal sentences";
+    private readonly string AverageSentenceLengthName = "Avg. Sentence Length among Distinct";
+    private readonly string OverwrittenSentencesName = "Sentences overwritten";
+    private readonly string AnalyzersParameterName = "Analyzers";
 
 
@@ -64,21 +64,32 @@
     }
 
+    public IFixedValueParameter<ReadOnlyCheckedItemCollection<IGrammarEnumerationAnalyzer>> AnalyzersParameter {
+      get { return (IFixedValueParameter<ReadOnlyCheckedItemCollection<IGrammarEnumerationAnalyzer>>)Parameters[AnalyzersParameterName]; }
+    }
+
+    public ICheckedItemCollection<IGrammarEnumerationAnalyzer> Analyzers {
+      get { return AnalyzersParameter.Value; }
+    }
+
     public SymbolString BestTrainingSentence;
 
     #endregion
 
-    public Dictionary<int, SymbolString> DistinctSentences { get; private set; }  // Semantically distinct sentences in a run.
-    public Dictionary<int, List<SymbolString>> AllSentences { get; private set; } // All sentences ever generated in a run.
+    public Dictionary<int, int> DistinctSentencesLength { get; private set; }  // Semantically distinct sentences and their length in a run.
     public HashSet<int> ArchivedPhrases { get; private set; }
-
-    internal SearchDataStore OpenPhrases { get; private set; }                    // Stack/Queue/etc. for fetching the next node in the search tree.  
-
-    public int EqualGeneratedSentences { get; private set; } // It is not guaranteed that shorter solutions are found first. 
-                                                             // When longer solutions are overwritten with shorter ones, 
-                                                             // this counter is increased.
-    public int Expansions { get; private set; }              // Number, how many times a nonterminal symbol is replaced with a production rule.
+    internal SearchDataStore OpenPhrases { get; private set; }           // Stack/Queue/etc. for fetching the next node in the search tree.  
+
+    #region execution stats
+    public int AllGeneratedSentencesCount { get; private set; }
+
+    public int OverwrittenSentencesCount { get; private set; } // It is not guaranteed that shorter solutions are found first. 
+                                                               // When longer solutions are overwritten with shorter ones, 
+                                                               // this counter is increased.
+    public int PhraseExpansionCount { get; private set; }      // Number, how many times a nonterminal symbol is replaced with a production rule.
+    #endregion
+
     public Grammar Grammar { get; private set; }
 
-    private readonly string dotFileName = Environment.GetFolderPath(System.Environment.SpecialFolder.DesktopDirectory) + @"\searchgraph.dot";
+    private readonly ISymbolicDataAnalysisExpressionTreeInterpreter expressionTreeLinearInterpreter = new SymbolicDataAnalysisExpressionTreeLinearInterpreter();
 
     #region ctors
@@ -95,4 +106,17 @@
       Parameters.Add(new ValueParameter<IntValue>(GuiUpdateIntervalParameterName, "Number of generated sentences, until GUI is refreshed.", new IntValue(1000)));
       Parameters.Add(new ValueParameter<EnumValue<StorageType>>(SearchDataStructureParameterName, new EnumValue<StorageType>(StorageType.Stack)));
+
+      var availableAnalyzers = new IGrammarEnumerationAnalyzer[] {
+        new SearchGraphVisualizer(),
+        new SentenceLogger()
+      };
+      Parameters.Add(new FixedValueParameter<ReadOnlyCheckedItemCollection<IGrammarEnumerationAnalyzer>>(
+        AnalyzersParameterName,
+        new CheckedItemCollection<IGrammarEnumerationAnalyzer>(availableAnalyzers).AsReadOnly()));
+
+      foreach (var analyzer in Analyzers) {
+        Analyzers.SetItemCheckedState(analyzer, false);
+      }
+      Analyzers.CheckedItemsChanged += AnalyzersOnCheckedItemsChanged;
     }
 
@@ -104,10 +128,10 @@
       InitResults();
 
-      AllSentences = new Dictionary<int, List<SymbolString>>();
       ArchivedPhrases = new HashSet<int>();
 
-      DistinctSentences = new Dictionary<int, SymbolString>();
-      Expansions = 0;
-      EqualGeneratedSentences = 0;
+      DistinctSentencesLength = new Dictionary<int, int>();
+      AllGeneratedSentencesCount = 0;
+      OverwrittenSentencesCount = 0;
+      PhraseExpansionCount = 0;
 
       Grammar = new Grammar(Problem.ProblemData.AllowedInputVariables.ToArray());
@@ -118,62 +142,52 @@
       #endregion
 
-      using (TextWriterTraceListener dotFileTrace = new TextWriterTraceListener(new FileStream(dotFileName, FileMode.Create))) {
-        LogSearchGraph(dotFileTrace, "digraph searchgraph { pad=0.02; nodesep=0.3; ranksep=0.02;ratio=0.5625;");
-
-        OpenPhrases.Store(phrase0Hash, phrase0);
-        while (OpenPhrases.Count > 0) {
-          if (cancellationToken.IsCancellationRequested) break;
-
-          StoredSymbolString fetchedPhrase = OpenPhrases.GetNext();
-          SymbolString currPhrase = fetchedPhrase.SymbolString;
-#if DEBUG
-          LogSearchGraph(dotFileTrace, $"{fetchedPhrase.Hash} [label=\"{Grammar.PostfixToInfixParser(fetchedPhrase.SymbolString)}\"];");
-#endif
-          ArchivedPhrases.Add(fetchedPhrase.Hash);
-
-          // expand next nonterminal symbols
-          int nonterminalSymbolIndex = currPhrase.FindIndex(s => s is NonterminalSymbol);
-          NonterminalSymbol expandedSymbol = currPhrase[nonterminalSymbolIndex] as NonterminalSymbol;
-
-          foreach (Production productionAlternative in expandedSymbol.Alternatives) {
-            SymbolString newPhrase = new SymbolString(currPhrase.Count + productionAlternative.Count);
-            newPhrase.AddRange(currPhrase);
-            newPhrase.RemoveAt(nonterminalSymbolIndex);     // TODO: removeat and insertRange are both O(n)
-            newPhrase.InsertRange(nonterminalSymbolIndex, productionAlternative);
-
-            Expansions++;
-            if (newPhrase.Count <= MaxTreeSize) {
-              var phraseHash = Grammar.CalcHashCode(newPhrase);
-#if DEBUG
-              LogSearchGraph(dotFileTrace, $"{fetchedPhrase.Hash} -> {phraseHash} [label=\"{expandedSymbol.StringRepresentation} + &rarr; {productionAlternative}\"];");
-#endif
-              if (newPhrase.IsSentence()) {
-                // Sentence was generated.
-                SaveToAllSentences(phraseHash, newPhrase);
-
-                if (!DistinctSentences.ContainsKey(phraseHash) || DistinctSentences[phraseHash].Count > newPhrase.Count) {
-                  if (DistinctSentences.ContainsKey(phraseHash)) EqualGeneratedSentences++; // for analysis only
-
-                  DistinctSentences[phraseHash] = newPhrase;
-                  EvaluateSentence(newPhrase);
-
-#if DEBUG
-                  LogSearchGraph(dotFileTrace, $"{phraseHash} [label=\"{Grammar.PostfixToInfixParser(newPhrase)}\", style=\"filled\", fillcolor=\"#f79423\", color=\"#f79423\"];");
-#endif
-                }
-                UpdateView();
-
-              } else if (!OpenPhrases.Contains(phraseHash) && !ArchivedPhrases.Contains(phraseHash)) {
-                OpenPhrases.Store(phraseHash, newPhrase);
+      OpenPhrases.Store(phrase0Hash, phrase0);
+      while (OpenPhrases.Count > 0) {
+        if (cancellationToken.IsCancellationRequested) break;
+
+        StoredSymbolString fetchedPhrase = OpenPhrases.GetNext();
+        SymbolString currPhrase = fetchedPhrase.SymbolString;
+
+        OnPhraseFetched(fetchedPhrase.Hash, currPhrase);
+
+        ArchivedPhrases.Add(fetchedPhrase.Hash);
+
+        // expand next nonterminal symbols
+        int nonterminalSymbolIndex = currPhrase.FindIndex(s => s is NonterminalSymbol);
+        NonterminalSymbol expandedSymbol = currPhrase[nonterminalSymbolIndex] as NonterminalSymbol;
+
+        foreach (Production productionAlternative in expandedSymbol.Alternatives) {
+          PhraseExpansionCount++;
+
+          SymbolString newPhrase = new SymbolString(currPhrase.Count + productionAlternative.Count);
+          newPhrase.AddRange(currPhrase);
+          newPhrase.RemoveAt(nonterminalSymbolIndex);     // TODO: removeat and insertRange are both O(n)
+          newPhrase.InsertRange(nonterminalSymbolIndex, productionAlternative);
+
+          if (newPhrase.Count <= MaxTreeSize) {
+            var phraseHash = Grammar.CalcHashCode(newPhrase);
+
+            OnPhraseDerived(fetchedPhrase.Hash, fetchedPhrase.SymbolString, phraseHash, newPhrase, expandedSymbol, productionAlternative);
+
+            if (newPhrase.IsSentence()) {
+              AllGeneratedSentencesCount++;
+
+              OnSentenceGenerated(fetchedPhrase.Hash, fetchedPhrase.SymbolString, phraseHash, newPhrase, expandedSymbol, productionAlternative);
+
+              if (!DistinctSentencesLength.ContainsKey(phraseHash) || DistinctSentencesLength[phraseHash] > newPhrase.Count) {
+                if (DistinctSentencesLength.ContainsKey(phraseHash)) OverwrittenSentencesCount++; // for analysis only
+
+                DistinctSentencesLength[phraseHash] = newPhrase.Count;
+                EvaluateSentence(newPhrase);
+
+                OnDistinctSentenceGenerated(fetchedPhrase.Hash, fetchedPhrase.SymbolString, phraseHash, newPhrase, expandedSymbol, productionAlternative);
               }
+              UpdateView();
+
+            } else if (!OpenPhrases.Contains(phraseHash) && !ArchivedPhrases.Contains(phraseHash)) {
+              OpenPhrases.Store(phraseHash, newPhrase);
             }
           }
         }
-#if DEBUG
-        // Overwrite formatting of start search node and best found solution.
-        //LogSearchGraph(dotFileTrace, $"{Grammar.CalcHashCode(BestTrainingSentence)} [label=\"{Grammar.PostfixToInfixParser(BestTrainingSentence)}\", shape=Mcircle, style=\"filled,bold\"];");
-        LogSearchGraph(dotFileTrace, $"{phrase0Hash} [label=\"{Grammar.PostfixToInfixParser(phrase0)}\", shape=doublecircle];}}");
-        dotFileTrace.Flush();
-#endif
       }
 
@@ -182,13 +196,5 @@
     }
 
-    // Store sentence to "MultiDictionary"
-    private void SaveToAllSentences(int hash, SymbolString sentence) {
-      if (AllSentences.ContainsKey(hash))
-        AllSentences[hash].Add(sentence);
-      else
-        AllSentences[hash] = new List<SymbolString> { sentence }; //TODO: here we store all sentences even if they have the same hash value, this is not strictly necessary
-    }
-
-#region Evaluation of generated models.
+    #region Evaluation of generated models.
 
     // Evaluate sentence within an algorithm run.
@@ -198,5 +204,5 @@
         Problem.ProblemData.TargetVariable,
         tree,
-        new SymbolicDataAnalysisExpressionTreeLinearInterpreter());
+        expressionTreeLinearInterpreter);
 
       var probData = Problem.ProblemData;
@@ -214,7 +220,7 @@
     }
 
-#endregion
-
-#region Visualization in HL
+    #endregion
+
+    #region Visualization in HL
     // Initialize entries in result set.
     private void InitResults() {
@@ -228,6 +234,6 @@
       Results.Add(new Result(DistinctSentencesName, new IntValue(0)));
       Results.Add(new Result(PhraseExpansionsName, new IntValue(0)));
-      Results.Add(new Result(GeneratedEqualSentencesName, new IntValue(0)));
-      Results.Add(new Result(AverageTreeLengthName, new DoubleValue(1.0)));
+      Results.Add(new Result(OverwrittenSentencesName, new IntValue(0)));
+      Results.Add(new Result(AverageSentenceLengthName, new DoubleValue(1.0)));
     }
 
@@ -238,12 +244,11 @@
 
       if (force || updates % GuiUpdateInterval == 1) {
-        var allGeneratedEnum = AllSentences.Values.SelectMany(x => x).ToArray();
         ((IntValue)Results[GeneratedPhrasesName].Value).Value = ArchivedPhrases.Count;
         ((IntValue)Results[SearchStructureSizeName].Value).Value = OpenPhrases.Count;
-        ((IntValue)Results[GeneratedSentencesName].Value).Value = allGeneratedEnum.Length;
-        ((IntValue)Results[DistinctSentencesName].Value).Value = DistinctSentences.Count;
-        ((IntValue)Results[PhraseExpansionsName].Value).Value = Expansions;
-        ((IntValue)Results[GeneratedEqualSentencesName].Value).Value = EqualGeneratedSentences;
-        ((DoubleValue)Results[AverageTreeLengthName].Value).Value = allGeneratedEnum.Select(sentence => sentence.Count).Average();
+        ((IntValue)Results[GeneratedSentencesName].Value).Value = AllGeneratedSentencesCount;
+        ((IntValue)Results[DistinctSentencesName].Value).Value = DistinctSentencesLength.Count;
+        ((IntValue)Results[PhraseExpansionsName].Value).Value = PhraseExpansionCount;
+        ((DoubleValue)Results[AverageSentenceLengthName].Value).Value = DistinctSentencesLength.Select(pair => pair.Value).Average();
+        ((IntValue)Results[OverwrittenSentencesName].Value).Value = OverwrittenSentencesCount;
       }
     }
@@ -259,35 +264,84 @@
       IRegressionSolution bestTrainingSolution = new RegressionSolution(model, Problem.ProblemData);
       Results.AddOrUpdateResult(BestTrainingSolutionName, bestTrainingSolution);
-
-      // Print generated sentences.
-      string[,] sentencesMatrix = new string[AllSentences.Values.SelectMany(x => x).Count(), 3];
-
-      int i = 0;
-      foreach (var sentenceSet in AllSentences) {
-        foreach (var sentence in sentenceSet.Value) {
-          sentencesMatrix[i, 0] = sentence.ToString();
-          sentencesMatrix[i, 1] = Grammar.PostfixToInfixParser(sentence).ToString();
-          sentencesMatrix[i, 2] = sentenceSet.Key.ToString();
-          i++;
+    }
+    #endregion
+
+    #region events
+    private void AnalyzersOnCheckedItemsChanged(object sender, CollectionItemsChangedEventArgs<IGrammarEnumerationAnalyzer> collectionItemsChangedEventArgs) {
+      foreach (IGrammarEnumerationAnalyzer grammarEnumerationAnalyzer in collectionItemsChangedEventArgs.Items) {
+        if (Analyzers.ItemChecked(grammarEnumerationAnalyzer)) {
+          grammarEnumerationAnalyzer.Register(this);
+        } else {
+          grammarEnumerationAnalyzer.Deregister(this);
         }
       }
-      Results.Add(new Result("All generated sentences", new StringMatrix(sentencesMatrix)));
-
-      string[,] distinctSentencesMatrix = new string[DistinctSentences.Count, 3];
-      i = 0;
-      foreach (KeyValuePair<int, SymbolString> distinctSentence in DistinctSentences) {
-        distinctSentencesMatrix[i, 0] = distinctSentence.Key.ToString();
-        distinctSentencesMatrix[i, 1] = Grammar.PostfixToInfixParser(distinctSentence.Value).ToString();
-        distinctSentencesMatrix[i, 2] = distinctSentence.Key.ToString();
-        i++;
-      }
-      Results.Add(new Result("Distinct generated sentences", new StringMatrix(distinctSentencesMatrix)));
-    }
-
-    private void LogSearchGraph(TraceListener listener, string msg) {
-      listener.Write(msg);
-    }
-#endregion
+    }
+
+    public event EventHandler<PhraseEventArgs> PhraseFetched;
+    private void OnPhraseFetched(int hash, SymbolString symbolString) {
+      if (PhraseFetched != null) {
+        PhraseFetched(this, new PhraseEventArgs(hash, symbolString));
+      }
+    }
+
+    public event EventHandler<PhraseAddedEventArgs> PhraseDerived;
+    private void OnPhraseDerived(int parentHash, SymbolString parentSymbolString, int addedHash, SymbolString addedSymbolString, Symbol expandedSymbol, Production expandedProduction) {
+      if (PhraseDerived != null) {
+        PhraseDerived(this, new PhraseAddedEventArgs(parentHash, parentSymbolString, addedHash, addedSymbolString, expandedSymbol, expandedProduction));
+      }
+    }
+
+    public event EventHandler<PhraseAddedEventArgs> SentenceGenerated;
+    private void OnSentenceGenerated(int parentHash, SymbolString parentSymbolString, int addedHash, SymbolString addedSymbolString, Symbol expandedSymbol, Production expandedProduction) {
+      if (SentenceGenerated != null) {
+        SentenceGenerated(this, new PhraseAddedEventArgs(parentHash, parentSymbolString, addedHash, addedSymbolString, expandedSymbol, expandedProduction));
+      }
+    }
+
+    public event EventHandler<PhraseAddedEventArgs> DistinctSentenceGenerated;
+    private void OnDistinctSentenceGenerated(int parentHash, SymbolString parentSymbolString, int addedHash, SymbolString addedSymbolString, Symbol expandedSymbol, Production expandedProduction) {
+      if (DistinctSentenceGenerated != null) {
+        DistinctSentenceGenerated(this, new PhraseAddedEventArgs(parentHash, parentSymbolString, addedHash, addedSymbolString, expandedSymbol, expandedProduction));
+      }
+    }
+
+    #endregion
 
   }
+
+  #region events for analysis
+
+  public class PhraseEventArgs : EventArgs {
+    public int Hash { get; }
+
+    public SymbolString Phrase { get; }
+
+    public PhraseEventArgs(int hash, SymbolString phrase) {
+      Hash = hash;
+      Phrase = phrase;
+    }
+  }
+
+  public class PhraseAddedEventArgs : EventArgs {
+    public int ParentHash { get; }
+    public int NewHash { get; }
+
+    public SymbolString ParentPhrase { get; }
+    public SymbolString NewPhrase { get; }
+
+    public Symbol ExpandedSymbol { get; }
+
+    public Production ExpandedProduction { get; }
+
+    public PhraseAddedEventArgs(int parentHash, SymbolString parentPhrase, int newHash, SymbolString newPhrase, Symbol expandedSymbol, Production expandedProduction) {
+      ParentHash = parentHash;
+      ParentPhrase = parentPhrase;
+      NewHash = newHash;
+      NewPhrase = newPhrase;
+      ExpandedSymbol = expandedSymbol;
+      ExpandedProduction = expandedProduction;
+    }
+  }
+
+  #endregion
 }
Index: /branches/2886_SymRegGrammarEnumeration/HeuristicLab.Algorithms.DataAnalysis.SymRegGrammarEnumeration/HeuristicLab.Algorithms.DataAnalysis.SymRegGrammarEnumeration.csproj
===================================================================
--- /branches/2886_SymRegGrammarEnumeration/HeuristicLab.Algorithms.DataAnalysis.SymRegGrammarEnumeration/HeuristicLab.Algorithms.DataAnalysis.SymRegGrammarEnumeration.csproj	(revision 15820)
+++ /branches/2886_SymRegGrammarEnumeration/HeuristicLab.Algorithms.DataAnalysis.SymRegGrammarEnumeration/HeuristicLab.Algorithms.DataAnalysis.SymRegGrammarEnumeration.csproj	(revision 15821)
@@ -91,4 +91,7 @@
   </ItemGroup>
   <ItemGroup>
+    <Compile Include="Analysis\IGrammarEnumerationAnalyzer.cs" />
+    <Compile Include="Analysis\SentenceLogger.cs" />
+    <Compile Include="Analysis\SearchGraphVisualizer.cs" />
     <Compile Include="GrammarEnumeration\Grammar.cs" />
     <Compile Include="GrammarEnumeration\GrammarEnumerationAlgorithm.cs" />
Index: /branches/2886_SymRegGrammarEnumeration/Test/GrammarEnumerationTest.cs
===================================================================
--- /branches/2886_SymRegGrammarEnumeration/Test/GrammarEnumerationTest.cs	(revision 15820)
+++ /branches/2886_SymRegGrammarEnumeration/Test/GrammarEnumerationTest.cs	(revision 15821)
@@ -36,5 +36,5 @@
     public void Cleanup() {
       if (alg.BestTrainingSentence != null) {
-        Console.WriteLine("Training: " + alg.Grammar.PostfixToInfixParser(alg.BestTrainingSentence));
+        Console.WriteLine("Training: " + alg.Grammar.ToInfixString(alg.BestTrainingSentence));
       }
     }
@@ -75,5 +75,5 @@
       int actualSolutionHash = alg.Grammar.CalcHashCode(alg.BestTrainingSentence);
 
-      Assert.IsTrue(alg.DistinctSentences.ContainsKey(targetSolutionHash), "Actual solution was not generated!");
+      Assert.IsTrue(alg.DistinctSentencesLength.ContainsKey(targetSolutionHash), "Actual solution was not generated!");
 
       Assert.AreEqual(targetSolutionHash, actualSolutionHash, "Actual solution was not recognized as best one.");
@@ -130,5 +130,5 @@
       int actualSolutionHash = alg.Grammar.CalcHashCode(alg.BestTrainingSentence);
 
-      Assert.IsTrue(alg.DistinctSentences.ContainsKey(targetSolutionHash), "Actual solution was not generated!");
+      Assert.IsTrue(alg.DistinctSentencesLength.ContainsKey(targetSolutionHash), "Actual solution was not generated!");
       Assert.AreEqual(targetSolutionHash, actualSolutionHash, "Actual solution was not recognized as best one.");
 
@@ -159,5 +159,5 @@
       int actualSolutionHash = alg.Grammar.CalcHashCode(alg.BestTrainingSentence);
 
-      Assert.IsTrue(alg.DistinctSentences.ContainsKey(targetSolutionHash), "Actual solution was not generated!");
+      Assert.IsTrue(alg.DistinctSentencesLength.ContainsKey(targetSolutionHash), "Actual solution was not generated!");
       Assert.AreEqual(targetSolutionHash, actualSolutionHash, "Actual solution was not recognized as best one.");
 
Index: /branches/2886_SymRegGrammarEnumeration/Test/TreeHashingTest.cs
===================================================================
--- /branches/2886_SymRegGrammarEnumeration/Test/TreeHashingTest.cs	(revision 15820)
+++ /branches/2886_SymRegGrammarEnumeration/Test/TreeHashingTest.cs	(revision 15821)
@@ -179,5 +179,5 @@
       SymbolString infix = new SymbolString(new[] { varA, grammar.Addition, varB, grammar.Addition, varC });
 
-      Assert.AreEqual(infix.ToString(), grammar.PostfixToInfixParser(postfix).ToString());
+      Assert.AreEqual(infix.ToString(), grammar.ToInfixString(postfix));
     }
 
@@ -188,5 +188,5 @@
       SymbolString infix = new SymbolString(new[] { varB, grammar.Addition, varA, grammar.Addition, varB, grammar.Multiplication, varC });
 
-      Assert.AreEqual(infix.ToString(), grammar.PostfixToInfixParser(postfix).ToString());
+      Assert.AreEqual(infix.ToString(), grammar.ToInfixString(postfix));
     }
 
