Index: /trunk/HeuristicLab.Encodings.SymbolicExpressionTreeEncoding/3.4/Interfaces/ISymbolicExpressionTreeNode.cs
===================================================================
--- /trunk/HeuristicLab.Encodings.SymbolicExpressionTreeEncoding/3.4/Interfaces/ISymbolicExpressionTreeNode.cs	(revision 18210)
+++ /trunk/HeuristicLab.Encodings.SymbolicExpressionTreeEncoding/3.4/Interfaces/ISymbolicExpressionTreeNode.cs	(revision 18211)
@@ -50,4 +50,6 @@
     void InsertSubtree(int index, ISymbolicExpressionTreeNode tree);
     void RemoveSubtree(int index);
+    void ReplaceSubtree(int index, ISymbolicExpressionTreeNode tree);
+    void ReplaceSubtree(ISymbolicExpressionTreeNode orig, ISymbolicExpressionTreeNode repl);
 
     void ResetLocalParameters(IRandom random);
Index: /trunk/HeuristicLab.Encodings.SymbolicExpressionTreeEncoding/3.4/SymbolicExpressionTreeNode.cs
===================================================================
--- /trunk/HeuristicLab.Encodings.SymbolicExpressionTreeEncoding/3.4/SymbolicExpressionTreeNode.cs	(revision 18210)
+++ /trunk/HeuristicLab.Encodings.SymbolicExpressionTreeEncoding/3.4/SymbolicExpressionTreeNode.cs	(revision 18211)
@@ -177,4 +177,17 @@
       ResetCachedValues();
     }
+    public virtual void ReplaceSubtree(int index, ISymbolicExpressionTreeNode repl) {
+      subtrees[index].Parent = null;
+      subtrees[index] = repl;
+      repl.Parent = this;
+      ResetCachedValues();
+    }
+    public virtual void ReplaceSubtree(ISymbolicExpressionTreeNode old, ISymbolicExpressionTreeNode repl) {
+      var index = IndexOfSubtree(old);
+      subtrees[index].Parent = null;
+      subtrees[index] = repl;
+      repl.Parent = this;
+      ResetCachedValues();
+    }
 
     public IEnumerable<ISymbolicExpressionTreeNode> IterateNodesBreadth() {
Index: /trunk/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Formatters/InfixExpressionFormatter.cs
===================================================================
--- /trunk/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Formatters/InfixExpressionFormatter.cs	(revision 18210)
+++ /trunk/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Formatters/InfixExpressionFormatter.cs	(revision 18211)
@@ -32,80 +32,91 @@
 namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
   public static class BaseInfixExpressionFormatter {
+    /// <summary>
+    ///  Performs some basic re-writing steps to simplify the code for formatting. Tree is changed.
+    ///  Removes single-argument +, * which have no effect
+    ///  Replaces variables with coefficients by an explicitly multiplication
+    ///  Replaces single-argument / with 1 / (..)
+    ///  Replaces multi-argument +, *, /, - with nested binary operations
+    ///  Rotates operations to remove necessity to group sub-expressions
+    /// </summary>
+    /// <param name="tree">The tree is changed</param>
+    public static void ConvertToBinaryLeftAssoc(ISymbolicExpressionTree tree) {
+      ConvertToBinaryLeftAssocRec(tree.Root.GetSubtree(0), tree.Root.GetSubtree(0).GetSubtree(0));
+    }
+
+    private static void ConvertToBinaryLeftAssocRec(ISymbolicExpressionTreeNode parent, ISymbolicExpressionTreeNode n) {
+      // recurse post-order
+      foreach (var subtree in n.Subtrees.ToArray()) ConvertToBinaryLeftAssocRec(n, subtree); // ToArray required as n.Subtrees is changed in method
+
+      if (n is VariableTreeNode varTreeNode && varTreeNode.Weight != 1.0) {
+        var mul = new Multiplication().CreateTreeNode();
+        var num = (NumberTreeNode)new Number().CreateTreeNode();
+        num.Value = varTreeNode.Weight;
+        varTreeNode.Weight = 1.0;
+        parent.ReplaceSubtree(n, mul);
+        mul.AddSubtree(num);
+        mul.AddSubtree(varTreeNode);
+      } else if (n.SubtreeCount == 1 && (n.Symbol is Addition || n.Symbol is Multiplication || n.Symbol is And || n.Symbol is Or || n.Symbol is Xor)) {
+        // single-argument addition or multiplication has no effect -> remove
+        parent.ReplaceSubtree(n, n.GetSubtree(0));
+      } else if (n.SubtreeCount == 1 && (n.Symbol is Division)) {
+        // single-argument division is 1/f(x)
+        n.InsertSubtree(0, new Constant() { Value = 1.0 }.CreateTreeNode());
+      } else if (n.SubtreeCount > 2 && IsLeftAssocOp(n.Symbol)) {
+        // multi-argument +, -, *, / are the same as multiple binary operations (left-associative)
+        var sy = n.Symbol;
+
+        var additionalTrees = n.Subtrees.Skip(2).ToArray();
+        while (n.SubtreeCount > 2) n.RemoveSubtree(2); // keep only the first two arguments
+
+        var childIdx = parent.IndexOfSubtree(n);
+        parent.RemoveSubtree(childIdx);
+        var newChild = n;
+        // build a tree bottom to top, each time adding a subtree on the right
+        for (int i = 0; i < additionalTrees.Length; i++) {
+          var newOp = sy.CreateTreeNode();
+          newOp.AddSubtree(newChild);
+          newOp.AddSubtree(additionalTrees[i]);
+          newChild = newOp;
+        }
+        parent.InsertSubtree(childIdx, newChild);
+      } else if (n.SubtreeCount == 2 && n.GetSubtree(1).SubtreeCount == 2 && 
+                 IsAssocOp(n.Symbol) && IsOperator(n.GetSubtree(1).Symbol) && 
+                 Priority(n.Symbol) == Priority(n.GetSubtree(1).Symbol)) {
+        // f(x) <op> (g(x) <op> h(x))) is the same as  (f(x) <op> g(x)) <op> h(x) for associative <op>
+        // which is the same as f(x) <op> g(x) <op> h(x) for left-associative <op>
+        // The latter version is preferred because we do not need to write the parentheses.
+        // rotation:
+        //     (op1)              (op2)
+        //     /   \              /  \
+        //    a    (op2)       (op1)  c 
+        //         /  \        /  \
+        //        b    c      a    b      
+        var op1 = n;
+        var op2 = n.GetSubtree(1);
+        var b = op2.GetSubtree(0); op2.RemoveSubtree(0);
+        op1.ReplaceSubtree(op2, b);
+        parent.ReplaceSubtree(op1, op2);
+        op2.InsertSubtree(0, op1);
+      }
+    }
     public static void FormatRecursively(ISymbolicExpressionTreeNode node, StringBuilder strBuilder,
                                           NumberFormatInfo numberFormat, string formatString, List<KeyValuePair<string, double>> parameters = null) {
-      if (node.SubtreeCount > 1) {
-        var token = GetToken(node.Symbol);
-        // operators
-        if (token == "+" || token == "-" || token == "OR" || token == "XOR") {
-          var parenthesisRequired = false;
-          if (node.Parent != null && IsOperator(node.Parent.Symbol)) {
-            var parentOp = GetToken(node.Parent.Symbol);
-            if (parentOp != "+" && parentOp != "-" && parentOp != "OR" && parentOp != "XOR")
-              parenthesisRequired = true;
-          }
+      // This method assumes that the tree has been converted to binary and left-assoc form (see ConvertToBinaryLeftAssocRec). 
+      // An exception is thrown if the tree has a different shape.
+
+      if (node.SubtreeCount == 0) {
+        // no subtrees
+        if (node.Symbol is Variable) {
+          var varNode = node as VariableTreeNode;
+          if (varNode.Weight != 1.0) { throw new NotSupportedException("Infix formatter does not support variables with coefficients."); }
+          AppendVariableName(strBuilder, varNode.VariableName);
+        } else if (node is INumericTreeNode numNode) {
+          var parenthesisRequired = RequiresParenthesis(node.Parent, node);
           if (parenthesisRequired) strBuilder.Append("(");
-          FormatRecursively(node.Subtrees.First(), strBuilder, numberFormat, formatString, parameters);
-
-          foreach (var subtree in node.Subtrees.Skip(1)) {
-            strBuilder.Append(" ").Append(token).Append(" ");
-            FormatRecursively(subtree, strBuilder, numberFormat, formatString, parameters);
-          }
-
+
+          AppendNumber(strBuilder, parameters, numNode.Value, formatString, numberFormat);
           if (parenthesisRequired) strBuilder.Append(")");
-        } else if (token == "*" || token == "/" || token == "AND") {
-          FormatRecursively(node.Subtrees.First(), strBuilder, numberFormat, formatString, parameters);
-
-          foreach (var subtree in node.Subtrees.Skip(1)) {
-            strBuilder.Append(" ").Append(token).Append(" ");
-            // a / b * c => a / (b * C)
-            if (subtree.SubtreeCount > 1 && token == "/" && GetToken(subtree.Symbol) == "*") {
-              strBuilder.Append("(");
-              FormatRecursively(subtree, strBuilder, numberFormat, formatString, parameters);
-              strBuilder.Append(")");
-            } else {
-              FormatRecursively(subtree, strBuilder, numberFormat, formatString, parameters);
-            }
-          }
-        } else if (token == "^") {
-          // handle integer powers directly
-          FormatRecursively(node.Subtrees.First(), strBuilder, numberFormat, formatString, parameters);
-
-          var power = node.GetSubtree(1);
-          if (power is INumericTreeNode numNode && Math.Truncate(numNode.Value) == numNode.Value) {
-            strBuilder.Append(" ").Append(token).Append(" ").Append(numNode.Value.ToString(formatString, numberFormat));
-          } else {
-            strBuilder.Append(" ").Append(token).Append(" ");
-            FormatRecursively(power, strBuilder, numberFormat, formatString, parameters);
-          }
-        } else {
-          // function with multiple arguments
-          strBuilder.Append(token).Append("(");
-          FormatRecursively(node.Subtrees.First(), strBuilder, numberFormat, formatString, parameters);
-          foreach (var subtree in node.Subtrees.Skip(1)) {
-            strBuilder.Append(", ");
-            FormatRecursively(subtree, strBuilder, numberFormat, formatString, parameters);
-          }
-
-          strBuilder.Append(")");
-        }
-      } else if (node.SubtreeCount == 1) {
-        var token = GetToken(node.Symbol);
-        if (token == "-" || token == "NOT") {
-          strBuilder.Append(token);
-          FormatRecursively(node.GetSubtree(0), strBuilder, numberFormat, formatString, parameters);
-        } else if (token == "/") {
-          strBuilder.Append("1/");
-          FormatRecursively(node.GetSubtree(0), strBuilder, numberFormat, formatString, parameters);
-        } else if (token == "+" || token == "*") {
-          FormatRecursively(node.GetSubtree(0), strBuilder, numberFormat, formatString, parameters);
-        } else {
-          // function with only one argument
-          strBuilder.Append(token).Append("(");
-          FormatRecursively(node.GetSubtree(0), strBuilder, numberFormat, formatString, parameters);
-          strBuilder.Append(")");
-        }
-      } else {
-        // no subtrees
-        if (node.Symbol is LaggedVariable) {
+        } else if (node.Symbol is LaggedVariable) {
           var varNode = node as LaggedVariableTreeNode;
           if (!varNode.Weight.IsAlmost(1.0)) {
@@ -119,11 +130,4 @@
                     .AppendFormat(numberFormat, "{0}", varNode.Lag)
                     .Append(")");
-        } else if (node.Symbol is Variable) {
-          var varNode = node as VariableTreeNode;
-          if (!varNode.Weight.IsAlmost(1.0)) {
-            AppendNumber(strBuilder, parameters, varNode.Weight, formatString, numberFormat);
-            strBuilder.Append("*");
-          }
-          AppendVariableName(strBuilder, varNode.VariableName);
         } else if (node.Symbol is FactorVariable) {
           var factorNode = node as FactorVariableTreeNode;
@@ -145,23 +149,120 @@
           strBuilder.Append(" = ");
           AppendVariableName(strBuilder, factorNode.VariableValue);
-        } else if (node is INumericTreeNode numNode) {
-          if (parameters == null && numNode.Value < 0) {
-            // negative value
-            strBuilder.Append(numNode.Value.ToString(formatString, numberFormat));
-          } else {
-            AppendNumber(strBuilder, parameters, numNode.Value, formatString, numberFormat);
-          }
         }
-      }
-    }
-
-    private static bool IsOperator(ISymbol sy) {
-      return sy is Addition ||
-        sy is Subtraction ||
-        sy is Multiplication ||
-        sy is Division ||
-        sy is And ||
-        sy is Or ||
-        sy is Xor;
+      } else if (node.SubtreeCount == 1) {
+        // only functions and single-argument subtraction (=negation) or NOT are possible here.
+        var token = GetToken(node.Symbol);
+        // the only operators that are allowed with a single argument
+        if (node.Symbol is Subtraction || node.Symbol is Not) {
+          if (RequiresParenthesis(node.Parent, node)) {
+            strBuilder.Append("(");
+          }
+          strBuilder.Append(token);
+          FormatRecursively(node.GetSubtree(0), strBuilder, numberFormat, formatString, parameters);
+          if (RequiresParenthesis(node.Parent, node)) {
+            strBuilder.Append(")");
+          }
+        } else if (IsOperator(node.Symbol)) {
+          throw new FormatException("Single-argument version of " + node.Symbol.Name + " is not supported.");
+        } else {
+          // function with only one argument
+          strBuilder.Append(token);
+          strBuilder.Append("(");
+          FormatRecursively(node.GetSubtree(0), strBuilder, numberFormat, formatString, parameters);
+          strBuilder.Append(")");
+        }
+      } else if (node.SubtreeCount > 1) {
+        var token = GetToken(node.Symbol);
+        // operators
+        if (IsOperator(node.Symbol)) {
+          var parenthesisRequired = RequiresParenthesis(node.Parent, node);
+          if (parenthesisRequired) strBuilder.Append("(");
+          FormatRecursively(node.Subtrees.First(), strBuilder, numberFormat, formatString, parameters);
+
+          foreach (var subtree in node.Subtrees.Skip(1)) {
+            strBuilder.Append(" ").Append(token).Append(" ");
+            FormatRecursively(subtree, strBuilder, numberFormat, formatString, parameters);
+          }
+
+          if (parenthesisRequired) strBuilder.Append(")");
+        } else {
+          // function with multiple arguments (AQ)
+
+          strBuilder.Append(token);
+          strBuilder.Append("(");
+
+          FormatRecursively(node.Subtrees.First(), strBuilder, numberFormat, formatString, parameters);
+          foreach (var subtree in node.Subtrees.Skip(1)) {
+            strBuilder.Append(", ");
+            FormatRecursively(subtree, strBuilder, numberFormat, formatString, parameters);
+          }
+          strBuilder.Append(")");
+        }
+      }
+    }
+
+    private static int Priority(ISymbol symbol) {
+      if (symbol is Addition || symbol is Subtraction || symbol is Or || symbol is Xor) return 1;
+      if (symbol is Division || symbol is Multiplication || symbol is And) return 2;
+      if (symbol is Power || symbol is Not) return 3;
+      throw new NotSupportedException();
+    }
+
+    private static bool RequiresParenthesis(ISymbolicExpressionTreeNode parent, ISymbolicExpressionTreeNode child) {
+      if (child.SubtreeCount > 2 && IsOperator(child.Symbol)) throw new NotSupportedException("Infix formatter does not support operators with more than two children.");
+
+      // Basically: We need a parenthesis for child if the parent symbol binds stronger than child symbol.
+      if (parent.Symbol == null || parent.Symbol is ProgramRootSymbol || parent.Symbol is StartSymbol) return false;
+      if (IsFunction(parent.Symbol)) return false;
+      if (parent.SubtreeCount == 1 && (parent.Symbol is Subtraction)) return true;
+      if (child.SubtreeCount == 0) return false;
+      var parentPrio = Priority(parent.Symbol);
+      var childPrio = Priority(child.Symbol);
+      if (parentPrio > childPrio) return true;
+      else if (parentPrio == childPrio) {
+        if (IsLeftAssocOp(child.Symbol)) return parent.GetSubtree(0) != child; // (..) required only for right child for left-assoc op
+        if (IsRightAssocOp(child.Symbol)) return parent.GetSubtree(1) != child;
+      }
+      return false;
+    }
+
+    private static bool IsFunction(ISymbol symbol) {
+      // functions are formatted in prefix form e.g. sin(...)
+      return !IsOperator(symbol) && !IsLeaf(symbol);
+    }
+
+    private static bool IsLeaf(ISymbol symbol) {
+      return symbol.MaximumArity == 0;
+    }
+
+    private static bool IsOperator(ISymbol symbol) {
+      return IsLeftAssocOp(symbol) || IsRightAssocOp(symbol);
+    }
+
+    private static bool IsAssocOp(ISymbol symbol) {
+      // (a <op> b) <op> c = a <op> (b <op> c)
+      return symbol is Addition ||
+        symbol is Multiplication ||
+        symbol is And ||
+        symbol is Or ||
+        symbol is Xor;
+    }
+
+    private static bool IsLeftAssocOp(ISymbol symbol) {
+      // a <op> b <op> c = (a <op> b) <op> c
+      return symbol is Addition ||
+        symbol is Subtraction ||
+        symbol is Multiplication ||
+        symbol is Division ||
+        symbol is And ||
+        symbol is Or ||
+        symbol is Xor;
+    }
+
+    private static bool IsRightAssocOp(ISymbol symbol) {
+      // a <op> b <op> c = a <op> (b <op> c)
+      // Negation (single-argument subtraction) is also right-assoc, but we do not have a separate symbol for negation.
+      return symbol is Not ||
+             symbol is Power; // x^y^z = x^(y^z) (as in Fortran or Mathematica)
     }
 
@@ -224,5 +325,7 @@
       // skip root and start symbols
       StringBuilder strBuilder = new StringBuilder();
-      BaseInfixExpressionFormatter.FormatRecursively(symbolicExpressionTree.Root.GetSubtree(0).GetSubtree(0),
+      var cleanTree = (ISymbolicExpressionTree)symbolicExpressionTree.Clone();
+      BaseInfixExpressionFormatter.ConvertToBinaryLeftAssoc(cleanTree);
+      BaseInfixExpressionFormatter.FormatRecursively(cleanTree.Root.GetSubtree(0).GetSubtree(0),
         strBuilder, numberFormat, formatString);
       return strBuilder.ToString();
@@ -235,5 +338,5 @@
 
   [StorableType("54D917E8-134E-4066-9A60-2737C12D81DC")]
-  [Item("Infix String Formater", "Formatter for symbolic expressions, which produces an infix expression " +
+  [Item("Infix String Formatter", "Formatter for symbolic expressions, which produces an infix expression " +
                                  "as well as a list of all coefficient values")]
   public sealed class InfixExpressionStringFormatter : NamedItem, ISymbolicExpressionTreeStringFormatter {
@@ -255,5 +358,7 @@
       StringBuilder strBuilder = new StringBuilder();
       var parameters = new List<KeyValuePair<string, double>>();
-      BaseInfixExpressionFormatter.FormatRecursively(symbolicExpressionTree.Root.GetSubtree(0).GetSubtree(0),
+      var cleanTree = (ISymbolicExpressionTree)symbolicExpressionTree.Clone();
+      BaseInfixExpressionFormatter.ConvertToBinaryLeftAssoc(cleanTree);
+      BaseInfixExpressionFormatter.FormatRecursively(cleanTree.Root.GetSubtree(0).GetSubtree(0),
         strBuilder, NumberFormatInfo.InvariantInfo, "G", parameters);
       strBuilder.Append($"{Environment.NewLine}{Environment.NewLine}");
Index: /trunk/HeuristicLab.Tests/HeuristicLab.Problems.DataAnalysis.Symbolic-3.4/InfixExpressionParserTest.cs
===================================================================
--- /trunk/HeuristicLab.Tests/HeuristicLab.Problems.DataAnalysis.Symbolic-3.4/InfixExpressionParserTest.cs	(revision 18210)
+++ /trunk/HeuristicLab.Tests/HeuristicLab.Problems.DataAnalysis.Symbolic-3.4/InfixExpressionParserTest.cs	(revision 18211)
@@ -22,4 +22,5 @@
 
 using System;
+using System.Linq;
 using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
 using Microsoft.VisualStudio.TestTools.UnitTesting;
@@ -37,37 +38,37 @@
       Assert.AreEqual("3", formatter.Format(parser.Parse("3")));
       Assert.AreEqual("3 * 3", formatter.Format(parser.Parse("3*3")));
-      Assert.AreEqual("3 * 4",formatter.Format(parser.Parse("3 * 4")));
-      Assert.AreEqual("0.123",formatter.Format(parser.Parse("123E-03")));
-      Assert.AreEqual("0.123",formatter.Format(parser.Parse("123e-03")));
-      Assert.AreEqual("123000",formatter.Format(parser.Parse("123e+03")));
-      Assert.AreEqual("123000",formatter.Format(parser.Parse("123E+03")));
-      Assert.AreEqual("0.123",formatter.Format(parser.Parse("123.0E-03")));
-      Assert.AreEqual("0.123",formatter.Format(parser.Parse("123.0e-03")));
-      Assert.AreEqual("123000",formatter.Format(parser.Parse("123.0e+03")));
-      Assert.AreEqual("123000",formatter.Format(parser.Parse("123.0E+03")));
-      Assert.AreEqual("0.123",formatter.Format(parser.Parse("123.0E-3")));
-      Assert.AreEqual("0.123",formatter.Format(parser.Parse("123.0e-3")));
-      Assert.AreEqual("123000",formatter.Format(parser.Parse("123.0e+3")));
-      Assert.AreEqual("123000",formatter.Format(parser.Parse("123.0E+3")));
-
-      Assert.AreEqual("3.1415 + 2",formatter.Format(parser.Parse("3.1415+2.0")));
-      Assert.AreEqual("3.1415 / 2",formatter.Format(parser.Parse("3.1415/2.0")));
-      Assert.AreEqual("3.1415 * 2",formatter.Format(parser.Parse("3.1415*2.0")));
+      Assert.AreEqual("3 * 4", formatter.Format(parser.Parse("3 * 4")));
+      Assert.AreEqual("0.123", formatter.Format(parser.Parse("123E-03")));
+      Assert.AreEqual("0.123", formatter.Format(parser.Parse("123e-03")));
+      Assert.AreEqual("123000", formatter.Format(parser.Parse("123e+03")));
+      Assert.AreEqual("123000", formatter.Format(parser.Parse("123E+03")));
+      Assert.AreEqual("0.123", formatter.Format(parser.Parse("123.0E-03")));
+      Assert.AreEqual("0.123", formatter.Format(parser.Parse("123.0e-03")));
+      Assert.AreEqual("123000", formatter.Format(parser.Parse("123.0e+03")));
+      Assert.AreEqual("123000", formatter.Format(parser.Parse("123.0E+03")));
+      Assert.AreEqual("0.123", formatter.Format(parser.Parse("123.0E-3")));
+      Assert.AreEqual("0.123", formatter.Format(parser.Parse("123.0e-3")));
+      Assert.AreEqual("123000", formatter.Format(parser.Parse("123.0e+3")));
+      Assert.AreEqual("123000", formatter.Format(parser.Parse("123.0E+3")));
+
+      Assert.AreEqual("3.1415 + 2", formatter.Format(parser.Parse("3.1415+2.0")));
+      Assert.AreEqual("3.1415 / 2", formatter.Format(parser.Parse("3.1415/2.0")));
+      Assert.AreEqual("3.1415 * 2", formatter.Format(parser.Parse("3.1415*2.0")));
       Assert.AreEqual("3.1415 - 2", formatter.Format(parser.Parse("3.1415-2.0")));
       // round-trip
-      Assert.AreEqual("3.1415 - 2",formatter.Format(parser.Parse(formatter.Format(parser.Parse("3.1415-2.0")))));
-      Assert.AreEqual("3.1415 + 2",formatter.Format(parser.Parse("3.1415+(2.0)")));
+      Assert.AreEqual("3.1415 - 2", formatter.Format(parser.Parse(formatter.Format(parser.Parse("3.1415-2.0")))));
+      Assert.AreEqual("3.1415 + 2", formatter.Format(parser.Parse("3.1415+(2.0)")));
       Assert.AreEqual("3.1415 + 2", formatter.Format(parser.Parse("(3.1415+(2.0))")));
 
 
-      Assert.AreEqual("LOG(3)",formatter.Format(parser.Parse("log(3)")));
-      Assert.AreEqual("LOG(-3)",formatter.Format(parser.Parse("log(-3)")));
-      Assert.AreEqual("EXP(3)",formatter.Format(parser.Parse("exp(3)")));
-      Assert.AreEqual("EXP(-3)",formatter.Format(parser.Parse("exp(-3)")));
+      Assert.AreEqual("LOG(3)", formatter.Format(parser.Parse("log(3)")));
+      Assert.AreEqual("LOG(-3)", formatter.Format(parser.Parse("log(-3)")));
+      Assert.AreEqual("EXP(3)", formatter.Format(parser.Parse("exp(3)")));
+      Assert.AreEqual("EXP(-3)", formatter.Format(parser.Parse("exp(-3)")));
       Assert.AreEqual("SQRT(3)", formatter.Format(parser.Parse("sqrt(3)")));
 
       Assert.AreEqual("SQR(-3)", formatter.Format(parser.Parse("sqr((-3))")));
 
-      Assert.AreEqual("3 / 3 + 2 / 2 + 1 / 1",formatter.Format(parser.Parse("3/3+2/2+1/1")));
+      Assert.AreEqual("3 / 3 + 2 / 2 + 1 / 1", formatter.Format(parser.Parse("3/3+2/2+1/1")));
       Assert.AreEqual("-3 + 30 - 2 + 20 - 1 + 10", formatter.Format(parser.Parse("-3+30-2+20-1+10")));
 
@@ -79,5 +80,5 @@
 
       // signed variables / constants
-      Assert.AreEqual("-1*'x1'", formatter.Format(parser.Parse("-x1")));
+      Assert.AreEqual("-1 * 'x1'", formatter.Format(parser.Parse("-x1")));
       Assert.AreEqual("1", formatter.Format(parser.Parse("--1.0")));
       Assert.AreEqual("1", formatter.Format(parser.Parse("----1.0")));
@@ -87,51 +88,51 @@
 
 
-      Assert.AreEqual("'x1'",formatter.Format(parser.Parse("\"x1\"")));
-      Assert.AreEqual("'var name'",formatter.Format(parser.Parse("\'var name\'")));
-      Assert.AreEqual("'var name'",formatter.Format(parser.Parse("\"var name\"")));
-      Assert.AreEqual("'1'",formatter.Format(parser.Parse("\"1\"")));
-
-      Assert.AreEqual("'var \" name'",formatter.Format(parser.Parse("'var \" name\'")));
+      Assert.AreEqual("'x1'", formatter.Format(parser.Parse("\"x1\"")));
+      Assert.AreEqual("'var name'", formatter.Format(parser.Parse("\'var name\'")));
+      Assert.AreEqual("'var name'", formatter.Format(parser.Parse("\"var name\"")));
+      Assert.AreEqual("'1'", formatter.Format(parser.Parse("\"1\"")));
+
+      Assert.AreEqual("'var \" name'", formatter.Format(parser.Parse("'var \" name\'")));
       Assert.AreEqual("\"var ' name\"", formatter.Format(parser.Parse("\"var \' name\"")));
 
 
-      Assert.AreEqual("'x1' * 'x2'",formatter.Format(parser.Parse("\"x1\"*\"x2\"")));
-      Assert.AreEqual("'x1' * 'x2' + 'x3' * 'x4'",formatter.Format(parser.Parse("\"x1\"*\"x2\"+\"x3\"*\"x4\"")));
+      Assert.AreEqual("'x1' * 'x2'", formatter.Format(parser.Parse("\"x1\"*\"x2\"")));
+      Assert.AreEqual("'x1' * 'x2' + 'x3' * 'x4'", formatter.Format(parser.Parse("\"x1\"*\"x2\"+\"x3\"*\"x4\"")));
       Assert.AreEqual("'x1' * 'x2' + 'x3' * 'x4'", formatter.Format(parser.Parse("x1*x2+x3*x4")));
 
 
-      Assert.AreEqual("3 ^ 2",formatter.Format(parser.Parse("POW(3, 2)")));
-      Assert.AreEqual("3.1 ^ 2.1",formatter.Format(parser.Parse("POW(3.1, 2.1)")));
-      Assert.AreEqual("3.1 ^ 2.1",formatter.Format(parser.Parse("POW(3.1 , 2.1)")));
-      Assert.AreEqual("3.1 ^ 2.1",formatter.Format(parser.Parse("POW(3.1 ,2.1)")));
-      Assert.AreEqual("-3.1 ^ -2.1",formatter.Format(parser.Parse("POW(-3.1 , - 2.1)")));
-      Assert.AreEqual("ROOT(3, 2)",formatter.Format(parser.Parse("ROOT(3, 2)")));
-      Assert.AreEqual("ROOT(3.1, 2.1)",formatter.Format(parser.Parse("ROOT(3.1, 2.1)")));
-      Assert.AreEqual("ROOT(3.1, 2.1)",formatter.Format(parser.Parse("ROOT(3.1 , 2.1)")));
-      Assert.AreEqual("ROOT(3.1, 2.1)",formatter.Format(parser.Parse("ROOT(3.1 ,2.1)")));
+      Assert.AreEqual("3 ^ 2", formatter.Format(parser.Parse("POW(3, 2)")));
+      Assert.AreEqual("3.1 ^ 2.1", formatter.Format(parser.Parse("POW(3.1, 2.1)")));
+      Assert.AreEqual("3.1 ^ 2.1", formatter.Format(parser.Parse("POW(3.1 , 2.1)")));
+      Assert.AreEqual("3.1 ^ 2.1", formatter.Format(parser.Parse("POW(3.1 ,2.1)")));
+      Assert.AreEqual("-3.1 ^ -2.1", formatter.Format(parser.Parse("POW(-3.1 , - 2.1)")));
+      Assert.AreEqual("ROOT(3, 2)", formatter.Format(parser.Parse("ROOT(3, 2)")));
+      Assert.AreEqual("ROOT(3.1, 2.1)", formatter.Format(parser.Parse("ROOT(3.1, 2.1)")));
+      Assert.AreEqual("ROOT(3.1, 2.1)", formatter.Format(parser.Parse("ROOT(3.1 , 2.1)")));
+      Assert.AreEqual("ROOT(3.1, 2.1)", formatter.Format(parser.Parse("ROOT(3.1 ,2.1)")));
       Assert.AreEqual("ROOT(-3.1, -2.1)", formatter.Format(parser.Parse("ROOT(-3.1 , -2.1)")));
 
-      Assert.AreEqual("IF(GT(0, 1), 1, 0)",formatter.Format(parser.Parse("IF(GT( 0, 1), 1, 0)")));
-      Assert.AreEqual("IF(LT(0, 1), 1, 0)",formatter.Format(parser.Parse("IF(LT(0,1), 1 , 0)")));
-      Assert.AreEqual("LAG('x', 1)",formatter.Format(parser.Parse("LAG(x, 1)")));
-      Assert.AreEqual("LAG('x', -1)",formatter.Format(parser.Parse("LAG(x, -1)")));
-      Assert.AreEqual("LAG('x', 1)",formatter.Format(parser.Parse("LAG(x, +1)")));
+      Assert.AreEqual("IF(GT(0, 1), 1, 0)", formatter.Format(parser.Parse("IF(GT( 0, 1), 1, 0)")));
+      Assert.AreEqual("IF(LT(0, 1), 1, 0)", formatter.Format(parser.Parse("IF(LT(0,1), 1 , 0)")));
+      Assert.AreEqual("LAG('x', 1)", formatter.Format(parser.Parse("LAG(x, 1)")));
+      Assert.AreEqual("LAG('x', -1)", formatter.Format(parser.Parse("LAG(x, -1)")));
+      Assert.AreEqual("LAG('x', 1)", formatter.Format(parser.Parse("LAG(x, +1)")));
       Assert.AreEqual("'x' * LAG('x', 1)", formatter.Format(parser.Parse("x * LAG('x', +1)")));
 
       // factor variables
-      Assert.AreEqual("'x'[1] * 'y'",formatter.Format(parser.Parse("x [1.0] * y")));
-      Assert.AreEqual("'x'[1, 2] * 'y'[1, 2]",formatter.Format(parser.Parse("x [1.0, 2.0] * y [1.0, 2.0]")));
-      Assert.AreEqual("'x'[1] * 'y'",formatter.Format(parser.Parse("x[1] * y")));
-      Assert.AreEqual("'x'[1, 2] * 'y'[1, 2]",formatter.Format(parser.Parse("x[1, 2] * y [1, 2]")));
-      Assert.AreEqual("'x'[1] * 'y'",formatter.Format(parser.Parse("x [+1.0] * y")));
-      Assert.AreEqual("'x'[-1] * 'y'",formatter.Format(parser.Parse("x [-1.0] * y")));
+      Assert.AreEqual("'x'[1] * 'y'", formatter.Format(parser.Parse("x [1.0] * y")));
+      Assert.AreEqual("'x'[1, 2] * 'y'[1, 2]", formatter.Format(parser.Parse("x [1.0, 2.0] * y [1.0, 2.0]")));
+      Assert.AreEqual("'x'[1] * 'y'", formatter.Format(parser.Parse("x[1] * y")));
+      Assert.AreEqual("'x'[1, 2] * 'y'[1, 2]", formatter.Format(parser.Parse("x[1, 2] * y [1, 2]")));
+      Assert.AreEqual("'x'[1] * 'y'", formatter.Format(parser.Parse("x [+1.0] * y")));
+      Assert.AreEqual("'x'[-1] * 'y'", formatter.Format(parser.Parse("x [-1.0] * y")));
       Assert.AreEqual("'x'[-1, -2] * 'y'[1, 2]", formatter.Format(parser.Parse("x [-1.0, -2.0] * y [+1.0, +2.0]")));
 
       // one-hot for factor
-      Assert.AreEqual("'x' = 'val' * 'y'",formatter.Format(parser.Parse("x='val' * y")));
-      Assert.AreEqual("'x' = 'val'",formatter.Format(parser.Parse("x = 'val'")));
-      Assert.AreEqual("'x' = 'val'",formatter.Format(parser.Parse("x = \"val\"")));
-      Assert.AreEqual("1 * 'x' = 'val'",formatter.Format(parser.Parse("1.0 * x = val")));
-      Assert.AreEqual("-1 * 'x' = 'val'",formatter.Format(parser.Parse("-1.0 * x = val")));
+      Assert.AreEqual("'x' = 'val' * 'y'", formatter.Format(parser.Parse("x='val' * y")));
+      Assert.AreEqual("'x' = 'val'", formatter.Format(parser.Parse("x = 'val'")));
+      Assert.AreEqual("'x' = 'val'", formatter.Format(parser.Parse("x = \"val\"")));
+      Assert.AreEqual("1 * 'x' = 'val'", formatter.Format(parser.Parse("1.0 * x = val")));
+      Assert.AreEqual("-1 * 'x' = 'val'", formatter.Format(parser.Parse("-1.0 * x = val")));
       Assert.AreEqual("1 * 'x' = 'val1' + 'y' = 'val2'", formatter.Format(parser.Parse("+1.0 * \"x\" = val1 + y = \"val2\"")));
 
@@ -143,5 +144,5 @@
       Assert.AreEqual("-1", formatter.Format(parser.Parse("< num =-1.0>")));
       Assert.AreEqual("-1", formatter.Format(parser.Parse("< num = - 1.0>")));
-      
+
       // numeric parameter with sign
       Assert.AreEqual("1", formatter.Format(parser.Parse("-<num=-1.0>")));
@@ -151,5 +152,5 @@
 
       {
-        // a tree with single-arity multiplication and addition
+        // a tree with single-argument multiplication and addition
         //   ...
         //    *
@@ -173,5 +174,5 @@
         var t = new SymbolicExpressionTree(root);
 
-        Assert.AreEqual("('x1' + 'x2')", formatter.Format(t)); // TODO parenthesis not strictly required here
+        Assert.AreEqual("'x1' + 'x2'", formatter.Format(t));
       }
       {
@@ -230,5 +231,219 @@
         var t = new SymbolicExpressionTree(root);
 
-        Assert.AreEqual("SIN(('x1' + 'x2'))", formatter.Format(t)); // TODO would be better to prevent double parenthesis here
+        Assert.AreEqual("SIN('x1' + 'x2')", formatter.Format(t));
+      }
+      {
+        // single-argument subtraction
+        //    -
+        //    |
+        //    v1
+        var root = new ProgramRootSymbol().CreateTreeNode();
+        var start = new StartSymbol().CreateTreeNode();
+        var sub = new Subtraction().CreateTreeNode();
+        var var1 = (VariableTreeNode)new Variable().CreateTreeNode(); var1.VariableName = "x1"; var1.Weight = -1.0;
+        sub.AddSubtree(var1);
+        start.AddSubtree(sub);
+        root.AddSubtree(start);
+        var t = new SymbolicExpressionTree(root);
+
+        Assert.AreEqual("-(-1 * 'x1')", formatter.Format(t));       // TODO: same as --1 * 'x1' and just 'x1'
+      }
+      {
+        // single-argument subtraction
+        //    -
+        //    |
+        //    +
+        //   / \
+        //  v1 v2
+        var root = new ProgramRootSymbol().CreateTreeNode();
+        var start = new StartSymbol().CreateTreeNode();
+        var sub = new Subtraction().CreateTreeNode();
+        var add = new Addition().CreateTreeNode();
+        var var1 = (VariableTreeNode)new Variable().CreateTreeNode(); var1.VariableName = "x1"; var1.Weight = 1.0;
+        var var2 = (VariableTreeNode)new Variable().CreateTreeNode(); var2.VariableName = "x2"; var2.Weight = 1.0;
+        add.AddSubtree(var1);
+        add.AddSubtree(var2);
+        sub.AddSubtree(add);
+        start.AddSubtree(sub);
+        root.AddSubtree(start);
+        var t = new SymbolicExpressionTree(root);
+
+        Assert.AreEqual("-('x1' + 'x2')", formatter.Format(t));
+      }
+      {
+        //     ^
+        //    / \
+        //    *  v3
+        //   / \
+        //  v1 v2
+        var root = new ProgramRootSymbol().CreateTreeNode();
+        var start = new StartSymbol().CreateTreeNode();
+        var pow = new Power().CreateTreeNode();
+        var mul = new Multiplication().CreateTreeNode();
+        var var1 = (VariableTreeNode)new Variable().CreateTreeNode(); var1.VariableName = "x1"; var1.Weight = 1.0;
+        var var2 = (VariableTreeNode)new Variable().CreateTreeNode(); var2.VariableName = "x2"; var2.Weight = 1.0;
+        var var3 = (VariableTreeNode)new Variable().CreateTreeNode(); var3.VariableName = "x3"; var3.Weight = 1.0;
+        mul.AddSubtree(var1);
+        mul.AddSubtree(var2);
+        pow.AddSubtree(mul);
+        pow.AddSubtree(var3);
+        start.AddSubtree(pow);
+        root.AddSubtree(start);
+        var t = new SymbolicExpressionTree(root);
+
+        Assert.AreEqual("('x1' * 'x2') ^ 'x3'", formatter.Format(t));
+      }
+      {
+        //       ^
+        //     /   \
+        //    *     *
+        //   / \   / \
+        //  v1 v2 v3 v4
+        var root = new ProgramRootSymbol().CreateTreeNode();
+        var start = new StartSymbol().CreateTreeNode();
+        var pow = new Power().CreateTreeNode();
+        var mul1 = new Multiplication().CreateTreeNode();
+        var mul2 = new Multiplication().CreateTreeNode();
+        var var1 = (VariableTreeNode)new Variable().CreateTreeNode(); var1.VariableName = "x1"; var1.Weight = 1.0;
+        var var2 = (VariableTreeNode)new Variable().CreateTreeNode(); var2.VariableName = "x2"; var2.Weight = 1.0;
+        var var3 = (VariableTreeNode)new Variable().CreateTreeNode(); var3.VariableName = "x3"; var3.Weight = 1.0;
+        var var4 = (VariableTreeNode)new Variable().CreateTreeNode(); var4.VariableName = "x4"; var4.Weight = 1.0;
+        mul1.AddSubtree(var1);
+        mul1.AddSubtree(var2);
+        mul2.AddSubtree(var3);
+        mul2.AddSubtree(var4);
+        pow.AddSubtree(mul1);
+        pow.AddSubtree(mul2);
+        start.AddSubtree(pow);
+        root.AddSubtree(start);
+        var t = new SymbolicExpressionTree(root);
+
+        Assert.AreEqual("('x1' * 'x2') ^ ('x3' * 'x4')", formatter.Format(t));
+      }
+      {
+        //       *
+        //     /   \
+        //    *     /
+        //   / \   / \
+        //  v1 v2 v3 v4
+        var root = new ProgramRootSymbol().CreateTreeNode();
+        var start = new StartSymbol().CreateTreeNode();
+        var mul1 = new Multiplication().CreateTreeNode();
+        var mul2 = new Multiplication().CreateTreeNode();
+        var div = new Division().CreateTreeNode();
+        var var1 = (VariableTreeNode)new Variable().CreateTreeNode(); var1.VariableName = "x1"; var1.Weight = 1.0;
+        var var2 = (VariableTreeNode)new Variable().CreateTreeNode(); var2.VariableName = "x2"; var2.Weight = 1.0;
+        var var3 = (VariableTreeNode)new Variable().CreateTreeNode(); var3.VariableName = "x3"; var3.Weight = 1.0;
+        var var4 = (VariableTreeNode)new Variable().CreateTreeNode(); var4.VariableName = "x4"; var4.Weight = 1.0;
+        mul2.AddSubtree(var1);
+        mul2.AddSubtree(var2);
+        div.AddSubtree(var3);
+        div.AddSubtree(var4);
+        mul1.AddSubtree(mul2);
+        mul1.AddSubtree(div);
+        start.AddSubtree(mul1);
+        root.AddSubtree(start);
+        var t = new SymbolicExpressionTree(root);
+
+        Assert.AreEqual("'x1' * 'x2' * 'x3' / 'x4'", formatter.Format(t));     // same as x1 * x2 * (x3 / x4)
+      }
+      {
+        //       *
+        //     /   \
+        //    div  div
+        //   / \   / \
+        //  v1 v2 v3 v4
+        var root = new ProgramRootSymbol().CreateTreeNode();
+        var start = new StartSymbol().CreateTreeNode();
+        var mul = new Multiplication().CreateTreeNode();
+        var div1 = new Division().CreateTreeNode();
+        var div2 = new Division().CreateTreeNode();
+        var var1 = (VariableTreeNode)new Variable().CreateTreeNode(); var1.VariableName = "x1"; var1.Weight = 1.0;
+        var var2 = (VariableTreeNode)new Variable().CreateTreeNode(); var2.VariableName = "x2"; var2.Weight = 1.0;
+        var var3 = (VariableTreeNode)new Variable().CreateTreeNode(); var3.VariableName = "x3"; var3.Weight = 1.0;
+        var var4 = (VariableTreeNode)new Variable().CreateTreeNode(); var4.VariableName = "x4"; var4.Weight = 1.0;
+        div1.AddSubtree(var1);
+        div1.AddSubtree(var2);
+        div2.AddSubtree(var3);
+        div2.AddSubtree(var4);
+        mul.AddSubtree(div1);
+        mul.AddSubtree(div2);
+        start.AddSubtree(mul);
+        root.AddSubtree(start);
+        var t = new SymbolicExpressionTree(root);
+
+        Assert.AreEqual("'x1' / 'x2' * 'x3' / 'x4'", formatter.Format(t)); // same as x1 / x2 * (x3 / x4)
+      }
+      {
+        //      div 
+        //     /   \
+        //    div   *
+        //   / \   / \ 
+        //  v1 v2 v3 v4
+        //         
+        var root = new ProgramRootSymbol().CreateTreeNode();
+        var start = new StartSymbol().CreateTreeNode();
+        var mul = new Multiplication().CreateTreeNode();
+        var div2 = new Division().CreateTreeNode();
+        var div1 = new Division().CreateTreeNode();
+        var var1 = (VariableTreeNode)new Variable().CreateTreeNode(); var1.VariableName = "x1"; var1.Weight = 1.0;
+        var var2 = (VariableTreeNode)new Variable().CreateTreeNode(); var2.VariableName = "x2"; var2.Weight = 1.0;
+        var var3 = (VariableTreeNode)new Variable().CreateTreeNode(); var3.VariableName = "x3"; var3.Weight = 1.0;
+        var var4 = (VariableTreeNode)new Variable().CreateTreeNode(); var4.VariableName = "x4"; var4.Weight = 1.0;
+        div2.AddSubtree(var1);
+        div2.AddSubtree(var2);
+        mul.AddSubtree(var3);
+        mul.AddSubtree(var4);
+        div1.AddSubtree(div2);
+        div1.AddSubtree(mul);
+        start.AddSubtree(div1);
+        root.AddSubtree(start);
+        var t = new SymbolicExpressionTree(root);
+
+        Assert.AreEqual("'x1' / 'x2' / ('x3' * 'x4')", formatter.Format(t));
+      }
+
+      {
+        // check random trees after formatting & parsing again
+
+        var rand = new Random.MersenneTwister(1234);
+        var interpreter = new SymbolicDataAnalysisExpressionTreeInterpreter(); // use an interpreter that supports boolean functions
+        var xy = new double[100, 4];
+        for (int i = 0; i < xy.GetLength(0); i++)
+          for (int j = 0; j < xy.GetLength(1); j++)
+            xy[i, j] = rand.NextDouble();
+
+        var ds = new Dataset(new string[] { "a", "b", "c", "y" }, xy);
+        var rows = Enumerable.Range(0, xy.GetLength(1)).ToArray();
+        var grammar = new TypeCoherentExpressionGrammar();
+        grammar.ConfigureAsDefaultRegressionGrammar();
+        grammar.Symbols.OfType<Logarithm>().First().Enabled = true; // enable a function
+        grammar.Symbols.OfType<Exponential>().First().Enabled = true; // enable a function
+        grammar.Symbols.OfType<AnalyticQuotient>().First().Enabled = true; // enable a function with two arguments
+        grammar.Symbols.OfType<Power>().First().Enabled = true; // another function with two arguments
+        grammar.Symbols.OfType<And>().First().Enabled = true; // enable Boolean operators
+        grammar.Symbols.OfType<Or>().First().Enabled = true; 
+        grammar.Symbols.OfType<Xor>().First().Enabled = true;
+        grammar.Symbols.OfType<Not>().First().Enabled = true;
+        grammar.Symbols.OfType<IfThenElse>().First().Enabled = true; // enable if-then-else function
+        // test multi-argument versions of operators
+        grammar.SetSubtreeCount(grammar.Symbols.OfType<Division>().First(), 1, 4);
+        grammar.SetSubtreeCount(grammar.Symbols.OfType<Subtraction>().First(), 1, 2);
+        grammar.SetSubtreeCount(grammar.Symbols.OfType<Addition>().First(), 1, 4);
+        grammar.SetSubtreeCount(grammar.Symbols.OfType<Multiplication>().First(), 1, 4);
+        grammar.SetSubtreeCount(grammar.Symbols.OfType<And>().First(), 1, 4);
+        grammar.SetSubtreeCount(grammar.Symbols.OfType<Or>().First(), 1, 4);
+        grammar.SetSubtreeCount(grammar.Symbols.OfType<Xor>().First(), 1, 4);
+        grammar.ConfigureVariableSymbols(new RegressionProblemData(ds, new string[] { "a", "b", "c" }, "y"));
+        var fmt = new SymbolicExpressionTreeStringFormatter();
+        for (int i = 0; i < 100000; i++) {
+          var t = ProbabilisticTreeCreator.Create(rand, grammar, 15, 8);
+          var p1 = interpreter.GetSymbolicExpressionTreeValues(t, ds, rows).ToArray();
+          var p2 = interpreter.GetSymbolicExpressionTreeValues(parser.Parse(formatter.Format(t)), ds, rows).ToArray(); // test formatter, and that parser can read the expression again, and that the evaluation is the same 
+          for (int j = 0; j < p1.Length; j++) {
+            if (double.IsNaN(p1[j]) && double.IsNaN(p2[j])) continue;
+            Assert.AreEqual(p1[j], p2[j], Math.Abs(p1[j] * 1e-6), $"Problem in formatted expression:\n{formatter.Format(t)}\n{fmt.Format(t)}");
+          }
+        }
       }
     }
