Index: /branches/MCTS-SymbReg-2796/HeuristicLab.Algorithms.DataAnalysis/3.4/HeuristicLab.Algorithms.DataAnalysis.MCTSSymbReg.csproj
===================================================================
--- /branches/MCTS-SymbReg-2796/HeuristicLab.Algorithms.DataAnalysis/3.4/HeuristicLab.Algorithms.DataAnalysis.MCTSSymbReg.csproj	(revision 15439)
+++ /branches/MCTS-SymbReg-2796/HeuristicLab.Algorithms.DataAnalysis/3.4/HeuristicLab.Algorithms.DataAnalysis.MCTSSymbReg.csproj	(revision 15440)
@@ -104,8 +104,8 @@
     <Compile Include="MctsSymbolicRegression\Disassembler.cs" />
     <Compile Include="MctsSymbolicRegression\ExpressionEvaluator.cs" />
+    <Compile Include="MctsSymbolicRegression\ExprHashSymbolic.cs" />
     <Compile Include="MctsSymbolicRegression\MctsSymbolicRegressionAlgorithm.cs" />
     <Compile Include="MctsSymbolicRegression\MctsSymbolicRegressionStatic.cs" />
     <Compile Include="MctsSymbolicRegression\OpCodes.cs" />
-    <Compile Include="MctsSymbolicRegression\ExprHash.cs" />
     <Compile Include="MctsSymbolicRegression\SymbolicExpressionGenerator.cs" />
     <Compile Include="MctsSymbolicRegression\Tree.cs" />
Index: /branches/MCTS-SymbReg-2796/HeuristicLab.Algorithms.DataAnalysis/3.4/MctsSymbolicRegression/Automaton.cs
===================================================================
--- /branches/MCTS-SymbReg-2796/HeuristicLab.Algorithms.DataAnalysis/3.4/MctsSymbolicRegression/Automaton.cs	(revision 15439)
+++ /branches/MCTS-SymbReg-2796/HeuristicLab.Algorithms.DataAnalysis/3.4/MctsSymbolicRegression/Automaton.cs	(revision 15440)
@@ -424,5 +424,8 @@
         v == StateLogTFEnd ||
         v == StateInvTFEnd ||
-        v == StateExpFEnd
+        v == StateExpFEnd ||
+        v == StateLogTEnd ||
+        v == StateInvTEnd ||
+        v == StateTermEnd 
         ;
     }
Index: anches/MCTS-SymbReg-2796/HeuristicLab.Algorithms.DataAnalysis/3.4/MctsSymbolicRegression/ExprHash.cs
===================================================================
--- /branches/MCTS-SymbReg-2796/HeuristicLab.Algorithms.DataAnalysis/3.4/MctsSymbolicRegression/ExprHash.cs	(revision 15439)
+++ 	(revision )
@@ -1,167 +1,0 @@
-﻿#region License Information
-/* HeuristicLab
- * Copyright (C) 2002-2016 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;
-using System.Diagnostics.Contracts;
-using HeuristicLab.Random;
-using System.Linq;
-
-namespace HeuristicLab.Algorithms.DataAnalysis.MctsSymbolicRegression {
-
-  // calculates a hash-code for expressions.
-  // The hash codes of equivalent expressions x * (x + y) = (y + x)*x = (yx * xx) must be the same.
-  // We use a random number for each symbol in the expressions and use symmetric operations to produce
-  // results of sub-expressions.
-  // Surrogate functions for the functions log, exp, 1/x are necessary.
-  // Ideally, the surrogate functions also match identities of the original expressions.
-  // Simply using floating point operations and random floats for symbols would be easiest. 
-  // However, when we use floating point operations it is necessary to handle the inaccuracies 
-  // in the last bits (e.g. for division, log and exp)
-  // 
-  // Collisions are OK as long as they occur seldom. 
-  // It is probably be very unlikely to miss the optimal solution because of a collision. 
-  //
-  // We only need to identify equivalent structures. The numeric constants are irrelevant.
-  // Therefore, equivalent structures with different numeric constants map to the same hash code.
-
-  public static class ExprHash {
-    const int MaxStackSize = 100;
-    const int MaxVariables = 1000;
-    private static double[] varSymbValues;
-    static ExprHash() {
-      var rand = new MersenneTwister();
-      varSymbValues = new double[MaxVariables];
-      for (int i = 0; i < MaxVariables; i++) {
-        varSymbValues[i] = rand.NextDouble();
-      }
-    }
-
-    public static ulong GetHash(byte[] code, int nParams) {
-      var bits = (ulong)BitConverter.DoubleToInt64Bits(Eval(code, nParams));
-      // clear last five bits (insignificant?)
-      bits = bits & 0xFFFFFFFFFFFFFFE0;
-      return bits;
-    }
-
-    private static double Eval(byte[] code, int nParams) {
-      // The hash code calculation already preserves commutativity, associativity and distributivity of operations.
-      // However, we also need to hash c1*x1 + c2*x1 to the same value as c3*x1.
-      // Similarly for x1*x2 + x1*x2 or log(x1) + log(x1)!
-
-      // Calculate sums lazily. Keep all terms and only when the actual sum is necessary remove duplicate terms and calculate sum
-      // think about speed later (TODO)
-
-      var stack = new ISet<double>[MaxStackSize];
-      var terms = new HashSet<double>(new ApproximateDoubleEqualityComparer()); // the set of arguments for the current operator (+, *)
-      int topOfStack = -1;
-      int pc = 0;
-      int nextParamIdx = -1;
-      OpCodes op;
-      short arg;
-      while (true) {
-        ReadNext(code, ref pc, out op, out arg);
-        switch (op) {
-          case OpCodes.Nop: break;
-          case OpCodes.LoadConst0: {
-              ++topOfStack;
-              stack[topOfStack] = new HashSet<double>( new[] { 0.0 });
-
-              // terms.Add(0.0); // ignore numeric constants in expr-hash
-
-              break;
-            }
-          case OpCodes.LoadConst1: {
-              ++topOfStack;
-              stack[topOfStack] = new HashSet<double>(new[] { 1.0 });
-
-              // args.Add(1.0); ignore numeric constants in expr-hash
-
-              break;
-            }
-          case OpCodes.LoadParamN: {
-              ++topOfStack;
-              stack[topOfStack] =  new HashSet<double>(new[] { 1.0 });
-              break;
-            }
-          case OpCodes.LoadVar: {
-              ++topOfStack;
-              stack[topOfStack] = new HashSet<double>(new[] { varSymbValues[arg] });
-
-              // args.Add(varSymbValues[arg]);
-
-              break;
-            }
-          case OpCodes.Add: {
-              // take arguments from stack and put both terms into the set of terms
-              // for every other operation we need to evaluate the sum of terms first and put it onto the stack (lazy eval of sums)
-
-              stack[topOfStack - 1].UnionWith(stack[topOfStack]);
-              topOfStack--;
-
-              // stack[topOfStack] = t1 + t2; (later)
-              break;
-            }
-          case OpCodes.Mul: {
-              var t1 = stack[topOfStack - 1];
-              var t2 = stack[topOfStack];
-              topOfStack--;
-              stack[topOfStack] = new HashSet<double>(new double[] { t1.Sum() * t2.Sum() });
-              break;
-            }
-          case OpCodes.Log: {
-              var v1 = stack[topOfStack];
-              stack[topOfStack] = new HashSet<double>(new double[] { Math.Log( v1.Sum()) });
-              break;
-            }
-          case OpCodes.Exp: {
-              var v1 = stack[topOfStack];
-              stack[topOfStack] = new HashSet<double>(new double[] { Math.Exp(v1.Sum()) });
-              break;
-            }
-          case OpCodes.Inv: {
-              var v1 = stack[topOfStack];
-              stack[topOfStack] = new HashSet<double>(new double[] { 1.0 / v1.Sum() });
-              break;
-            }
-          case OpCodes.Exit:
-            Contract.Assert(topOfStack == 0);
-            return stack[topOfStack].Sum();
-          default: throw new InvalidOperationException();
-        }
-      }
-    }
-
-    private static void EvalTerms(HashSet<double> terms, double[] stack, ref int topOfStack) {
-      ++topOfStack;
-      stack[topOfStack] = terms.Sum();
-      terms.Clear();
-    }
-
-    private static void ReadNext(byte[] code, ref int pc, out OpCodes op, out short s) {
-      op = (OpCodes)Enum.ToObject(typeof(OpCodes), code[pc++]);
-      s = 0;
-      if (op == OpCodes.LoadVar) {
-        s = (short)((code[pc] << 8) | code[pc + 1]);
-        pc += 2;
-      }
-    }
-  }
-}
Index: /branches/MCTS-SymbReg-2796/HeuristicLab.Algorithms.DataAnalysis/3.4/MctsSymbolicRegression/ExprHashSymbolic.cs
===================================================================
--- /branches/MCTS-SymbReg-2796/HeuristicLab.Algorithms.DataAnalysis/3.4/MctsSymbolicRegression/ExprHashSymbolic.cs	(revision 15440)
+++ /branches/MCTS-SymbReg-2796/HeuristicLab.Algorithms.DataAnalysis/3.4/MctsSymbolicRegression/ExprHashSymbolic.cs	(revision 15440)
@@ -0,0 +1,270 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 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;
+using System.Diagnostics.Contracts;
+using System.Linq;
+
+namespace HeuristicLab.Algorithms.DataAnalysis.MctsSymbolicRegression {
+  internal enum UnaryFunctionType { Log, Exp, Inv };
+  internal interface Factor { }
+  internal class SymbolFactor : Factor {
+    internal char symbolId;
+    public SymbolFactor(char symbolId) {
+      this.symbolId = symbolId;
+    }
+    public override int GetHashCode() {
+      return symbolId.GetHashCode();
+    }
+    public override bool Equals(object obj) {
+      SymbolFactor other = obj as SymbolFactor;
+      if (other == null) return false;
+      else return other.symbolId == this.symbolId;
+    }
+  }
+    internal class FunctionFactor : Factor {
+    internal UnaryFunctionType functionType;
+    internal Polynomial argument;
+
+    public FunctionFactor(UnaryFunctionType functionType, Polynomial argument) {
+      this.functionType = functionType;
+      this.argument = argument;
+    }
+
+    public override int GetHashCode() {
+      var h = functionType.GetHashCode();
+      return ((h<<5)+h) ^ argument.GetHashCode();
+    }
+    public override bool Equals(object obj) {
+      FunctionFactor other = obj as FunctionFactor;
+      if (other == null) return false;
+      return
+        other.functionType == this.functionType &&
+        other.argument == this.argument;
+    }
+  }
+
+  internal class Monomial {
+    internal List<Factor> factors = new List<Factor>();
+    public Monomial(params Factor[] factor) {
+      foreach (var f in factor) factors.Add(f);
+    }
+    public override int GetHashCode() {
+      return factors.OrderBy(ti => ti.GetHashCode()).Aggregate(0, (a, ti) => ((a  << 5) + a) ^ ti.GetHashCode());
+    }
+    public override bool Equals(object obj) {
+      Monomial other = obj as Monomial;
+      if (other == null) return false;
+      if (other.factors.Count != this.factors.Count) return false;
+      return factors.All(ti => other.factors.Contains(ti)) &&
+        other.factors.All(ti => factors.Contains(ti));
+    }
+
+    public static Monomial Product(Monomial a, Monomial b) {
+      var p = new Monomial();
+      var invFactorArg = new Polynomial();
+      var expFactorArg = new Polynomial();
+      var expFactor = new FunctionFactor(UnaryFunctionType.Exp, expFactorArg);
+
+      foreach (var aFactor in a.factors.Concat(b.factors)) {
+        // collect all exp and inv factors into one and simplify
+        var funcFactor = aFactor as FunctionFactor;
+        if (funcFactor != null && funcFactor.functionType == UnaryFunctionType.Exp) {
+          expFactorArg.Add(funcFactor.argument);
+        } else if (funcFactor != null && funcFactor.functionType == UnaryFunctionType.Inv) {
+          if (!invFactorArg.terms.Any()) invFactorArg.terms = new HashSet<Monomial>(funcFactor.argument.terms);
+          else invFactorArg.Mul(funcFactor.argument);
+        } else {
+          p.factors.Add(aFactor);
+        }
+      }
+      if (expFactorArg.terms.Any()) {
+        p.factors.Add(expFactor);
+      }
+      if (invFactorArg.terms.Any()) {
+        var invFactor = new FunctionFactor(UnaryFunctionType.Inv, invFactorArg);
+        p.factors.Add(invFactor);
+      }
+      return p;
+    }
+  }
+
+  internal class Polynomial {
+    internal HashSet<Monomial> terms = new HashSet<Monomial>();
+    public Polynomial(params Monomial[] term) {
+      foreach (var t in term) terms.Add(t);
+    }
+    public void Add(Polynomial other) {
+      this.terms.UnionWith(other.terms);
+    }
+    public void Mul(Polynomial other) {
+      var myTerms = terms;
+      var otherTerms = other.terms;
+      var newTerms = new HashSet<Monomial>();
+      foreach (var a in myTerms) {
+        foreach (var b in otherTerms) {
+          newTerms.Add(Monomial.Product(a, b));
+        }
+      }
+      terms = newTerms;
+    }
+    public override int GetHashCode() {
+      return terms.OrderBy(ti => ti.GetHashCode()).Aggregate(0, (a, ti) => ((a<<5)+a) ^ ti.GetHashCode());
+    }
+    public override bool Equals(object obj) {
+      Polynomial other = obj as Polynomial;
+      if (other == null) return false;
+      if (other.terms.Count != this.terms.Count) return false;
+      return terms.All(ti => other.terms.Contains(ti)) && 
+        other.terms.All(ti => terms.Contains(ti));
+    }
+  }
+
+
+  // calculates a hash-code for expressions.
+
+  public static class ExprHashSymbolic {
+
+
+    const int MaxStackSize = 100;
+    const int MaxVariables = 26;
+    private static SymbolFactor[] varSymbols;
+    private static SymbolFactor zero;
+    private static SymbolFactor one;
+
+
+    static ExprHashSymbolic() {
+      const string symbols = "abcdefghijklmnopqrstuvwxyz";
+
+      varSymbols = new SymbolFactor[MaxVariables];
+      for (int i = 0; i < MaxVariables; i++) {
+        varSymbols[i] = new SymbolFactor(symbols[i]);
+      }
+      zero = new SymbolFactor('0');
+      one = new SymbolFactor('1');
+    }
+
+    public static int GetHash(byte[] code, int nParams) {
+      return Eval(code, nParams);
+    }
+
+    private static int Eval(byte[] code, int nParams) {
+      // The hash code calculation already preserves commutativity, associativity and distributivity of operations.
+      // We assume that the structure contains numeric parameters 
+      // x = c*x
+      // exp(x) = c*exp(c*x)
+      // log(x) = c*log(x+c)
+      // inv(x) = c/(x+c)
+      // Accordingly, each structure represents a class of functions.
+
+      // The following expressions should hash to the same value as they represent 
+      // equivalent function classes
+      // - x1 + x1 is equivalent to x1
+      // - exp(x1) * exp(x1) is equivalent to exp(x1) 
+      // 
+      // The following experssions must not hash to the same value.
+      // - exp(x1) + exp(x1) is different from exp(x1)
+      // - log(x1) + log(x1) is different from log(x1)
+      // - 1/x1 + 1/x1 is different from 1/x1
+      // - TODO list all
+
+      // think about speed later (TODO)
+
+      var stack = new Polynomial[MaxStackSize];
+      int topOfStack = -1;
+      int pc = 0;
+      int nextParamIdx = -1;
+      OpCodes op;
+      short arg;
+      while (true) {
+        ReadNext(code, ref pc, out op, out arg);
+        switch (op) {
+          case OpCodes.Nop: break;
+          case OpCodes.LoadConst0: {
+              ++topOfStack;
+              stack[topOfStack] = new Polynomial(new Monomial(zero));
+              break;
+            }
+          case OpCodes.LoadConst1: {
+              ++topOfStack;
+              stack[topOfStack] = new Polynomial(new Monomial(one));
+              break;
+            }
+          case OpCodes.LoadParamN: {
+              ++topOfStack;
+              stack[topOfStack] = new Polynomial(new Monomial(one)); // TODO empty, or unique?
+              break;
+            }
+          case OpCodes.LoadVar: {
+              ++topOfStack;
+              stack[topOfStack] = new Polynomial(new Monomial(varSymbols[arg]));
+
+              break;
+            }
+          case OpCodes.Add: {
+              stack[topOfStack - 1].Add(stack[topOfStack]);
+              topOfStack--;
+              break;
+            }
+          case OpCodes.Mul: {
+              stack[topOfStack - 1].Mul(stack[topOfStack]);
+              topOfStack--;
+              break;
+            }
+          case OpCodes.Log: {
+              var v1 = stack[topOfStack];
+              stack[topOfStack] = new Polynomial(new Monomial(new FunctionFactor(UnaryFunctionType.Log, v1)));
+              break;
+            }
+          case OpCodes.Exp: {
+              var v1 = stack[topOfStack];
+              stack[topOfStack] = new Polynomial(new Monomial(new FunctionFactor(UnaryFunctionType.Exp, v1)));
+              break;
+            }
+          case OpCodes.Inv: {
+              var v1 = stack[topOfStack];
+              stack[topOfStack] = new Polynomial(new Monomial(new FunctionFactor(UnaryFunctionType.Inv, v1)));
+              break;
+            }
+          case OpCodes.Exit:
+            Contract.Assert(topOfStack == 0);
+            return stack[topOfStack].GetHashCode();
+          default: throw new InvalidOperationException();
+        }
+      }
+    }
+
+    private static void EvalTerms(HashSet<double> terms, double[] stack, ref int topOfStack) {
+      ++topOfStack;
+      stack[topOfStack] = terms.Sum();
+      terms.Clear();
+    }
+
+    private static void ReadNext(byte[] code, ref int pc, out OpCodes op, out short s) {
+      op = (OpCodes)Enum.ToObject(typeof(OpCodes), code[pc++]);
+      s = 0;
+      if (op == OpCodes.LoadVar) {
+        s = (short)((code[pc] << 8) | code[pc + 1]);
+        pc += 2;
+      }
+    }
+  }
+}
Index: /branches/MCTS-SymbReg-2796/HeuristicLab.Algorithms.DataAnalysis/3.4/MctsSymbolicRegression/MctsSymbolicRegressionStatic.cs
===================================================================
--- /branches/MCTS-SymbReg-2796/HeuristicLab.Algorithms.DataAnalysis/3.4/MctsSymbolicRegression/MctsSymbolicRegressionStatic.cs	(revision 15439)
+++ /branches/MCTS-SymbReg-2796/HeuristicLab.Algorithms.DataAnalysis/3.4/MctsSymbolicRegression/MctsSymbolicRegressionStatic.cs	(revision 15440)
@@ -587,6 +587,8 @@
             // for selected states (EvalStates) we introduce state unification (detection of equivalent states)
             if (automaton.IsEvalState(possibleFollowStates[i])) {
-              var hc = Hashcode(automaton) * (ulong)tree.state; // TODO fix unit test for structure enumeration
+              var hc = Hashcode(automaton);
+              hc = ((hc << 5) + hc) ^ (ulong)tree.state; // TODO fix unit test for structure enumeration
               if (!state.nodes.TryGetValue(hc, out child)) {
+                // Console.WriteLine("New expression (hash: {0}, state: {1})", Hashcode(automaton), automaton.stateNames[possibleFollowStates[i]]);
                 child = new Tree() {
                   state = possibleFollowStates[i],
@@ -598,8 +600,10 @@
               // only allow forward edges (don't add the child if we would go back in the graph)
               else if (child.level > tree.level) {
+                // Console.WriteLine("Existing expression (hash: {0}, state: {1})", Hashcode(automaton), automaton.stateNames[possibleFollowStates[i]]);
                 // whenever we join paths we need to propagate back the statistics of the existing node through the newly created link
                 // to all parents 
                 BackpropagateStatistics(tree, state, child.visits);
               } else {
+                // Console.WriteLine("Cycle (hash: {0}, state: {1})", Hashcode(automaton), automaton.stateNames[possibleFollowStates[i]]);
                 // prevent cycles
                 Debug.Assert(child.level <= tree.level);
@@ -688,5 +692,5 @@
       int nParams;
       automaton.GetCode(out code, out nParams);
-      return ExprHash.GetHash(code, nParams);
+      return (ulong)ExprHashSymbolic.GetHash(code, nParams);
     }
 
Index: /branches/MCTS-SymbReg-2796/Tests/HeuristicLab.Algorithms.DataAnalysis-3.4/MctsSymbolicRegressionTest.cs
===================================================================
--- /branches/MCTS-SymbReg-2796/Tests/HeuristicLab.Algorithms.DataAnalysis-3.4/MctsSymbolicRegressionTest.cs	(revision 15439)
+++ /branches/MCTS-SymbReg-2796/Tests/HeuristicLab.Algorithms.DataAnalysis-3.4/MctsSymbolicRegressionTest.cs	(revision 15440)
@@ -374,5 +374,5 @@
     [TestCategory("Algorithms.DataAnalysis")]
     [TestProperty("Time", "short")]
-    public void ExprHashTest() {
+    public void ExprHashSymbolicTest() {
       int nParams;
       byte[] code;
@@ -386,5 +386,5 @@
         codeGen.Emit1(OpCodes.Exit);
         codeGen.GetCode(out code, out nParams);
-        var h1 = ExprHash.GetHash(code, nParams);
+        var h1 = ExprHashSymbolic.GetHash(code, nParams);
 
         codeGen = new CodeGenerator();
@@ -394,5 +394,5 @@
         codeGen.Emit1(OpCodes.Exit);
         codeGen.GetCode(out code, out nParams);
-        var h2 = ExprHash.GetHash(code, nParams);
+        var h2 = ExprHashSymbolic.GetHash(code, nParams);
 
         Assert.AreEqual(h1, h2);
@@ -407,5 +407,5 @@
         codeGen.Emit1(OpCodes.Exit);
         codeGen.GetCode(out code, out nParams);
-        var h1 = ExprHash.GetHash(code, nParams);
+        var h1 = ExprHashSymbolic.GetHash(code, nParams);
 
         codeGen = new CodeGenerator();
@@ -415,5 +415,5 @@
         codeGen.Emit1(OpCodes.Exit);
         codeGen.GetCode(out code, out nParams);
-        var h2 = ExprHash.GetHash(code, nParams);
+        var h2 = ExprHashSymbolic.GetHash(code, nParams);
 
         Assert.AreEqual(h1, h2);
@@ -430,5 +430,5 @@
         codeGen.Emit1(OpCodes.Exit);
         codeGen.GetCode(out code, out nParams);
-        var h1 = ExprHash.GetHash(code, nParams);
+        var h1 = ExprHashSymbolic.GetHash(code, nParams);
 
         codeGen = new CodeGenerator();
@@ -442,5 +442,5 @@
         codeGen.Emit1(OpCodes.Exit);
         codeGen.GetCode(out code, out nParams);
-        var h2 = ExprHash.GetHash(code, nParams);
+        var h2 = ExprHashSymbolic.GetHash(code, nParams);
 
         Assert.AreEqual(h1, h2);
@@ -448,6 +448,5 @@
 
 
-      {
-        // div 
+      { // 1/(x1x2) = 1/x1 * 1/x2
         var codeGen = new CodeGenerator();
         codeGen.Emit2(MctsSymbolicRegression.OpCodes.LoadVar, 1);
@@ -457,5 +456,5 @@
         codeGen.Emit1(OpCodes.Exit);
         codeGen.GetCode(out code, out nParams);
-        var h1 = ExprHash.GetHash(code, nParams);
+        var h1 = ExprHashSymbolic.GetHash(code, nParams);
 
         codeGen = new CodeGenerator();
@@ -467,5 +466,5 @@
         codeGen.Emit1(OpCodes.Exit);
         codeGen.GetCode(out code, out nParams);
-        var h2 = ExprHash.GetHash(code, nParams);
+        var h2 = ExprHashSymbolic.GetHash(code, nParams);
 
         Assert.AreEqual(h1, h2);
@@ -480,5 +479,5 @@
         codeGen.Emit1(OpCodes.Exit);
         codeGen.GetCode(out code, out nParams);
-        var h1 = ExprHash.GetHash(code, nParams);
+        var h1 = ExprHashSymbolic.GetHash(code, nParams);
 
         codeGen = new CodeGenerator();
@@ -490,5 +489,5 @@
         codeGen.GetCode(out code, out nParams);
         codeGen.Emit1(OpCodes.Exit);
-        var h2 = ExprHash.GetHash(code, nParams);
+        var h2 = ExprHashSymbolic.GetHash(code, nParams);
 
         Assert.AreEqual(h1, h2);
@@ -503,5 +502,5 @@
         codeGen.Emit1(OpCodes.Exit);
         codeGen.GetCode(out code, out nParams);
-        var h1 = ExprHash.GetHash(code, nParams);
+        var h1 = ExprHashSymbolic.GetHash(code, nParams);
 
         codeGen = new CodeGenerator();
@@ -513,5 +512,5 @@
         codeGen.Emit1(OpCodes.Exit);
         codeGen.GetCode(out code, out nParams);
-        var h2 = ExprHash.GetHash(code, nParams);
+        var h2 = ExprHashSymbolic.GetHash(code, nParams);
 
         Assert.AreEqual(h1, h2);
@@ -526,5 +525,5 @@
         codeGen.Emit1(OpCodes.Exit);
         codeGen.GetCode(out code, out nParams);
-        var h1 = ExprHash.GetHash(code, nParams);
+        var h1 = ExprHashSymbolic.GetHash(code, nParams);
 
         codeGen = new CodeGenerator();
@@ -532,5 +531,5 @@
         codeGen.Emit1(OpCodes.Exit);
         codeGen.GetCode(out code, out nParams);
-        var h2 = ExprHash.GetHash(code, nParams);
+        var h2 = ExprHashSymbolic.GetHash(code, nParams);
 
         Assert.AreEqual(h1, h2);
@@ -550,5 +549,5 @@
         codeGen.Emit1(OpCodes.Exit);
         codeGen.GetCode(out code, out nParams);
-        var h1 = ExprHash.GetHash(code, nParams);
+        var h1 = ExprHashSymbolic.GetHash(code, nParams);
 
         codeGen = new CodeGenerator();
@@ -558,10 +557,10 @@
         codeGen.Emit1(OpCodes.Exit);
         codeGen.GetCode(out code, out nParams);
-        var h2 = ExprHash.GetHash(code, nParams);
+        var h2 = ExprHashSymbolic.GetHash(code, nParams);
 
         Assert.AreEqual(h1, h2);
       }
 
-      {
+      { // c1 x1 + c2 x1 = c3 x1 (extended version)
         var codeGen = new CodeGenerator();
         codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadConst0);
@@ -582,5 +581,5 @@
         codeGen.Emit1(OpCodes.Exit);
         codeGen.GetCode(out code, out nParams);
-        var h1 = ExprHash.GetHash(code, nParams);
+        var h1 = ExprHashSymbolic.GetHash(code, nParams);
 
         codeGen = new CodeGenerator();
@@ -594,8 +593,167 @@
         codeGen.Emit1(OpCodes.Exit);
         codeGen.GetCode(out code, out nParams);
-        var h2 = ExprHash.GetHash(code, nParams);
+        var h2 = ExprHashSymbolic.GetHash(code, nParams);
 
         Assert.AreEqual(h1, h2);
       }
+      {
+        // exp(x1) * exp(x1) = exp(x1)
+        var codeGen = new CodeGenerator();
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadConst0);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadConst1);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadParamN);
+        codeGen.Emit2(MctsSymbolicRegression.OpCodes.LoadVar, 1);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Mul);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Exp);
+
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadParamN);
+        codeGen.Emit2(MctsSymbolicRegression.OpCodes.LoadVar, 1);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Mul);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Exp);
+
+        codeGen.Emit1(OpCodes.Mul);
+
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Mul);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Add);
+
+        codeGen.Emit1(OpCodes.Exit);
+        codeGen.GetCode(out code, out nParams);
+        var h1 = ExprHashSymbolic.GetHash(code, nParams);
+
+        codeGen = new CodeGenerator();
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadConst0);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadConst1);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadParamN);
+        codeGen.Emit2(MctsSymbolicRegression.OpCodes.LoadVar, 1);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Mul);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Exp);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Mul);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Add);
+        codeGen.Emit1(OpCodes.Exit);
+        codeGen.GetCode(out code, out nParams);
+        var h2 = ExprHashSymbolic.GetHash(code, nParams);
+
+        Assert.AreEqual(h1, h2);
+      }
+      {
+        // inv(x1) + inv(x1) != inv(x1)
+        var codeGen = new CodeGenerator();
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadConst0);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadConst1);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadParamN);
+        codeGen.Emit2(MctsSymbolicRegression.OpCodes.LoadVar, 1);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Mul);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Inv);
+
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadParamN);
+        codeGen.Emit2(MctsSymbolicRegression.OpCodes.LoadVar, 1);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Mul);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Inv);
+
+        codeGen.Emit1(OpCodes.Add);
+
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Mul);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Add);
+
+        codeGen.Emit1(OpCodes.Exit);
+        codeGen.GetCode(out code, out nParams);
+        var h1 = ExprHashSymbolic.GetHash(code, nParams);
+
+        codeGen = new CodeGenerator();
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadConst0);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadConst1);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadParamN);
+        codeGen.Emit2(MctsSymbolicRegression.OpCodes.LoadVar, 1);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Mul);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Inv);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Mul);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Add);
+        codeGen.Emit1(OpCodes.Exit);
+        codeGen.GetCode(out code, out nParams);
+        var h2 = ExprHashSymbolic.GetHash(code, nParams);
+
+        Assert.AreNotEqual(h1, h2);
+      }
+
+      {
+        // exp(x1) + exp(x1) != exp(x1)
+        var codeGen = new CodeGenerator();
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadConst0);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadConst1);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadParamN);
+        codeGen.Emit2(MctsSymbolicRegression.OpCodes.LoadVar, 1);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Mul);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Exp);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Mul);
+
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadConst1);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadParamN);
+        codeGen.Emit2(MctsSymbolicRegression.OpCodes.LoadVar, 1);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Mul);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Exp);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Mul);
+
+        codeGen.Emit1(OpCodes.Add);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Add);
+
+        codeGen.Emit1(OpCodes.Exit);
+        codeGen.GetCode(out code, out nParams);
+        var h1 = ExprHashSymbolic.GetHash(code, nParams);
+
+        codeGen = new CodeGenerator();
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadConst0);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadConst1);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadParamN);
+        codeGen.Emit2(MctsSymbolicRegression.OpCodes.LoadVar, 1);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Mul);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Exp);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Mul);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Add);
+        codeGen.Emit1(OpCodes.Exit);
+        codeGen.GetCode(out code, out nParams);
+        var h2 = ExprHashSymbolic.GetHash(code, nParams);
+
+        Assert.AreNotEqual(h1, h2);
+      }
+      {
+        // log(x1) + log(x1) != log(x1)
+        var codeGen = new CodeGenerator();
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadConst0);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadConst1);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadParamN);
+        codeGen.Emit2(MctsSymbolicRegression.OpCodes.LoadVar, 1);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Mul);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Log);
+
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadParamN);
+        codeGen.Emit2(MctsSymbolicRegression.OpCodes.LoadVar, 1);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Mul);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Log);
+
+        codeGen.Emit1(OpCodes.Add);
+
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Mul);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Add);
+
+        codeGen.Emit1(OpCodes.Exit);
+        codeGen.GetCode(out code, out nParams);
+        var h1 = ExprHashSymbolic.GetHash(code, nParams);
+
+        codeGen = new CodeGenerator();
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadConst0);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadConst1);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadParamN);
+        codeGen.Emit2(MctsSymbolicRegression.OpCodes.LoadVar, 1);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Mul);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Log);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Mul);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Add);
+        codeGen.Emit1(OpCodes.Exit);
+        codeGen.GetCode(out code, out nParams);
+        var h2 = ExprHashSymbolic.GetHash(code, nParams);
+
+        Assert.AreNotEqual(h1, h2);
+      }
+
     }
     #endregion
@@ -662,14 +820,14 @@
         // log(x + c) * exp(x)
         // log(x + c) * 1/(x + c)
-        // log(x + c) + log(x + c)                        // TODO
+        // log(x + c) + log(x + c)                        
         // log(x + c) + exp(x)
         // log(x + c) + 1/(x+c)
         //              -- 6
-        // exp(x) * 1/(x+c)
-        // exp(cx) + exp(cx)                              // TODO
+        // exp(cx) * 1/(x+c)
+        // exp(cx) + exp(cx)                              
         // exp(cx) + 1/(x+c)
         //              -- 3
-        // 1/(x+c) * 1/(x+c)                              // TODO
-        // 1/(x+c) + 1/(x+c)                              // TODO
+        // 1/(x+c) * 1/(x+c)                              
+        // 1/(x+c) + 1/(x+c)                              
         //              -- 2
         // log(x*x)
@@ -679,5 +837,5 @@
 
 
-        TestMctsNumberOfSolutions(regProblem, 2, 27);
+        TestMctsNumberOfSolutions(regProblem, 2, 25);
       }
       {
@@ -688,30 +846,22 @@
         //              -- 2
         // x * x
-        // x + x                                            ?
         // x * exp(x)
         // x + exp(x)
-        // exp(x) * exp(x)
-        // exp(x) + exp(x)                                  ?
+        // exp(x) + exp(x)
         // exp(x*x)
-        //              -- 7
+        //              -- 5
         // x * x * x
         // x + x * x                                       
-        // x + x + x                                        ?
         // x * x * exp(x)
         // x + x * exp(x)                                   
-        // x + x + exp(x)                                   ?
         // exp(x) + x*x
         // exp(x) + x*exp(x)                                
-        // x + exp(x) * exp(x)                              
-        // x + exp(x) + exp(x)                              ?
-        // x * exp(x) * exp(x)
+        // x + exp(x) + exp(x)                              
         // x * exp(x*x)
         // x + exp(x*x)
-        //              -- 13
-
-        // exp(x) * exp(x) * exp(x)
-        // exp(x) + exp(x) * exp(x)                         
-        // exp(x) + exp(x) + exp(x)                         ?
-        //              -- 3
+        //              -- 9
+
+        // exp(x) + exp(x) + exp(x)                         
+        //              -- 1
 
         // exp(x)   * exp(x*x)
@@ -720,5 +870,5 @@
         // exp(x*x*x)
         //              -- 1
-        TestMctsNumberOfSolutions(regProblem, 3, 2 + 7 + 13 + 3 + 2 + 1, allowLog: false, allowInv: false);
+        TestMctsNumberOfSolutions(regProblem, 3, 2+5+9+1+2+1, allowLog: false, allowInv: false);
       }      
     }
@@ -762,18 +912,18 @@
         // exp(y)
         //                  -- 4
-        // x (*|+) x
+        // x (*) x
         // x (*|+) exp(x)
         // x (*|+) y
         // x (*|+) exp(y)
-        //                  -- 8
-        // exp(x) (*|+) exp(x)
+        //                  -- 7
+        // exp(x) (+) exp(x)
         // exp(x) (*|+) exp(y)
-        //                  -- 4
-        // y (*|+) y
+        //                  -- 3
+        // y (*) y
         // y (*|+) exp(x)
         // y (*|+) exp(y)
-        //                  -- 6
-        // exp(y) (*|+) exp(y)
-        //                  -- 2
+        //                  -- 5
+        // exp(y) (+) exp(y)
+        //                  -- 1
         //
         // exp(x*x)
@@ -782,5 +932,5 @@
         //                  -- 3
 
-        TestMctsNumberOfSolutions(regProblem, 2, 4 + 8 + 4 + 6 + 2 + 3, allowLog: false, allowInv: false);
+        TestMctsNumberOfSolutions(regProblem, 2, 4 + 7 + 3 + 5 + 1 + 3, allowLog: false, allowInv: false);
       }
 
@@ -830,13 +980,8 @@
         // inv(y*y)
         //             -- 6
-        // log(x+x)
         // log(x+y)
-        // log(y+y)
-
-        // inv(x+x)
         // inv(x+y)
-        // inv(y+y)
-        //             -- 6
-        TestMctsNumberOfSolutions(regProblem, 2, 6 + 6 + 4 + 2 + 5 + 3 + 1 + 6 + 6, allowExp: false, allowSum: false);
+        //             -- 2
+        TestMctsNumberOfSolutions(regProblem, 2, 6 + 6 + 4 + 2 + 5 + 3 + 1 + 6 + 2, allowExp: false, allowSum: false);
       }
     }
