Index: /trunk/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Hashing/HashExtensions.cs
===================================================================
--- /trunk/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Hashing/HashExtensions.cs	(revision 16218)
+++ /trunk/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Hashing/HashExtensions.cs	(revision 16218)
@@ -0,0 +1,243 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2018 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Collections.Generic;
+
+namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
+  public static class SymbolicExpressionHashExtensions {
+    public sealed class HashNode<T> : IComparable<HashNode<T>>, IEquatable<HashNode<T>> where T : class {
+      public T Data;
+      public int Arity;
+      public int Size;
+      public bool IsCommutative;
+
+      public bool Enabled;
+      public int HashValue;           // the initial (fixed) hash value for this individual node/data
+      public int CalculatedHashValue; // the calculated hash value (taking into account the children hash values)
+
+      public Action<HashNode<T>[], int> Simplify;
+      public IComparer<T> Comparer;
+
+      public bool IsChild => Arity == 0;
+
+      public HashNode(IComparer<T> comparer) {
+        Comparer = comparer;
+      }
+
+      private HashNode() { }
+
+      public int CompareTo(HashNode<T> other) {
+        var res = Comparer.Compare(Data, other.Data);
+        return res == 0 ? CalculatedHashValue.CompareTo(other.CalculatedHashValue) : res;
+      }
+
+      public override string ToString() {
+        return $"{Data} {Arity} {Size} {CalculatedHashValue} {Enabled}";
+      }
+
+      public bool Equals(HashNode<T> other) {
+        return CalculatedHashValue.Equals(other.CalculatedHashValue);
+      }
+
+      public override bool Equals(object obj) {
+        var other = obj as HashNode<T>;
+        if (other != null)
+          return Equals(other);
+        return base.Equals(obj);
+      }
+
+      public override int GetHashCode() {
+        return CalculatedHashValue;
+      }
+
+      public static bool operator ==(HashNode<T> a, HashNode<T> b) {
+        return a.Equals(b);
+      }
+
+      public static bool operator !=(HashNode<T> a, HashNode<T> b) {
+        return !a.Equals(b);
+      }
+    }
+
+    public static int ComputeHash<T>(this HashNode<T>[] nodes, int i) where T : class {
+      var node = nodes[i];
+      var hashes = new int[node.Arity + 1];
+      int idx = 0;
+      foreach (int c in nodes.IterateChildren(i)) {
+        hashes[idx++] = nodes[c].CalculatedHashValue;
+      }
+      hashes[idx] = node.HashValue;
+      return HashUtil.JSHash(hashes);
+    }
+
+    public static HashNode<T>[] Simplify<T>(this HashNode<T>[] nodes) where T : class {
+      var reduced = nodes.Reduce();
+      reduced.Sort();
+
+      for (int i = 0; i < reduced.Length; ++i) {
+        var node = reduced[i];
+        if (node.IsChild) {
+          continue;
+        }
+        node.Simplify?.Invoke(reduced, i);
+      }
+      // detect if anything was simplified
+      var count = 0;
+      foreach (var node in reduced) {
+        if (!node.Enabled) { ++count; }
+      }
+      if (count == 0) {
+        return reduced;
+      }
+
+      var simplified = new HashNode<T>[reduced.Length - count];
+      int j = 0;
+      foreach (var node in reduced) {
+        if (node.Enabled) {
+          simplified[j++] = node;
+        }
+      }
+      simplified.UpdateNodeSizes();
+      simplified.Sort();
+      return simplified;
+    }
+
+    public static void Sort<T>(this HashNode<T>[] nodes) where T : class {
+      for (int i = 0; i < nodes.Length; ++i) {
+        var node = nodes[i];
+
+        if (node.IsChild) {
+          continue;
+        }
+
+        if (node.IsCommutative) { // only sort when the argument order does not matter
+          var arity = node.Arity;
+          var size = node.Size;
+
+          if (arity == size) { // all child nodes are terminals
+            Array.Sort(nodes, i - size, size);
+          } else { // i have some non-terminal children
+            var sorted = new HashNode<T>[size];
+            var indices = nodes.IterateChildren(i);
+            Array.Sort(indices, (a, b) => nodes[a].CompareTo(nodes[b]));
+
+            int idx = 0;
+            foreach (var j in indices) {
+              var child = nodes[j];
+              if (!child.IsChild) { // must copy complete subtree
+                Array.Copy(nodes, j - child.Size, sorted, idx, child.Size);
+                idx += child.Size;
+              }
+              sorted[idx++] = nodes[j];
+            }
+            Array.Copy(sorted, 0, nodes, i - size, size);
+          }
+        }
+        node.CalculatedHashValue = nodes.ComputeHash(i);
+      }
+    }
+
+    /// <summary>
+    /// Get a function node's child indices from left to right
+    /// </summary>
+    /// <typeparam name="T"></typeparam>
+    /// <param name="nodes"></param>
+    /// <param name="i"></param>
+    /// <returns>An array containing child indices</returns>
+    public static int[] IterateChildren<T>(this HashNode<T>[] nodes, int i) where T : class {
+      var node = nodes[i];
+      var arity = node.Arity;
+      var children = new int[arity];
+      var idx = i - 1;
+      for (int j = 0; j < arity; ++j) {
+        while (!nodes[idx].Enabled) { --idx; } // skip nodes possibly disabled by simplification
+        children[j] = idx;
+        idx -= 1 + nodes[idx].Size;
+      }
+      return children;
+    }
+
+    public static void UpdateNodeSizes<T>(this HashNode<T>[] nodes) where T : class {
+      for (int i = 0; i < nodes.Length; ++i) {
+        var node = nodes[i];
+        if (node.IsChild) {
+          node.Size = 0;
+          continue;
+        }
+        node.Size = node.Arity;
+        foreach (int j in nodes.IterateChildren(i)) {
+          node.Size += nodes[j].Size;
+        }
+      }
+    }
+
+    /// <summary>
+    /// Reduce nodes of the same type according to arithmetic rules
+    /// </summary>
+    /// <typeparam name="T"></typeparam>
+    /// <param name="g">The given grammar (controls symbol arities)</param>
+    /// <param name="nodes">The node array</param>
+    /// <param name="i">Parent index</param>
+    /// <param name="j">Child index</param>
+    private static void Reduce<T>(this HashNode<T>[] nodes, int i, int j) where T : class {
+      var p = nodes[i]; // parent node
+      var c = nodes[j]; // child node
+
+      if (c.IsChild)
+        return;
+
+      foreach (int k in nodes.IterateChildren(j)) {
+        if (nodes[k].IsChild) continue;
+        nodes.Reduce(j, k);
+      }
+
+      // handle commutative symbols (add, mul) 
+      if (p.IsCommutative && p.HashValue == c.HashValue) {
+        c.Enabled = false;
+        p.Arity += c.Arity - 1;
+      }
+    }
+
+    public static HashNode<T>[] Reduce<T>(this HashNode<T>[] nodes) where T : class {
+      nodes.UpdateNodeSizes();
+      int i = nodes.Length - 1;
+      foreach (int c in nodes.IterateChildren(i)) {
+        if (nodes[c].IsChild) continue;
+        nodes.Reduce(i, c);
+      }
+      int count = 0;
+      foreach (var node in nodes) {
+        if (!node.Enabled) { ++count; }
+      }
+      if (count == 0)
+        return nodes;
+
+      var reduced = new HashNode<T>[nodes.Length - count];
+      i = 0;
+      foreach (var node in nodes) {
+        if (node.Enabled) { reduced[i++] = node; }
+      }
+      reduced.UpdateNodeSizes();
+      return reduced;
+    }
+  }
+}
Index: /trunk/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Hashing/HashUtil.cs
===================================================================
--- /trunk/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Hashing/HashUtil.cs	(revision 16218)
+++ /trunk/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Hashing/HashUtil.cs	(revision 16218)
@@ -0,0 +1,100 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2018 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Security.Cryptography;
+
+namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
+  public static class HashUtil {
+    // This class contains some hash functions adapted from http://partow.net/programming/hashfunctions/index.html#AvailableHashFunctions
+
+    // A simple hash function from Robert Sedgwicks Algorithms in C book.I've added some simple optimizations to the algorithm in order to speed up its hashing process. 
+    public static int RSHash(int[] input) {
+      const int b = 378551;
+      int a = 63689;
+      int hash = 0;
+
+      foreach (var v in input) {
+        hash = (hash * a) + v;
+        a *= b;
+      }
+      return hash;
+    }
+
+    // A bitwise hash function written by Justin Sobel
+    public static int JSHash(int[] input) {
+      int hash = 1315423911;
+      for (int i = 0; i < input.Length; ++i)
+        hash ^= (hash << 5) + input[i] + (hash >> 2);
+      return hash;
+    }
+
+    // This hash function comes from Brian Kernighan and Dennis Ritchie's book "The C Programming Language". It is a simple hash function using a strange set of possible seeds which all constitute a pattern of 31....31...31 etc, it seems to be very similar to the DJB hash function. 
+    public static int BKDRHash(int[] input) {
+      const int seed = 131;
+      int hash = 0;
+      foreach (var v in input) {
+        hash = (hash * seed) + v;
+      }
+      return hash;
+    }
+
+    // This is the algorithm of choice which is used in the open source SDBM project. The hash function seems to have a good over-all distribution for many different data sets. It seems to work well in situations where there is a high variance in the MSBs of the elements in a data set. 
+    public static int SDBMHash(int[] input) {
+      int hash = 0;
+      foreach (var v in input) {
+        hash = v + (hash << 6) + (hash << 16) - hash;
+      }
+      return hash;
+    }
+
+    // An algorithm produced by Professor Daniel J. Bernstein and shown first to the world on the usenet newsgroup comp.lang.c. It is one of the most efficient hash functions ever published. 
+    public static int DJBHash(int[] input) {
+      int hash = 5381;
+      foreach (var v in input) {
+        hash = (hash << 5) + hash + v;
+      }
+      return hash;
+    }
+
+    // An algorithm proposed by Donald E.Knuth in The Art Of Computer Programming Volume 3, under the topic of sorting and search chapter 6.4. 
+    public static int DEKHash(int[] input) {
+      int hash = input.Length;
+      foreach (var v in input) {
+        hash = (hash << 5) ^ (hash >> 27) ^ v;
+      }
+      return hash;
+    }
+
+    public static int CryptoHash(HashAlgorithm ha, int[] input) {
+      return BitConverter.ToInt32(ha.ComputeHash(input.ToByteArray()), 0);
+    }
+
+    public static byte[] ToByteArray(this int[] input) {
+      const int size = sizeof(int);
+      var bytes = new byte[input.Length * sizeof(int)];
+      for (int i = 0; i < input.Length; ++i) {
+        Array.Copy(BitConverter.GetBytes(bytes[i]), 0, bytes, i * size, size);
+      }
+      return bytes;
+    }
+  }
+}
Index: /trunk/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Hashing/SymbolicExpressionTreeHash.cs
===================================================================
--- /trunk/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Hashing/SymbolicExpressionTreeHash.cs	(revision 16218)
+++ /trunk/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Hashing/SymbolicExpressionTreeHash.cs	(revision 16218)
@@ -0,0 +1,270 @@
+﻿using System.Collections.Generic;
+using System.Linq;
+using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
+using static HeuristicLab.Problems.DataAnalysis.Symbolic.SymbolicExpressionHashExtensions;
+
+namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
+  public static class SymbolicExpressionTreeHash {
+    private static readonly Addition add = new Addition();
+    private static readonly Subtraction sub = new Subtraction();
+    private static readonly Multiplication mul = new Multiplication();
+    private static readonly Division div = new Division();
+    private static readonly Logarithm log = new Logarithm();
+    private static readonly Exponential exp = new Exponential();
+    private static readonly Sine sin = new Sine();
+    private static readonly Cosine cos = new Cosine();
+    private static readonly Constant constant = new Constant();
+
+    private static readonly ISymbolicExpressionTreeNodeComparer comparer = new SymbolicExpressionTreeNodeComparer();
+
+    public static int ComputeHash(this ISymbolicExpressionTree tree) {
+      return ComputeHash(tree.Root.GetSubtree(0).GetSubtree(0));
+    }
+
+    public static Dictionary<ISymbolicExpressionTreeNode, int> ComputeNodeHashes(this ISymbolicExpressionTree tree) {
+      var root = tree.Root.GetSubtree(0).GetSubtree(0);
+      var nodes = root.MakeNodes();
+      nodes.UpdateNodeSizes();
+
+      for (int i = 0; i < nodes.Length; ++i) {
+        if (nodes[i].IsChild)
+          continue;
+        nodes[i].CalculatedHashValue = nodes.ComputeHash(i);
+      }
+      return nodes.ToDictionary(x => x.Data, x => x.CalculatedHashValue);
+    }
+
+    public static int ComputeHash(this ISymbolicExpressionTreeNode treeNode) {
+      var hashNodes = treeNode.MakeNodes();
+      var simplified = hashNodes.Simplify();
+      return ComputeHash(simplified);
+    }
+
+    public static int ComputeHash(this HashNode<ISymbolicExpressionTreeNode>[] nodes) {
+      int hash = 1315423911;
+      foreach (var node in nodes)
+        hash ^= (hash << 5) + node.CalculatedHashValue + (hash >> 2);
+      return hash;
+    }
+
+    public static HashNode<ISymbolicExpressionTreeNode> ToHashNode(this ISymbolicExpressionTreeNode node) {
+      var symbol = node.Symbol;
+      var name = symbol.Name;
+      if (symbol is Variable) {
+        var variableTreeNode = (VariableTreeNode)node;
+        name = variableTreeNode.VariableName;
+      }
+      var hash = name.GetHashCode();
+      var hashNode = new HashNode<ISymbolicExpressionTreeNode>(comparer) {
+        Data = node,
+        Arity = node.SubtreeCount,
+        Size = node.SubtreeCount,
+        IsCommutative = node.Symbol is Addition || node.Symbol is Multiplication,
+        Enabled = true,
+        HashValue = hash,
+        CalculatedHashValue = hash
+      };
+      if (symbol is Addition) {
+        hashNode.Simplify = SimplifyAddition;
+      } else if (symbol is Multiplication) {
+        hashNode.Simplify = SimplifyMultiplication;
+      } else if (symbol is Division) {
+        hashNode.Simplify = SimplifyDivision;
+      } else if (symbol is Logarithm || symbol is Exponential || symbol is Sine || symbol is Cosine) {
+        hashNode.Simplify = SimplifyUnaryNode;
+      } else if (symbol is Subtraction) {
+        hashNode.Simplify = SimplifyBinaryNode;
+      }
+      return hashNode;
+    }
+
+    public static HashNode<ISymbolicExpressionTreeNode>[] MakeNodes(this ISymbolicExpressionTreeNode node) {
+      return node.IterateNodesPostfix().Select(ToHashNode).ToArray();
+    }
+
+    #region parse a nodes array back into a tree
+    public static ISymbolicExpressionTree ToTree(this HashNode<ISymbolicExpressionTreeNode>[] nodes) {
+      var root = new ProgramRootSymbol().CreateTreeNode();
+      var start = new StartSymbol().CreateTreeNode();
+      root.AddSubtree(start);
+      start.AddSubtree(nodes.ToSubtree());
+      return new SymbolicExpressionTree(root);
+    }
+
+    public static ISymbolicExpressionTreeNode ToSubtree(this HashNode<ISymbolicExpressionTreeNode>[] nodes) {
+      var treeNodes = nodes.Select(x => x.Data.Symbol.CreateTreeNode()).ToArray();
+
+      for (int i = nodes.Length - 1; i >= 0; --i) {
+        var node = nodes[i];
+
+        if (node.IsChild) {
+          if (node.Data is VariableTreeNode variable) {
+            var variableTreeNode = (VariableTreeNode)treeNodes[i];
+            variableTreeNode.VariableName = variable.VariableName;
+            variableTreeNode.Weight = 1;
+          } else if (node.Data is ConstantTreeNode @const) {
+            var constantTreeNode = (ConstantTreeNode)treeNodes[i];
+            constantTreeNode.Value = @const.Value;
+          }
+          continue;
+        }
+
+        var treeNode = treeNodes[i];
+
+        foreach (var j in nodes.IterateChildren(i)) {
+          treeNode.AddSubtree(treeNodes[j]);
+        }
+      }
+
+      return treeNodes.Last();
+    }
+
+    private static T CreateTreeNode<T>(this ISymbol symbol) where T : class, ISymbolicExpressionTreeNode {
+      return (T)symbol.CreateTreeNode();
+    }
+    #endregion
+
+    #region tree simplification
+    // these simplification methods rely on the assumption that child nodes of the current node have already been simplified
+    // (in other words simplification should be applied in a bottom-up fashion) 
+    public static ISymbolicExpressionTree Simplify(ISymbolicExpressionTree tree) {
+      var root = tree.Root.GetSubtree(0).GetSubtree(0);
+      var nodes = root.MakeNodes();
+      var simplified = nodes.Simplify();
+      return simplified.ToTree();
+    }
+
+    public static void SimplifyAddition(HashNode<ISymbolicExpressionTreeNode>[] nodes, int i) {
+      // simplify additions of terms by eliminating terms with the same symbol and hash
+      var children = nodes.IterateChildren(i);
+
+      var curr = children[0];
+      var node = nodes[i];
+
+      foreach (var j in children.Skip(1)) {
+        if (nodes[j] == nodes[curr]) {
+          for (int k = j - nodes[j].Size; k <= j; ++k) {
+            nodes[k].Enabled = false;
+          }
+          node.Arity--;
+        } else {
+          curr = j;
+        }
+      }
+      if (node.Arity == 1) { // if the arity is 1 we don't need the addition node at all
+        node.Enabled = false;
+      }
+    }
+
+    // simplify multiplications by reducing constants and div terms  
+    public static void SimplifyMultiplication(HashNode<ISymbolicExpressionTreeNode>[] nodes, int i) {
+      var node = nodes[i];
+      var children = nodes.IterateChildren(i);
+
+      for (int j = 0; j < children.Length; ++j) {
+        var c = children[j];
+        var child = nodes[c];
+
+        if (!child.Enabled)
+          continue;
+
+        var symbol = child.Data.Symbol;
+        if (symbol is Constant) {
+          for (int k = j + 1; k < children.Length; ++k) {
+            var d = children[k];
+            if (nodes[d].Data.Symbol is Constant) {
+              ((ConstantTreeNode)child.Data).Value *= ((ConstantTreeNode)nodes[d].Data).Value;
+              nodes[d].Enabled = false;
+              node.Arity--;
+            } else {
+              break;
+            }
+          }
+        } else if (symbol is Division) {
+          var div = nodes[c];
+          var denominator =
+            div.Arity == 1 ?
+            nodes[c - 1] :                    // 1 / x is expressed as div(x) (with a single child)
+            nodes[c - nodes[c - 1].Size - 2]; // assume division always has arity 1 or 2
+
+          foreach (var d in children) {
+            if (nodes[d].Enabled && nodes[d] == denominator) {
+              nodes[c].Enabled = nodes[d].Enabled = denominator.Enabled = false;
+              node.Arity -= 2; // matching child + division node
+              break;
+            }
+          }
+        }
+
+        if (node.Arity == 0) { // if everything is simplified this node becomes constant
+          var constantTreeNode = constant.CreateTreeNode<ConstantTreeNode>();
+          constantTreeNode.Value = 1;
+          nodes[i] = constantTreeNode.ToHashNode();
+        } else if (node.Arity == 1) { // when i have only 1 arg left i can skip this node
+          node.Enabled = false;
+        }
+      }
+    }
+
+    public static void SimplifyDivision(HashNode<ISymbolicExpressionTreeNode>[] nodes, int i) {
+      var node = nodes[i];
+      var children = nodes.IterateChildren(i);
+
+      if (children.All(x => nodes[x].Data.Symbol is Constant)) {
+        var v = ((ConstantTreeNode)nodes[children.First()].Data).Value;
+        if (node.Arity == 1) {
+          v = 1 / v;
+        } else if (node.Arity > 1) {
+          foreach (var j in children.Skip(1)) {
+            v /= ((ConstantTreeNode)nodes[j].Data).Value;
+          }
+        }
+        var constantTreeNode = constant.CreateTreeNode<ConstantTreeNode>();
+        constantTreeNode.Value = v;
+        nodes[i] = constantTreeNode.ToHashNode();
+        return;
+      }
+
+      var nominator = nodes[children[0]];
+      foreach (var j in children.Skip(1)) {
+        var denominator = nodes[j];
+        if (nominator == denominator) {
+          // disable all the children of the division node (nominator and children + denominator and children)
+          nominator.Enabled = denominator.Enabled = false;
+          node.Arity -= 2; // nominator + denominator
+        }
+        if (node.Arity == 0) {
+          var constantTreeNode = constant.CreateTreeNode<ConstantTreeNode>();
+          constantTreeNode.Value = 1; // x / x = 1
+          nodes[i] = constantTreeNode.ToHashNode();
+        }
+      }
+    }
+
+    public static void SimplifyUnaryNode(HashNode<ISymbolicExpressionTreeNode>[] nodes, int i) {
+      // check if the child of the unary node is a constant, then the whole node can be simplified
+      var parent = nodes[i];
+      var child = nodes[i - 1];
+
+      var parentSymbol = parent.Data.Symbol;
+      var childSymbol = child.Data.Symbol;
+
+      if (childSymbol is Constant) {
+        nodes[i].Enabled = false;
+      } else if ((parentSymbol is Exponential && childSymbol is Logarithm) || (parentSymbol is Logarithm && childSymbol is Exponential)) {
+        child.Enabled = parent.Enabled = false;
+      }
+    }
+
+    public static void SimplifyBinaryNode(HashNode<ISymbolicExpressionTreeNode>[] nodes, int i) {
+      var children = nodes.IterateChildren(i);
+      if (children.All(x => nodes[x].Data.Symbol is Constant)) {
+        foreach (var j in children) {
+          nodes[j].Enabled = false;
+        }
+        nodes[i] = constant.CreateTreeNode().ToHashNode();
+      }
+    }
+    #endregion
+  }
+}
Index: /trunk/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/HeuristicLab.Problems.DataAnalysis.Symbolic-3.4.csproj
===================================================================
--- /trunk/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/HeuristicLab.Problems.DataAnalysis.Symbolic-3.4.csproj	(revision 16217)
+++ /trunk/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/HeuristicLab.Problems.DataAnalysis.Symbolic-3.4.csproj	(revision 16218)
@@ -145,4 +145,7 @@
     <Compile Include="Formatters\SymbolicDataAnalysisExpressionMathematicaFormatter.cs" />
     <Compile Include="Formatters\SymbolicDataAnalysisExpressionCSharpFormatter.cs" />
+    <Compile Include="Hashing\HashExtensions.cs" />
+    <Compile Include="Hashing\HashUtil.cs" />
+    <Compile Include="Hashing\SymbolicExpressionTreeHash.cs" />
     <Compile Include="Importer\InfixExpressionParser.cs" />
     <Compile Include="Importer\SymbolicExpressionImporter.cs" />
