Index: /branches/2886_SymRegGrammarEnumeration/HeuristicLab.Algorithms.DataAnalysis.SymRegGrammarEnumeration/Analysis/SearchGraphVisualizer.cs
===================================================================
--- /branches/2886_SymRegGrammarEnumeration/HeuristicLab.Algorithms.DataAnalysis.SymRegGrammarEnumeration/Analysis/SearchGraphVisualizer.cs	(revision 15831)
+++ /branches/2886_SymRegGrammarEnumeration/HeuristicLab.Algorithms.DataAnalysis.SymRegGrammarEnumeration/Analysis/SearchGraphVisualizer.cs	(revision 15832)
@@ -50,5 +50,5 @@
         var alg = (GrammarEnumerationAlgorithm)sender;
         var phrase0 = new SymbolString(new[] { alg.Grammar.StartSymbol });
-        var phrase0Hash = alg.Grammar.CalcHashCode(phrase0);
+        var phrase0Hash = alg.Grammar.Hasher.CalcHashCode(phrase0);
 
         dotFileTrace.WriteLine($"{phrase0Hash} [label=\"{phrase0}\", shape=doublecircle]; }}");
Index: /branches/2886_SymRegGrammarEnumeration/HeuristicLab.Algorithms.DataAnalysis.SymRegGrammarEnumeration/GrammarEnumeration/Grammar.cs
===================================================================
--- /branches/2886_SymRegGrammarEnumeration/HeuristicLab.Algorithms.DataAnalysis.SymRegGrammarEnumeration/GrammarEnumeration/Grammar.cs	(revision 15831)
+++ /branches/2886_SymRegGrammarEnumeration/HeuristicLab.Algorithms.DataAnalysis.SymRegGrammarEnumeration/GrammarEnumeration/Grammar.cs	(revision 15832)
@@ -1,8 +1,6 @@
-﻿using System;
-using System.Collections.Generic;
+﻿using System.Collections.Generic;
 using System.Diagnostics;
 using System.Linq;
 using HeuristicLab.Algorithms.DataAnalysis.SymRegGrammarEnumeration.GrammarEnumeration;
-using HeuristicLab.Common;
 using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
 using HeuristicLab.Problems.DataAnalysis.Symbolic;
@@ -12,4 +10,6 @@
 
     public Symbol StartSymbol { get; }
+
+    public Hasher<int> Hasher { get; }
 
     #region Symbols
@@ -146,130 +146,7 @@
       infixExpressionFormatter = new InfixExpressionFormatter();
       #endregion
-    }
-
-    #region Hashing
-    public int CalcHashCode(SymbolString sentence) {
-      return CalcHashCode<int>(sentence, AggregateIntHashes);
-    }
-
-    private int CalcHashCode<THashType>(SymbolString sentence, Func<Symbol, THashType[], THashType> aggregateFunction) {
-      Debug.Assert(sentence.Any(), "Trying to evaluate empty sentence!");
-
-      Stack<Symbol> parseStack = new Stack<Symbol>(sentence);
-
-      Symbol peek = parseStack.Peek();
-      return aggregateFunction(peek, GetSubtreeHashes(parseStack, aggregateFunction)).GetHashCode();
-    }
-
-    private THashType[] GetSubtreeHashes<THashType>(Stack<Symbol> parseStack, Func<Symbol, THashType[], THashType> aggregateHashes) {
-      Symbol currentSymbol = parseStack.Pop();
-
-      // ADDITION
-      if (currentSymbol == Addition) {
-        var uniqueChildHashes = new HashSet<THashType>();
-
-        // First subtree
-        if (parseStack.Peek() == Addition) {
-          uniqueChildHashes.UnionWith(GetSubtreeHashes(parseStack, aggregateHashes));
-        } else {
-          var peek = parseStack.Peek();
-          uniqueChildHashes.Add(aggregateHashes(peek, GetSubtreeHashes(parseStack, aggregateHashes)));
-        }
-        // Second subtree
-        if (parseStack.Peek() == Addition) {
-          uniqueChildHashes.UnionWith(GetSubtreeHashes(parseStack, aggregateHashes));
-        } else {
-          var peek = parseStack.Peek();
-          uniqueChildHashes.Add(aggregateHashes(peek, GetSubtreeHashes(parseStack, aggregateHashes)));
-        }
-
-        var result = uniqueChildHashes.ToArray();
-        Array.Sort(result);
-        return result;
-      }
-
-      // MULTIPLICATION
-      if (currentSymbol == Multiplication) {
-        var childHashes = new List<THashType>();
-
-        // First subtree
-        if (parseStack.Peek() == Multiplication) {
-          childHashes.AddRange(GetSubtreeHashes(parseStack, aggregateHashes));
-        } else {
-          childHashes.Add(aggregateHashes(parseStack.Peek(), GetSubtreeHashes(parseStack, aggregateHashes)));
-        }
-        // Second subtree
-        if (parseStack.Peek() == Multiplication) {
-          childHashes.AddRange(GetSubtreeHashes(parseStack, aggregateHashes));
-        } else {
-          childHashes.Add(aggregateHashes(parseStack.Peek(), GetSubtreeHashes(parseStack, aggregateHashes)));
-        }
-
-        // Sort due to commutativity
-        childHashes.Sort();
-
-        // Cancel out inverse factors.
-        bool[] isFactorRemaining = Enumerable.Repeat(true, childHashes.Count).ToArray();
-
-        for (int i = 0; i < isFactorRemaining.Length; i++) {
-          if (!isFactorRemaining[i]) continue;
-          if (isFactorRemaining.Length <= 2) break; // Until we have constants, we can't cancel out all terms.
-
-          var currFactor = childHashes[i];
-          var invFactor = aggregateHashes(Inv, new[] { currFactor });
-
-          int indexOfInv = childHashes.IndexOf(invFactor);
-          if (indexOfInv >= 0 && isFactorRemaining[indexOfInv]) {
-            isFactorRemaining[i] = isFactorRemaining[indexOfInv] = false;
-          }
-        }
-
-        return childHashes
-          .Where((c, i) => isFactorRemaining[i])
-          .ToArray();
-      }
-
-      // LOG, EXP, SIN, INV
-      if (currentSymbol == Log || currentSymbol == Exp ||
-          currentSymbol == Sin || currentSymbol == Cos ||
-          currentSymbol == Inv) {
-        return new[] { aggregateHashes(parseStack.Peek(), GetSubtreeHashes(parseStack, aggregateHashes)) };
-      }
-
-      // var or nonterminal symbol
-      return new[] { aggregateHashes(currentSymbol, new THashType[0]) };
-    }
-
-    private string AggregateStringHashes(Symbol operatorSym, string[] hashes) {
-      var hashesArray = hashes.ToArray();
-
-      if ((operatorSym == Addition || operatorSym == Multiplication) && hashesArray.Length <= 1) {
-        return hashesArray[0];
-      }
-      if (operatorSym is NonterminalSymbol || ((TerminalSymbol)operatorSym).IsVariable) {
-        return operatorSym.StringRepresentation;
-      }
-
-      return $"[{operatorSym.StringRepresentation} ° {string.Join(" ° ", hashesArray)}]";
-    }
-
-    private int AggregateIntHashes(Symbol operatorSym, int[] hashes) {
-      int start;
-      if ((operatorSym == Addition || operatorSym == Multiplication) && hashes.Length <= 1) {
-        start = 0;
-
-      } else if (operatorSym is NonterminalSymbol || ((TerminalSymbol)operatorSym).IsVariable) {
-        return operatorSym.StringRepresentation.GetHashCode();
-
-      } else {
-        start = operatorSym.StringRepresentation.GetHashCode();
-      }
-
-      for (int i = 0; i < hashes.Length; i++) {
-        start = ((start << 5) + start) ^ hashes[i];
-      }
-      return start;
-    }
-    #endregion
+
+      Hasher = new IntHasher(this);
+    }
 
     #region Parse to SymbolicExpressionTree
@@ -323,5 +200,5 @@
         parsedSubTree.AddSubtree(ParseSymbolicExpressionTree(parseStack));
 
-      } else if (currentSymbol ==  Cos) {
+      } else if (currentSymbol == Cos) {
         parsedSubTree = cosSy.CreateTreeNode();
         parsedSubTree.AddSubtree(ParseSymbolicExpressionTree(parseStack));
@@ -334,5 +211,5 @@
         parsedSubTree.AddSubtree(ParseSymbolicExpressionTree(parseStack));
 
-      } else if (currentSymbol.IsVariable) {
+      } else if (currentSymbol is VariableTerminalSymbol) {
         VariableTreeNode varNode = (VariableTreeNode)varSy.CreateTreeNode();
         varNode.Weight = 1.0;
Index: /branches/2886_SymRegGrammarEnumeration/HeuristicLab.Algorithms.DataAnalysis.SymRegGrammarEnumeration/GrammarEnumeration/GrammarEnumerationAlgorithm.cs
===================================================================
--- /branches/2886_SymRegGrammarEnumeration/HeuristicLab.Algorithms.DataAnalysis.SymRegGrammarEnumeration/GrammarEnumeration/GrammarEnumerationAlgorithm.cs	(revision 15831)
+++ /branches/2886_SymRegGrammarEnumeration/HeuristicLab.Algorithms.DataAnalysis.SymRegGrammarEnumeration/GrammarEnumeration/GrammarEnumerationAlgorithm.cs	(revision 15832)
@@ -135,5 +135,5 @@
       OpenPhrases = new SearchDataStore(SearchDataStructure); // Select search strategy
       var phrase0 = new SymbolString(new[] { Grammar.StartSymbol });
-      var phrase0Hash = Grammar.CalcHashCode(phrase0);
+      var phrase0Hash = Grammar.Hasher.CalcHashCode(phrase0);
       #endregion
 
@@ -160,5 +160,5 @@
 
           if (newPhrase.Count() <= MaxTreeSize) {
-            var phraseHash = Grammar.CalcHashCode(newPhrase);
+            var phraseHash = Grammar.Hasher.CalcHashCode(newPhrase);
 
             OnPhraseDerived(fetchedPhrase.Hash, fetchedPhrase.SymbolString, phraseHash, newPhrase, expandedSymbol, appliedProductions[i]);
Index: /branches/2886_SymRegGrammarEnumeration/HeuristicLab.Algorithms.DataAnalysis.SymRegGrammarEnumeration/GrammarEnumeration/Symbol.cs
===================================================================
--- /branches/2886_SymRegGrammarEnumeration/HeuristicLab.Algorithms.DataAnalysis.SymRegGrammarEnumeration/GrammarEnumeration/Symbol.cs	(revision 15831)
+++ /branches/2886_SymRegGrammarEnumeration/HeuristicLab.Algorithms.DataAnalysis.SymRegGrammarEnumeration/GrammarEnumeration/Symbol.cs	(revision 15832)
@@ -20,6 +20,7 @@
     #region IEquatable
     public static bool operator ==(Symbol s1, Symbol s2) {
-      return !ReferenceEquals(null, s1) && !ReferenceEquals(null, s2) && (ReferenceEquals(s1, s2)
-        || s1.Equals(s2));
+      if (ReferenceEquals(s1, s2)) return true;
+      if (ReferenceEquals(s1, null) || ReferenceEquals(s2, null)) return false;
+      return s1.Equals(s2);
     }
 
@@ -29,13 +30,14 @@
 
     public bool Equals(Symbol other) {
-      if (ReferenceEquals(null, other)) return false;
-      if (ReferenceEquals(this, other)) return true;
-      return string.Equals(StringRepresentation, other.StringRepresentation);
+      if (ReferenceEquals(other, null)) return false;
+      if (ReferenceEquals(other, this)) return true;
+      if (this.GetType() != other.GetType()) return false; // Otherwise, this needs to be reimplemented in derived classes.
+      return StringRepresentation == other.StringRepresentation;
     }
 
     public override bool Equals(object obj) {
-      if (ReferenceEquals(null, obj)) return false;
-      if (ReferenceEquals(this, obj)) return true;
-      if (obj.GetType() != this.GetType()) return false;
+      if (ReferenceEquals(obj, null)) return false;
+      if (ReferenceEquals(obj, this)) return true;
+      if (this.GetType() != obj.GetType()) return false;
       return Equals((Symbol)obj);
     }
@@ -48,32 +50,38 @@
 
   public class TerminalSymbol : Symbol {
-    public readonly bool IsVariable;
 
-    public TerminalSymbol(string representation, bool isVariable = false) : base(representation) {
-      IsVariable = isVariable;
-    }
+    public TerminalSymbol(string representation) : base(representation) { }
   }
 
+  public class VariableTerminalSymbol : TerminalSymbol {
+    public VariableTerminalSymbol(string representation) : base(representation) { }
+  }
+
+
   public class NonterminalSymbol : Symbol {
-    public List<Production> Alternatives { get; }
+    private readonly List<Production> alternatives;
+
+    public IReadOnlyList<Production> Alternatives {
+      get { return alternatives.AsReadOnly(); }
+    }
 
     public NonterminalSymbol(string representation) : base(representation) {
-      Alternatives = new List<Production>();
+      alternatives = new List<Production>();
     }
 
     public void AddProduction(params Symbol[] production) {
-      Alternatives.Add(new Production(production));
+      alternatives.Add(new Production(production));
     }
   }
 
   public class VariableSymbol : NonterminalSymbol { // Convenience class
-    public IEnumerable<TerminalSymbol> VariableTerminalSymbols { get; }
+    public IEnumerable<VariableTerminalSymbol> VariableTerminalSymbols { get; }
 
     public VariableSymbol(string representation, IEnumerable<string> variableNames) : base(representation) {
-      List<TerminalSymbol> createdSymbols = new List<TerminalSymbol>();
+      List<VariableTerminalSymbol> createdSymbols = new List<VariableTerminalSymbol>();
       VariableTerminalSymbols = createdSymbols;
 
       foreach (string variableName in variableNames) {
-        TerminalSymbol s = new TerminalSymbol(variableName, isVariable: true);
+        VariableTerminalSymbol s = new VariableTerminalSymbol(variableName);
         createdSymbols.Add(s);
         AddProduction(s);
Index: /branches/2886_SymRegGrammarEnumeration/HeuristicLab.Algorithms.DataAnalysis.SymRegGrammarEnumeration/Hashing/Hasher.cs
===================================================================
--- /branches/2886_SymRegGrammarEnumeration/HeuristicLab.Algorithms.DataAnalysis.SymRegGrammarEnumeration/Hashing/Hasher.cs	(revision 15832)
+++ /branches/2886_SymRegGrammarEnumeration/HeuristicLab.Algorithms.DataAnalysis.SymRegGrammarEnumeration/Hashing/Hasher.cs	(revision 15832)
@@ -0,0 +1,143 @@
+﻿using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+
+namespace HeuristicLab.Algorithms.DataAnalysis.SymRegGrammarEnumeration.GrammarEnumeration {
+  public abstract class Hasher<THashType> {
+    protected Hasher(Grammar grammar) {
+      Grammar = grammar;
+    }
+
+    protected Grammar Grammar { get; }
+
+    protected abstract THashType AggregateHashes(Symbol operatorSym, THashType[] hashes);
+
+    public int CalcHashCode(SymbolString sentence) {
+      Debug.Assert(sentence.Any(), "Trying to evaluate empty sentence!");
+
+      Stack<Symbol> parseStack = new Stack<Symbol>(sentence);
+
+      Symbol peek = parseStack.Peek();
+      return AggregateHashes(peek, GetSubtreeHashes(parseStack)).GetHashCode();
+    }
+
+    private THashType[] GetSubtreeHashes(Stack<Symbol> parseStack) {
+      Symbol currentSymbol = parseStack.Pop();
+
+      // ADDITION
+      if (currentSymbol == Grammar.Addition) {
+        var uniqueChildHashes = new HashSet<THashType>();
+
+        // First subtree
+        if (parseStack.Peek() == Grammar.Addition) {
+          uniqueChildHashes.UnionWith(GetSubtreeHashes(parseStack));
+        } else {
+          var peek = parseStack.Peek();
+          uniqueChildHashes.Add(AggregateHashes(peek, GetSubtreeHashes(parseStack)));
+        }
+        // Second subtree
+        if (parseStack.Peek() == Grammar.Addition) {
+          uniqueChildHashes.UnionWith(GetSubtreeHashes(parseStack));
+        } else {
+          var peek = parseStack.Peek();
+          uniqueChildHashes.Add(AggregateHashes(peek, GetSubtreeHashes(parseStack)));
+        }
+
+        var result = uniqueChildHashes.ToArray();
+        Array.Sort(result);
+        return result;
+      }
+
+      // MULTIPLICATION
+      if (currentSymbol == Grammar.Multiplication) {
+        var childHashes = new List<THashType>();
+
+        // First subtree
+        if (parseStack.Peek() == Grammar.Multiplication) {
+          childHashes.AddRange(GetSubtreeHashes(parseStack));
+        } else {
+          childHashes.Add(AggregateHashes(parseStack.Peek(), GetSubtreeHashes(parseStack)));
+        }
+        // Second subtree
+        if (parseStack.Peek() == Grammar.Multiplication) {
+          childHashes.AddRange(GetSubtreeHashes(parseStack));
+        } else {
+          childHashes.Add(AggregateHashes(parseStack.Peek(), GetSubtreeHashes(parseStack)));
+        }
+
+        // Sort due to commutativity
+        childHashes.Sort();
+
+        // Cancel out inverse factors.
+        bool[] isFactorRemaining = Enumerable.Repeat(true, childHashes.Count).ToArray();
+
+        for (int i = 0; i < isFactorRemaining.Length; i++) {
+          if (!isFactorRemaining[i]) continue;
+          if (isFactorRemaining.Length <= 2) break; // Until we have constants, we can't cancel out all terms.
+
+          var currFactor = childHashes[i];
+          var invFactor = AggregateHashes(Grammar.Inv, new[] { currFactor });
+
+          int indexOfInv = childHashes.IndexOf(invFactor);
+          if (indexOfInv >= 0 && isFactorRemaining[indexOfInv]) {
+            isFactorRemaining[i] = isFactorRemaining[indexOfInv] = false;
+          }
+        }
+
+        return childHashes
+          .Where((c, i) => isFactorRemaining[i])
+          .ToArray();
+      }
+
+      // LOG, EXP, SIN, INV
+      if (currentSymbol == Grammar.Log || currentSymbol == Grammar.Exp ||
+          currentSymbol == Grammar.Sin || currentSymbol == Grammar.Cos ||
+          currentSymbol == Grammar.Inv) {
+        return new[] { AggregateHashes(parseStack.Peek(), GetSubtreeHashes(parseStack)) };
+      }
+
+      // var or nonterminal symbol
+      return new[] { AggregateHashes(currentSymbol, new THashType[0]) };
+    }
+  }
+
+  public class IntHasher : Hasher<int> {
+    public IntHasher(Grammar grammar) : base(grammar) { }
+
+    protected override int AggregateHashes(Symbol operatorSym, int[] hashes) {
+      int start;
+      if ((operatorSym == Grammar.Addition || operatorSym == Grammar.Multiplication) && hashes.Length <= 1) {
+        start = 0;
+
+      } else if (operatorSym is NonterminalSymbol || operatorSym is VariableTerminalSymbol) {
+        return operatorSym.StringRepresentation.GetHashCode();
+
+      } else {
+        start = operatorSym.StringRepresentation.GetHashCode();
+      }
+
+      for (int i = 0; i < hashes.Length; i++) {
+        start = ((start << 5) + start) ^ hashes[i];
+      }
+      return start;
+    }
+  }
+
+  public class StringHasher : Hasher<string> {
+    public StringHasher(Grammar grammar) : base(grammar) { }
+
+    protected override string AggregateHashes(Symbol operatorSym, string[] hashes) {
+      var hashesArray = hashes.ToArray();
+
+      if ((operatorSym == Grammar.Addition || operatorSym == Grammar.Multiplication) && hashesArray.Length <= 1) {
+        return hashesArray[0];
+      }
+      if (operatorSym is NonterminalSymbol || operatorSym is VariableTerminalSymbol) {
+        return operatorSym.StringRepresentation;
+      }
+
+      return $"[{operatorSym.StringRepresentation} ° {string.Join(" ° ", hashesArray)}]";
+    }
+  }
+}
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 15831)
+++ /branches/2886_SymRegGrammarEnumeration/HeuristicLab.Algorithms.DataAnalysis.SymRegGrammarEnumeration/HeuristicLab.Algorithms.DataAnalysis.SymRegGrammarEnumeration.csproj	(revision 15832)
@@ -116,4 +116,5 @@
     <Compile Include="GrammarEnumeration\Grammar.cs" />
     <Compile Include="GrammarEnumeration\GrammarEnumerationAlgorithm.cs" />
+    <Compile Include="Hashing\Hasher.cs" />
     <Compile Include="GrammarEnumeration\SearchDataStructure.cs" />
     <Compile Include="GrammarEnumeration\Sentence.cs" />
Index: /branches/2886_SymRegGrammarEnumeration/Test/GrammarEnumerationTest.cs
===================================================================
--- /branches/2886_SymRegGrammarEnumeration/Test/GrammarEnumerationTest.cs	(revision 15831)
+++ /branches/2886_SymRegGrammarEnumeration/Test/GrammarEnumerationTest.cs	(revision 15832)
@@ -72,6 +72,6 @@
       });
 
-      int targetSolutionHash = alg.Grammar.CalcHashCode(targetSolution);
-      int actualSolutionHash = alg.Grammar.CalcHashCode(alg.BestTrainingSentence);
+      int targetSolutionHash = alg.Grammar.Hasher.CalcHashCode(targetSolution);
+      int actualSolutionHash = alg.Grammar.Hasher.CalcHashCode(alg.BestTrainingSentence);
 
       Assert.IsTrue(alg.DistinctSentencesLength.ContainsKey(targetSolutionHash), "Actual solution was not generated!");
@@ -127,6 +127,6 @@
       });
 
-      int targetSolutionHash = alg.Grammar.CalcHashCode(targetSolution);
-      int actualSolutionHash = alg.Grammar.CalcHashCode(alg.BestTrainingSentence);
+      int targetSolutionHash = alg.Grammar.Hasher.CalcHashCode(targetSolution);
+      int actualSolutionHash = alg.Grammar.Hasher.CalcHashCode(alg.BestTrainingSentence);
 
       Assert.IsTrue(alg.DistinctSentencesLength.ContainsKey(targetSolutionHash), "Actual solution was not generated!");
@@ -156,6 +156,6 @@
       });
 
-      int targetSolutionHash = alg.Grammar.CalcHashCode(targetSolution);
-      int actualSolutionHash = alg.Grammar.CalcHashCode(alg.BestTrainingSentence);
+      int targetSolutionHash = alg.Grammar.Hasher.CalcHashCode(targetSolution);
+      int actualSolutionHash = alg.Grammar.Hasher.CalcHashCode(alg.BestTrainingSentence);
 
       Assert.IsTrue(alg.DistinctSentencesLength.ContainsKey(targetSolutionHash), "Actual solution was not generated!");
Index: /branches/2886_SymRegGrammarEnumeration/Test/TreeHashingTest.cs
===================================================================
--- /branches/2886_SymRegGrammarEnumeration/Test/TreeHashingTest.cs	(revision 15831)
+++ /branches/2886_SymRegGrammarEnumeration/Test/TreeHashingTest.cs	(revision 15832)
@@ -28,6 +28,6 @@
       SymbolString s2 = new SymbolString(new[] { varA, varB, grammar.Addition, varC, grammar.Addition });
 
-      int hash1 = grammar.CalcHashCode(s1);
-      int hash2 = grammar.CalcHashCode(s2);
+      int hash1 = grammar.Hasher.CalcHashCode(s1);
+      int hash2 = grammar.Hasher.CalcHashCode(s2);
 
       Assert.AreEqual(hash1, hash2);
@@ -40,6 +40,6 @@
       SymbolString s2 = new SymbolString(new[] { varB, varB, grammar.Addition, varB, grammar.Addition });
 
-      int hash1 = grammar.CalcHashCode(s1);
-      int hash2 = grammar.CalcHashCode(s2);
+      int hash1 = grammar.Hasher.CalcHashCode(s1);
+      int hash2 = grammar.Hasher.CalcHashCode(s2);
 
       Assert.AreNotEqual(hash1, hash2);
@@ -52,6 +52,6 @@
       SymbolString s2 = new SymbolString(new[] { varB, varA, grammar.Addition });
 
-      int hash1 = grammar.CalcHashCode(s1);
-      int hash2 = grammar.CalcHashCode(s2);
+      int hash1 = grammar.Hasher.CalcHashCode(s1);
+      int hash2 = grammar.Hasher.CalcHashCode(s2);
 
       Assert.AreEqual(hash1, hash2);
@@ -64,6 +64,6 @@
       SymbolString s2 = new SymbolString(new[] { varA, varB, varA, grammar.Addition, grammar.Addition });
 
-      int hash1 = grammar.CalcHashCode(s1);
-      int hash2 = grammar.CalcHashCode(s2);
+      int hash1 = grammar.Hasher.CalcHashCode(s1);
+      int hash2 = grammar.Hasher.CalcHashCode(s2);
 
       Assert.AreEqual(hash1, hash2);
@@ -76,6 +76,6 @@
       SymbolString s2 = new SymbolString(new[] { varA });
 
-      int hash1 = grammar.CalcHashCode(s1);
-      int hash2 = grammar.CalcHashCode(s2);
+      int hash1 = grammar.Hasher.CalcHashCode(s1);
+      int hash2 = grammar.Hasher.CalcHashCode(s2);
 
       Assert.AreEqual(hash1, hash2);
@@ -88,6 +88,6 @@
       SymbolString s2 = new SymbolString(new[] { varA, varA, varA, grammar.Multiplication, grammar.Addition });
 
-      int hash1 = grammar.CalcHashCode(s1);
-      int hash2 = grammar.CalcHashCode(s2);
+      int hash1 = grammar.Hasher.CalcHashCode(s1);
+      int hash2 = grammar.Hasher.CalcHashCode(s2);
 
       Assert.AreNotEqual(hash1, hash2);
@@ -100,6 +100,6 @@
       SymbolString s2 = new SymbolString(new Symbol[] { varA, grammar.Expr, grammar.Addition });
 
-      int hash1 = grammar.CalcHashCode(s1);
-      int hash2 = grammar.CalcHashCode(s2);
+      int hash1 = grammar.Hasher.CalcHashCode(s1);
+      int hash2 = grammar.Hasher.CalcHashCode(s2);
 
       Assert.AreEqual(hash1, hash2);
@@ -114,6 +114,6 @@
       SymbolString s2 = new SymbolString(new Symbol[] { varA, varB, grammar.Multiplication });
 
-      int hash1 = grammar.CalcHashCode(s1);
-      int hash2 = grammar.CalcHashCode(s2);
+      int hash1 = grammar.Hasher.CalcHashCode(s1);
+      int hash2 = grammar.Hasher.CalcHashCode(s2);
 
       Assert.AreEqual(hash1, hash2);
@@ -126,6 +126,6 @@
       SymbolString s2 = new SymbolString(new Symbol[] { varA, varA, varA, grammar.Multiplication, grammar.Addition, grammar.Sin, varA, grammar.Inv, varA, grammar.Sin, varA, grammar.Multiplication, grammar.Multiplication, grammar.Addition });
 
-      int hash1 = grammar.CalcHashCode(s1);
-      int hash2 = grammar.CalcHashCode(s2);
+      int hash1 = grammar.Hasher.CalcHashCode(s1);
+      int hash2 = grammar.Hasher.CalcHashCode(s2);
 
       Assert.AreEqual(hash1, hash2);
