Index: branches/GP.Grammar.Editor/HeuristicLab.Optimization/3.3/BatchRun.cs
===================================================================
--- branches/GP.Grammar.Editor/HeuristicLab.Optimization/3.3/BatchRun.cs	(revision 6675)
+++ branches/GP.Grammar.Editor/HeuristicLab.Optimization/3.3/BatchRun.cs	(revision 6784)
@@ -147,5 +147,7 @@
     }
 
-    private bool stopPending;
+    private bool batchRunStarted = false;
+    private bool batchRunPaused = false;
+    private bool batchRunStopped = false;
 
     public BatchRun()
@@ -158,5 +160,4 @@
       repetitionsCounter = 0;
       Runs = new RunCollection();
-      stopPending = false;
     }
     public BatchRun(string name)
@@ -168,5 +169,4 @@
       repetitionsCounter = 0;
       Runs = new RunCollection();
-      stopPending = false;
     }
     public BatchRun(string name, string description)
@@ -177,11 +177,7 @@
       repetitionsCounter = 0;
       Runs = new RunCollection();
-      stopPending = false;
     }
     [StorableConstructor]
-    private BatchRun(bool deserializing)
-      : base(deserializing) {
-      stopPending = false;
-    }
+    private BatchRun(bool deserializing) : base(deserializing) { }
     [StorableHook(HookType.AfterDeserialization)]
     private void AfterDeserialization() {
@@ -197,5 +193,7 @@
       repetitionsCounter = original.repetitionsCounter;
       runs = cloner.Clone(original.runs);
-      stopPending = original.stopPending;
+      batchRunStarted = original.batchRunStarted;
+      batchRunPaused = original.batchRunPaused;
+      batchRunStopped = original.batchRunStopped;
       Initialize();
     }
@@ -227,19 +225,32 @@
       if ((ExecutionState != ExecutionState.Prepared) && (ExecutionState != ExecutionState.Paused))
         throw new InvalidOperationException(string.Format("Start not allowed in execution state \"{0}\".", ExecutionState));
-      if (Optimizer != null) Optimizer.Start();
+      if (Optimizer == null) return;
+
+      batchRunStarted = true;
+      batchRunPaused = false;
+      batchRunStopped = false;
+      if (Optimizer.ExecutionState != ExecutionState.Prepared) Optimizer.Prepare();
+      Optimizer.Start();
     }
     public void Pause() {
       if (ExecutionState != ExecutionState.Started)
         throw new InvalidOperationException(string.Format("Pause not allowed in execution state \"{0}\".", ExecutionState));
-      if ((Optimizer != null) && (Optimizer.ExecutionState == ExecutionState.Started))
-        Optimizer.Pause();
+      if (Optimizer == null) return;
+      if (Optimizer.ExecutionState != ExecutionState.Started) return;
+
+      batchRunStarted = false;
+      batchRunPaused = true;
+      batchRunStopped = false;
+      Optimizer.Pause();
     }
     public void Stop() {
       if ((ExecutionState != ExecutionState.Started) && (ExecutionState != ExecutionState.Paused))
         throw new InvalidOperationException(string.Format("Stop not allowed in execution state \"{0}\".", ExecutionState));
-      stopPending = true;
-      if ((Optimizer != null) &&
-          ((Optimizer.ExecutionState == ExecutionState.Started) || (Optimizer.ExecutionState == ExecutionState.Paused)))
-        Optimizer.Stop();
+      if (Optimizer == null) return;
+      if (Optimizer.ExecutionState != ExecutionState.Started && Optimizer.ExecutionState != ExecutionState.Paused) return;
+      batchRunStarted = false;
+      batchRunPaused = false;
+      batchRunStopped = true;
+      Optimizer.Stop();
     }
 
@@ -324,12 +335,17 @@
     }
     private void Optimizer_Paused(object sender, EventArgs e) {
-      OnPaused();
+      if (ExecutionState == ExecutionState.Started) {
+        batchRunStarted = false;
+        batchRunPaused = true;
+        batchRunStopped = false;
+        OnPaused();
+      }
     }
     private void Optimizer_Prepared(object sender, EventArgs e) {
-      if ((ExecutionState == ExecutionState.Paused) || (ExecutionState == ExecutionState.Stopped))
+      if (ExecutionState == ExecutionState.Stopped || !batchRunStarted) {
         OnPrepared();
+      }
     }
     private void Optimizer_Started(object sender, EventArgs e) {
-      stopPending = false;
       if (ExecutionState != ExecutionState.Started)
         OnStarted();
@@ -338,10 +354,11 @@
       repetitionsCounter++;
 
-      if (!stopPending && (repetitionsCounter < repetitions)) {
+      if (batchRunStopped) OnStopped();
+      else if (repetitionsCounter >= repetitions) OnStopped();
+      else if (batchRunPaused) OnPaused();
+      else if (batchRunStarted) {
         Optimizer.Prepare();
         Optimizer.Start();
-      } else {
-        OnStopped();
-      }
+      } else OnStopped();
     }
     private void Optimizer_Runs_CollectionReset(object sender, CollectionItemsChangedEventArgs<IRun> e) {
Index: branches/GP.Grammar.Editor/HeuristicLab.Optimization/3.3/Calculator.cs
===================================================================
--- branches/GP.Grammar.Editor/HeuristicLab.Optimization/3.3/Calculator.cs	(revision 6675)
+++ branches/GP.Grammar.Editor/HeuristicLab.Optimization/3.3/Calculator.cs	(revision 6784)
@@ -1,5 +1,4 @@
 ﻿using System;
 using System.Collections.Generic;
-using System.Drawing;
 using System.Linq;
 using System.Text.RegularExpressions;
@@ -7,15 +6,14 @@
 using HeuristicLab.Core;
 using HeuristicLab.Data;
-using HeuristicLab.Parameters;
 using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
 using System.Globalization;
 
-namespace HeuristicLab.Optimization {  
-    
+namespace HeuristicLab.Optimization {
+
   [StorableClass]
   public class Calculator : IDeepCloneable {
 
     #region Fields & Properties
-    
+
     private List<string> tokens;
 
@@ -26,5 +24,6 @@
     }
 
-    private static readonly Regex TokenRegex = new Regex(@"[a-zA-Z0-9._]+|""([^""]|\"")+""|[-+*/^]|log");    
+    private static readonly Regex TokenRegex =
+      new Regex(@"[a-zA-Z0-9._]+|""([^""]|\\"")*""|'([^']|\\')*'|[-+*/<>^]|dup|swap|drop|log|true|false|if|==|not|isnull|null|ismatch|rename");
 
     #endregion
@@ -35,6 +34,7 @@
     protected Calculator(bool deserializing) { }
     public Calculator() { }
-    public Calculator(Calculator original, Cloner cloner) {
-      this.tokens = original.tokens.ToList();
+    protected Calculator(Calculator original, Cloner cloner) {
+      cloner.RegisterClonedObject(original, this);
+      tokens = original.tokens.ToList();
     }
     public IDeepCloneable Clone(Cloner cloner) {
@@ -46,61 +46,174 @@
     #endregion
 
-    public IEnumerable<string> Tokenize(string s) {
+    private static IEnumerable<string> Tokenize(string s) {
       return TokenRegex.Matches(s).Cast<Match>().Select(m => m.Value);
     }
 
-    private double GetVariableValue(IDictionary<string, IItem> variables, string name) {
-      if (variables.ContainsKey(name)) {
-        var item = variables[name];
-        var intValue = item as IntValue;
-        if (intValue != null) {
-          return intValue.Value;
-        } else {
-          var doubleValue = item as DoubleValue;
-          if (doubleValue != null)
-            return doubleValue.Value;
-          else
-            throw new InvalidOperationException("Non numerical argument");
-        }
-      } else {
-        throw new InvalidOperationException(string.Format("variable \"{0}\" not found", name));
-      }
-    }
-
     public IItem GetValue(IDictionary<string, IItem> variables) {
-      var stack = new Stack<double>();
-      Action<Func<double, double, double>> binf = op => {
-        var b = stack.Pop();
-        stack.Push(op(stack.Pop(), b));
-      };
-      try {
-        foreach (var token in tokens) {
+      var stack = new Stack<object>();
+      int i = 0;
+      try {
+        for (; i<tokens.Count; i++) {
+          var token = tokens[i];
           double d;
-          if (double.TryParse(token,
-                NumberStyles.AllowDecimalPoint |
-              NumberStyles.AllowExponent |
-              NumberStyles.AllowLeadingSign, CultureInfo.InvariantCulture, out d)) {
+          if (TryParse(token, out d)) {
             stack.Push(d);
           } else if (token.StartsWith("\"")) {
             stack.Push(GetVariableValue(variables, token.Substring(1, token.Length - 2).Replace(@"\""", @"""")));
+          } else if (token.StartsWith("'")) {
+            stack.Push(token.Substring(1, token.Length-2).Replace("\'", "'"));
           } else {
-            switch (token) {
-              case "log": stack.Push(Math.Log(stack.Pop())); break;
-              case "+": binf((x, y) => x + y); break;
-              case "-": binf((x, y) => x - y); break;
-              case "*": binf((x, y) => x * y); break;
-              case "/": binf((x, y) => x / y); break;
-              case "^": binf(Math.Pow); break;
-              default: stack.Push(GetVariableValue(variables, token)); break;
-            }
+            Apply(token, stack, variables);
           }
         }
-      } catch (InvalidOperationException x) {
-        return new StringValue(string.Format("Calculation Failed: {0}", x.Message));
-      }
-      if (stack.Count != 1)
-        return new StringValue("Invalid final evaluation stack size != 1");
-      return new DoubleValue(stack.Pop());      
-    }    
+      } catch (Exception x) {
+        throw new Exception(string.Format(
+          "Calculation Failed at token #{1}: '{2}' {0}current stack is: {0}{3}", Environment.NewLine,
+          i, tokens[i], string.Join(Environment.NewLine, stack.Select(AsString))),
+          x);
+      }
+      if (stack.Count != 1) throw new Exception(string.Format("Invalid final evaluation stack size {0} (should be 1)", stack.Count));
+      var result = stack.Pop();
+      if (result is string) return new StringValue((string)result);
+      if (result is double) return new DoubleValue((double)result);
+      if (result is bool) return new BoolValue((bool)result);
+      return null;
+    }
+
+    private static void Apply(string token, Stack<object> stack, IDictionary<string, IItem> variables) {
+      switch (token) {
+        case "null":  stack.Push(null); break;
+        case "true":  stack.Push(true); break;
+        case "false": stack.Push(false); break;
+
+        case "drop": stack.Pop(); break;
+        case "dup": stack.Push(stack.Peek()); break;
+        case "swap":
+          var top = stack.Pop();
+          var next = stack.Pop();
+          stack.Push(top);
+          stack.Push(next);
+          break;
+
+        case "log": Apply(stack, x => Math.Log((double)x)); break;
+        case "+": Apply(stack, (x, y) => (double)x + (double)y); break;
+        case "-": Apply(stack, (x, y) => (double)x - (double)y); break;
+        case "*": Apply(stack, (x, y) => (double)x * (double)y); break;
+        case "/": Apply(stack, (x, y) => (double)x / (double)y); break;
+        case "^": Apply(stack, (x, y) => Math.Pow((double)x, (double)y)); break;
+        case "<": Apply(stack, (x, y) => (double)x < (double)y); break;
+        case ">": Apply(stack, (x, y) => (double)x > (double)y); break;
+
+        case "==": Apply(stack, (x, y) => Equal(x, y)); break;
+        case "not": Apply(stack, x => !(bool)x); break;
+        case "isnull": Apply(stack, x => x == null); break;
+        case "if": Apply(stack, (then, else_, cond) => (bool)cond ? then : else_); break;
+
+        case "ismatch": Apply(stack, (s, p) => new Regex((string)p).IsMatch((string)s)); break;
+        case "rename": Apply(stack, (s, p, r) => new Regex((string)p).Replace((string)s, (string)r)); break;
+
+        default: stack.Push(GetVariableValue(variables, token)); break;
+      }
+    }
+
+    #region Auxiliary Functions
+
+    #region IItem value conversion
+    private static object GetIntValue(IItem value) {
+      var v = value as IntValue;
+      if (v != null) return (double)v.Value;
+      return null;
+    }
+
+    private static object GetDoubleValue(IItem value) {
+      var v = value as DoubleValue;
+      if (v != null) return v.Value;
+      return null;
+    }
+
+    private static object GetBoolValue(IItem value) {
+      var v = value as BoolValue;
+      if (v != null) return v.Value;
+      return null;
+    }
+
+    private static object GetVariableValue(IDictionary<string, IItem> variables, string name) {
+      if (variables.ContainsKey(name)) {
+        var item = variables[name];
+        return
+          GetIntValue(item) ??
+          GetDoubleValue(item) ??
+          GetBoolValue(item) ??
+          item.ToString();
+      }
+      return null;
+    }
+    #endregion
+
+    #region variadic equality
+    private static bool Equal(object a, object b) { return EqualNumber(a, b) || EqualBool(a, b) || EqualString(a, b) || a == b; }
+    private static bool EqualNumber(object a, object b) { return a is double && b is double && (double)a == (double)b; }
+    private static bool EqualBool(object a, object b) { return a is bool && b is bool && (bool)a == (bool)b; }
+    private static bool EqualString(object a, object b) { return a is string && b is string && ((string)a).Equals((string)b); }
+    #endregion
+
+    #region stack calculation
+    private static void Apply(Stack<object> stack, Func<object, object> func) {
+      if (stack.Count < 1)
+        throw new InvalidOperationException("Stack is empty");
+      var a = stack.Pop();
+      try {
+        stack.Push(func(a));
+      } catch (Exception) {
+        stack.Push(a);
+        throw;
+      }
+    }
+
+    private static void Apply(Stack<object> stack, Func<object, object, object> func) {
+      if (stack.Count < 2)
+        throw new InvalidOperationException("Stack contains less than two elements");
+      var b = stack.Pop();
+      var a = stack.Pop();
+      try {
+        stack.Push(func(a, b));
+      } catch (Exception) {
+        stack.Push(b);
+        stack.Push(a);
+        throw;
+      }
+    }
+
+    private static void Apply(Stack<object> stack, Func<object, object, object, object> func) {
+      if (stack.Count < 3)
+        throw new InvalidOperationException("Stack contains less than three elements");
+      var c = stack.Pop();
+      var b = stack.Pop();
+      var a = stack.Pop();
+      try {
+        stack.Push(func(a, b, c));
+      } catch (Exception) {
+        stack.Push(a);
+        stack.Push(b);
+        stack.Push(c);
+        throw;
+      }
+    }
+    #endregion
+
+    private static bool TryParse(string token, out double d) {
+      return double.TryParse(token,
+                             NumberStyles.AllowDecimalPoint |
+                             NumberStyles.AllowExponent |
+                             NumberStyles.AllowLeadingSign, CultureInfo.InvariantCulture, out d);
+    }
+
+    private static string AsString(object o) {
+      if (o == null) return "null";
+      if (o is string) return string.Format("'{0}'", o);
+      return o.ToString();
+    }
+
+    #endregion
   }
 }
Index: branches/GP.Grammar.Editor/HeuristicLab.Optimization/3.3/Experiment.cs
===================================================================
--- branches/GP.Grammar.Editor/HeuristicLab.Optimization/3.3/Experiment.cs	(revision 6675)
+++ branches/GP.Grammar.Editor/HeuristicLab.Optimization/3.3/Experiment.cs	(revision 6784)
@@ -104,5 +104,6 @@
     }
 
-    private bool stopPending;
+    private bool experimentStarted = false;
+    private bool experimentStopped = false;
 
     public Experiment()
@@ -114,5 +115,4 @@
       optimizers = new OptimizerList();
       Runs = new RunCollection();
-      stopPending = false;
       Initialize();
     }
@@ -124,5 +124,4 @@
       optimizers = new OptimizerList();
       Runs = new RunCollection();
-      stopPending = false;
       Initialize();
     }
@@ -133,12 +132,8 @@
       optimizers = new OptimizerList();
       Runs = new RunCollection();
-      stopPending = false;
       Initialize();
     }
     [StorableConstructor]
-    private Experiment(bool deserializing)
-      : base(deserializing) {
-      stopPending = false;
-    }
+    private Experiment(bool deserializing) : base(deserializing) { }
     [StorableHook(HookType.AfterDeserialization)]
     private void AfterDeserialization() {
@@ -151,5 +146,7 @@
       optimizers = cloner.Clone(original.optimizers);
       runs = cloner.Clone(original.runs);
-      stopPending = original.stopPending;
+
+      experimentStarted = original.experimentStarted;
+      experimentStopped = original.experimentStopped;
       Initialize();
     }
@@ -172,35 +169,40 @@
       if ((ExecutionState != ExecutionState.Prepared) && (ExecutionState != ExecutionState.Paused) && (ExecutionState != ExecutionState.Stopped))
         throw new InvalidOperationException(string.Format("Prepare not allowed in execution state \"{0}\".", ExecutionState));
-      if (Optimizers.Count > 0) {
-        if (clearRuns) runs.Clear();
-        foreach (IOptimizer optimizer in Optimizers.Where(x => x.ExecutionState != ExecutionState.Started))
+      if (Optimizers.Count == 0) return;
+
+      if (clearRuns) runs.Clear();
+      foreach (IOptimizer optimizer in Optimizers.Where(x => x.ExecutionState != ExecutionState.Started))
+        if (clearRuns || optimizer.ExecutionState != ExecutionState.Prepared)
           optimizer.Prepare(clearRuns);
-      }
     }
     public void Start() {
       if ((ExecutionState != ExecutionState.Prepared) && (ExecutionState != ExecutionState.Paused))
         throw new InvalidOperationException(string.Format("Start not allowed in execution state \"{0}\".", ExecutionState));
-      stopPending = false;
-      if (Optimizers.Count > 0) {
-        IOptimizer optimizer = Optimizers.FirstOrDefault(x => (x.ExecutionState == ExecutionState.Prepared) || (x.ExecutionState == ExecutionState.Paused));
-        if (optimizer != null) optimizer.Start();
-      }
+      if (Optimizers.Count == 0) return;
+
+      experimentStarted = true;
+      experimentStopped = false;
+      IOptimizer optimizer = Optimizers.FirstOrDefault(x => (x.ExecutionState == ExecutionState.Prepared) || (x.ExecutionState == ExecutionState.Paused));
+      if (optimizer != null) optimizer.Start();
     }
     public void Pause() {
       if (ExecutionState != ExecutionState.Started)
         throw new InvalidOperationException(string.Format("Pause not allowed in execution state \"{0}\".", ExecutionState));
-      if (Optimizers.Count > 0) {
-        foreach (IOptimizer optimizer in Optimizers.Where(x => x.ExecutionState == ExecutionState.Started))
-          optimizer.Pause();
-      }
+      if (Optimizers.Count == 0) return;
+
+      experimentStarted = false;
+      experimentStopped = false;
+      foreach (IOptimizer optimizer in Optimizers.Where(x => x.ExecutionState == ExecutionState.Started))
+        optimizer.Pause();
     }
     public void Stop() {
       if ((ExecutionState != ExecutionState.Started) && (ExecutionState != ExecutionState.Paused))
         throw new InvalidOperationException(string.Format("Stop not allowed in execution state \"{0}\".", ExecutionState));
-      stopPending = true;
-      if (Optimizers.Count > 0) {
-        foreach (IOptimizer optimizer in Optimizers.Where(x => (x.ExecutionState == ExecutionState.Started) || (x.ExecutionState == ExecutionState.Paused)))
-          optimizer.Stop();
-      }
+      if (Optimizers.Count == 0) return;
+
+      experimentStarted = false;
+      experimentStopped = true;
+      foreach (IOptimizer optimizer in Optimizers.Where(x => (x.ExecutionState == ExecutionState.Started) || (x.ExecutionState == ExecutionState.Paused)))
+        optimizer.Stop();
     }
 
@@ -333,11 +335,9 @@
     }
     private void optimizer_Stopped(object sender, EventArgs e) {
-      if (!stopPending && Optimizers.Any(x => (x.ExecutionState == ExecutionState.Prepared) || (x.ExecutionState == ExecutionState.Paused))) {
+      if (experimentStopped && Optimizers.All(o => o.ExecutionState == ExecutionState.Stopped || o.ExecutionState == ExecutionState.Prepared)) OnStopped();
+      else if (Optimizers.All(o => o.ExecutionState == ExecutionState.Stopped)) OnStopped();
+      else if (Optimizers.Any(o => o.ExecutionState == ExecutionState.Paused) && Optimizers.All(o => o.ExecutionState != ExecutionState.Started)) OnPaused();
+      else if (experimentStarted && !experimentStopped && Optimizers.Any(x => (x.ExecutionState == ExecutionState.Prepared) || (x.ExecutionState == ExecutionState.Paused))) {
         Optimizers.First(x => (x.ExecutionState == ExecutionState.Prepared) || (x.ExecutionState == ExecutionState.Paused)).Start();
-      } else {
-        if (Optimizers.All(x => (x.ExecutionState != ExecutionState.Started) && (x.ExecutionState != ExecutionState.Paused))) {
-          stopPending = false;
-          OnStopped();
-        }
       }
     }
Index: branches/GP.Grammar.Editor/HeuristicLab.Optimization/3.3/HeuristicLab.Optimization-3.3.csproj
===================================================================
--- branches/GP.Grammar.Editor/HeuristicLab.Optimization/3.3/HeuristicLab.Optimization-3.3.csproj	(revision 6675)
+++ branches/GP.Grammar.Editor/HeuristicLab.Optimization/3.3/HeuristicLab.Optimization-3.3.csproj	(revision 6784)
@@ -111,4 +111,7 @@
   </ItemGroup>
   <ItemGroup>
+    <Compile Include="RunCollectionDiscretizer.cs" />
+    <Compile Include="RunCollectionGroupCreater.cs" />
+    <Compile Include="RunCollectionSorter.cs" />
     <Compile Include="RunCollectionValueRemover.cs" />
     <Compile Include="IRunCollectionModifier.cs" />
@@ -140,6 +143,4 @@
     <Compile Include="Problems\Problem.cs" />
     <Compile Include="RunCollectionFormulaModifer.cs" />
-    <Compile Include="RunCollectionFuzzifier.cs" />
-    <Compile Include="RunCollectionModificationEvaluator.cs" />
     <Compile Include="RunCollectionConstraints\RunCollectionComparisonConstraint.cs" />
     <Compile Include="RunCollectionConstraints\RunCollectionConstraintCollection.cs" />
Index: branches/GP.Grammar.Editor/HeuristicLab.Optimization/3.3/RunCollection.cs
===================================================================
--- branches/GP.Grammar.Editor/HeuristicLab.Optimization/3.3/RunCollection.cs	(revision 6675)
+++ branches/GP.Grammar.Editor/HeuristicLab.Optimization/3.3/RunCollection.cs	(revision 6784)
@@ -51,4 +51,5 @@
 
       constraints = new RunCollectionConstraintCollection(original.constraints.Select(x => cloner.Clone(x)));
+      modifiers = new CheckedItemList<IRunCollectionModifier>(original.modifiers.Select(cloner.Clone));
       foreach (IRunCollectionConstraint constraint in constraints)
         constraint.ConstrainedValue = this;
@@ -67,4 +68,5 @@
       dataTypes = new Dictionary<string, HashSet<Type>>();
       constraints = new RunCollectionConstraintCollection();
+      modifiers = new CheckedItemList<IRunCollectionModifier>();
       RegisterConstraintsEvents();
     }
@@ -83,4 +85,11 @@
       get { return constraints; }
     }
+
+    [Storable]
+    private CheckedItemList<IRunCollectionModifier> modifiers;
+    public CheckedItemList<IRunCollectionModifier> Modifiers {
+      get { return modifiers; }
+    }
+
 
     private bool updateOfRunsInProgress;
@@ -98,4 +107,5 @@
     private void AfterDeserialization() {
       if (constraints == null) constraints = new RunCollectionConstraintCollection();
+      if (modifiers == null) modifiers = new CheckedItemList<IRunCollectionModifier>();
       RegisterConstraintsEvents();
       RegisterConstraintEvents(constraints);
@@ -393,4 +403,43 @@
     }
     #endregion
+
+    #region Modification
+    public void Modify() {
+      UpdateOfRunsInProgress = true;
+      var runs = this.ToList();
+      var selectedRuns = runs.Where(r => r.Visible).ToList();
+      int nSelected = selectedRuns.Count;
+      if (nSelected > 0) {
+        foreach (var modifier in Modifiers.CheckedItems)
+          modifier.Value.Modify(selectedRuns);
+        if (nSelected != selectedRuns.Count || HaveDifferentOrder(selectedRuns, runs.Where(r => r.Visible))) {
+          Clear();
+          AddRange(ReplaceVisibleRuns(runs, selectedRuns));
+        } else if (runs.Count > 0) {
+          OnCollectionReset(this, runs);
+        }
+      }
+      UpdateOfRunsInProgress = false;
+    }
+
+    private static IEnumerable<IRun> ReplaceVisibleRuns(IEnumerable<IRun> runs, IEnumerable<IRun> visibleRuns) {
+      var newRuns = new List<IRun>();
+      var runIt = runs.GetEnumerator();
+      var visibleRunIt = visibleRuns.GetEnumerator();
+      while (runIt.MoveNext()) {
+        if (runIt.Current != null && !runIt.Current.Visible)
+          newRuns.Add(runIt.Current);
+        else if (visibleRunIt.MoveNext())
+          newRuns.Add(visibleRunIt.Current);
+      }
+      while (visibleRunIt.MoveNext())
+        newRuns.Add(visibleRunIt.Current);
+      return newRuns;
+    }
+
+    private static bool HaveDifferentOrder(IEnumerable<IRun> l1, IEnumerable<IRun> l2) {
+      return l1.Zip(l2, (r1, r2) => r1 != r2).Any();
+    }
+    #endregion
   }
 }
Index: branches/GP.Grammar.Editor/HeuristicLab.Optimization/3.3/RunCollectionConstraints/RunCollectionEqualityConstraint.cs
===================================================================
--- branches/GP.Grammar.Editor/HeuristicLab.Optimization/3.3/RunCollectionConstraints/RunCollectionEqualityConstraint.cs	(revision 6675)
+++ branches/GP.Grammar.Editor/HeuristicLab.Optimization/3.3/RunCollectionConstraints/RunCollectionEqualityConstraint.cs	(revision 6784)
@@ -100,5 +100,5 @@
       foreach (IRun run in ConstrainedValue.Where(r => r.Visible)) {
         IItem item = ConstrainedValue.GetValue(run, constraintColumn);
-        if (!base.Check(item.ToString()))
+        if (item != null && !base.Check(item.ToString()))
           run.Visible = false;
       }
Index: branches/GP.Grammar.Editor/HeuristicLab.Optimization/3.3/RunCollectionDiscretizer.cs
===================================================================
--- branches/GP.Grammar.Editor/HeuristicLab.Optimization/3.3/RunCollectionDiscretizer.cs	(revision 6784)
+++ branches/GP.Grammar.Editor/HeuristicLab.Optimization/3.3/RunCollectionDiscretizer.cs	(revision 6784)
@@ -0,0 +1,161 @@
+﻿using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Data;
+using HeuristicLab.Parameters;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Optimization {
+  [Item("RunCollection Discretizer", "Creates several levels from the distribution of a certain result accross a run collection and assignes a discretized value.")]
+  [StorableClass]
+  public class RunCollectionDiscretizer : ParameterizedNamedItem, IRunCollectionModifier {
+
+    public override bool CanChangeName { get { return false; } }
+    public override bool CanChangeDescription { get { return false; } }
+
+    #region Parameters
+    public ValueParameter<StringValue> SourceParameter {
+      get { return (ValueParameter<StringValue>)Parameters["Source"]; }
+    }
+    public ValueParameter<StringValue> TargetParameter {
+      get { return (ValueParameter<StringValue>)Parameters["Target"]; }
+    }
+    public ValueParameter<DoubleValue> SpreadParameter {
+      get { return (ValueParameter<DoubleValue>)Parameters["Spread"]; }
+    }
+    public ValueParameter<StringValue> GroupByParameter {
+      get { return (ValueParameter<StringValue>)Parameters["GroupBy"]; }
+    }    
+    public ValueParameter<ItemList<StringValue>> LevelsParameter {
+      get { return (ValueParameter<ItemList<StringValue>>)Parameters["Levels"]; }
+    }    
+    #endregion
+
+    private string Source { get { return SourceParameter.Value.Value; } }
+    private string Target { get { return TargetParameter.Value.Value; } }    
+    private double Spread { get { return SpreadParameter.Value.Value; } }
+    private string GroupBy { get { return GroupByParameter.Value.Value; } }
+    private List<string> Levels { get { return LevelsParameter.Value.Select(v => v.Value).ToList(); } }
+
+      #region Construction & Cloning
+    [StorableConstructor]
+    protected RunCollectionDiscretizer(bool deserializing) : base(deserializing) { }
+    protected RunCollectionDiscretizer(RunCollectionDiscretizer original, Cloner cloner)
+      : base(original, cloner) {
+      RegisterEvents();
+    }
+    public RunCollectionDiscretizer() {
+      Parameters.Add(new ValueParameter<StringValue>("Source", "Source value name to be fuzzified.", new StringValue("Value")));
+      Parameters.Add(new ValueParameter<StringValue>("Target", "Target value name. The new, fuzzified variable to be created.", new StringValue("Calc.Value")));      
+      Parameters.Add(new ValueParameter<DoubleValue>("Spread", "The number of standard deviations considered one additional level.", new DoubleValue(1)));
+      Parameters.Add(new ValueParameter<StringValue>("GroupBy", "Create separate analyzes for different values of this variable.", new StringValue("")));
+      Parameters.Add(new ValueParameter<ItemList<StringValue>>("Levels", "The list of levels to be assigned.",
+        new ItemList<StringValue> {
+          new StringValue("Very Low"),
+          new StringValue("Low"),
+          new StringValue("Average"),
+          new StringValue("High"),
+          new StringValue("Very High"),
+        }));     
+      RegisterEvents();
+      UpdateName();
+    }
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new RunCollectionDiscretizer(this, cloner);
+    }
+    [StorableHook(HookType.AfterDeserialization)]
+    private void AfterDeserialization() {
+      RegisterEvents();
+    }
+    #endregion
+
+    private void RegisterEvents() {
+      SourceParameter.ToStringChanged += Parameter_NameChanged;
+      TargetParameter.ToStringChanged += Parameter_NameChanged;      
+      GroupByParameter.ToStringChanged += Parameter_NameChanged;
+    }
+
+    private void Parameter_NameChanged(object sender, EventArgs e) {
+      UpdateName();
+    }
+
+    private void UpdateName() {
+      name = string.Format("{0} := Discrete({1}{2})",
+        Target,
+        Source,
+        string.IsNullOrWhiteSpace(GroupBy) ? "" : string.Format("/{0}", GroupBy));        
+      OnNameChanged();
+    }
+
+    #region IRunCollectionModifier Members
+
+    public void Modify(List<IRun> runs) {
+      foreach (var group in runs
+        .Select(r => new {Run=r, Value=GetSourceValue(r)})
+        .Where(r => r.Value.HasValue && !double.IsNaN(r.Value.Value) && !double.IsInfinity(r.Value.Value))
+        .Select(r => new {r.Run, r.Value.Value, Bin=GetGroupByValue(r.Run)})
+        .GroupBy(r => r.Bin).ToList()) {
+        var values = group.Select(r => r.Value).ToList();
+        if (values.Count > 0) {
+          var avg = values.Average();
+          var stdDev = values.StandardDeviation();
+          foreach (var r in group) {
+            r.Run.Results[Target] = new StringValue(Fuzzify(r.Value, avg, stdDev));
+          }
+        }
+      }      
+    }
+
+    private double? GetSourceValue(IRun run) {
+      return CastSourceValue(run.Results) ?? CastSourceValue(run.Parameters);
+    }
+
+    private string GetGroupByValue(IRun run) {
+      if (string.IsNullOrWhiteSpace(GroupBy))
+        return String.Empty;
+      IItem value;
+      run.Results.TryGetValue(GroupBy, out value);
+      if (value == null)
+        run.Parameters.TryGetValue(GroupBy, out value);
+      if (value != null)
+        return value.ToString();
+      else
+        return String.Empty;
+    }
+
+    private double? CastSourceValue(IDictionary<string, IItem> variables) {
+      IItem value;
+      variables.TryGetValue(Source, out value);
+      var intValue = value as IntValue;
+      if (intValue != null) {
+        return intValue.Value;
+      } else {
+        var doubleValue = value as DoubleValue;
+        if (doubleValue != null)
+          return doubleValue.Value;
+      }
+      return null;
+    }
+
+    private string Fuzzify(double value, double avg, double stdDev) {
+      double dev = (value - avg)/(stdDev*Spread);
+      int index;
+      if (Levels.Count % 2 == 1) {
+        index = (int) Math.Floor(Math.Abs(dev));
+        index = (Levels.Count - 1)/2 + Math.Sign(dev) * index;
+      } else {
+        index = (int) Math.Ceiling(Math.Abs(dev));
+        if (dev > 0)
+          index = Levels.Count/2 + index;
+        else
+          index = Levels.Count/2 + 1 - index;
+      }
+      return Levels[Math.Min(Levels.Count - 1, Math.Max(0, index))];
+    }
+
+    #endregion
+  }
+}
Index: branches/GP.Grammar.Editor/HeuristicLab.Optimization/3.3/RunCollectionFormulaModifer.cs
===================================================================
--- branches/GP.Grammar.Editor/HeuristicLab.Optimization/3.3/RunCollectionFormulaModifer.cs	(revision 6675)
+++ branches/GP.Grammar.Editor/HeuristicLab.Optimization/3.3/RunCollectionFormulaModifer.cs	(revision 6784)
@@ -20,11 +20,14 @@
     public ValueParameter<StringValue> ResultNameParameter {
       get { return (ValueParameter<StringValue>)Parameters["ResultName"]; }
-    }    
-    
+    }
+
     public ValueParameter<StringValue> FormulaParameter {
       get { return (ValueParameter<StringValue>)Parameters["Formula"]; }
-    }    
+    }
 
-    #region Construction & Cloning    
+    private string ResultName { get { return ResultNameParameter.Value.Value; } }
+    private string Formula { get { return FormulaParameter.Value.Value; } }
+
+    #region Construction & Cloning
     [StorableConstructor]
     protected RunCollectionFormulaModifer(bool deserializing) : base(deserializing) { }
@@ -35,5 +38,28 @@
     public RunCollectionFormulaModifer() {
       Parameters.Add(new ValueParameter<StringValue>("ResultName", "The name of the result to be generated by this formula.", new StringValue("Calc.Value")));
-      Parameters.Add(new ValueParameter<StringValue>("Formula", "RPN formula for new value. This can contain existing run parameters or results (optionally in quotes if they contain whitespace), numbers and arithmetic operators in postfix notation.", new StringValue("1 1 +")));
+      Parameters.Add(new ValueParameter<StringValue>("Formula",
+@"RPN formula for new value in postfix notation.
+
+This can contain the following elements:
+
+literals:
+  numbers, true, false, null and strings in single quotes
+variables (run parameters or results):
+  unquoted or in double quotes if they contain special characters or whitespace
+mathematical functions:
+  +, -, /, ^ (power), log
+predicates:
+  ==, <, >, isnull, not
+stack manipulation:
+  drop swap dup
+string matching:
+  <string> <pattern> ismatch
+string replacing:
+  <string> <pattern> <replacement> rename
+conditionals:
+  <then> <else> <condition> if
+
+If the final value is null, the result variable is removed if it exists.",
+        new StringValue("1 1 +")));
       UpdateName();
       RegisterEvents();
@@ -58,10 +84,10 @@
 
     private void UpdateName() {
-      name = string.Format("{0} := {1}", ResultNameParameter.Value.Value, FormulaParameter.Value.Value);
+      name = string.Format("{0} := {1}", ResultName, Formula);
       OnNameChanged();
     }
 
     public void Modify(List<IRun> runs) {
-      var calc = new Calculator {Formula = FormulaParameter.Value.Value};      
+      var calc = new Calculator { Formula = Formula };
       foreach (var run in runs) {
         var variables = new Dictionary<string, IItem>();
@@ -70,8 +96,12 @@
         foreach (var result in run.Results)
           variables[result.Key] = result.Value;
-        run.Results[ResultNameParameter.Value.Value] = calc.GetValue(variables);        
-      }      
+        var value = calc.GetValue(variables);
+        if (value != null)
+          run.Results[ResultName] = value;
+        else
+          run.Results.Remove(ResultName);
+      }
     }
-    
+
   }
 }
Index: branches/GP.Grammar.Editor/HeuristicLab.Optimization/3.3/RunCollectionGroupCreater.cs
===================================================================
--- branches/GP.Grammar.Editor/HeuristicLab.Optimization/3.3/RunCollectionGroupCreater.cs	(revision 6784)
+++ branches/GP.Grammar.Editor/HeuristicLab.Optimization/3.3/RunCollectionGroupCreater.cs	(revision 6784)
@@ -0,0 +1,84 @@
+﻿using System;
+using System.Collections.Generic;
+using System.Linq;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Data;
+using HeuristicLab.Parameters;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Optimization {
+
+  [Item("RunCollection Group Creater", "Regroups existing runs according to equal values in GroupBy and prefixes them according to their value in Prefix.")]
+  [StorableClass]
+  public class RunCollectionGroupCreater : ParameterizedNamedItem, IRunCollectionModifier {
+    
+    public ValueParameter<ItemCollection<StringValue>> GroupByParameter {
+      get { return (ValueParameter<ItemCollection<StringValue>>)Parameters["GroupBy"]; }
+    }
+
+    public ValueParameter<StringValue> PrefixParameter {
+      get { return (ValueParameter<StringValue>)Parameters["Prefix"]; }
+    }
+
+    private IEnumerable<string> GroupBy { get { return GroupByParameter.Value.Select(v => v.Value); } }
+    private string Prefix { get { return PrefixParameter.Value.Value; } }
+
+    #region Construction & Cloning
+    [StorableConstructor]
+    protected RunCollectionGroupCreater(bool deserializing) : base(deserializing) { }
+    protected RunCollectionGroupCreater(RunCollectionGroupCreater original, Cloner cloner) : base(original, cloner) { }
+    public RunCollectionGroupCreater() {
+      Parameters.Add(new ValueParameter<ItemCollection<StringValue>>("GroupBy", "The variable that has to be the same for all members of a group.",
+        new ItemCollection<StringValue>(new[] { new StringValue("Problem Name") })));
+      Parameters.Add(new ValueParameter<StringValue>("Prefix", "The distinguishing prefix values for the individual runs.",
+        new StringValue("Algorithm Name")));      
+    }
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new RunCollectionGroupCreater(this, cloner);
+    }
+    #endregion    
+
+    private static string GetStringValue(string name, IRun r) {
+      IItem item;
+      r.Results.TryGetValue(name, out item);
+      if (item != null)
+        return item.ToString();
+      r.Parameters.TryGetValue(name, out item);
+      return item != null ? item.ToString() : "<none>";
+    }
+
+    private static string GetValues(IEnumerable<string> names, IRun r) {
+      return string.Join("/", names.Select(n => GetStringValue(n, r)));
+    }
+
+    public void Modify(List<IRun> runs) {
+      var groups = runs.GroupBy(r => GetValues(GroupBy, r).ToString()).ToList();
+      runs.Clear();
+      foreach (var group in groups) {
+        var run = new Run { Name = string.Format(group.Key) };
+        foreach (var r in group) {
+          var prefix = GetStringValue(Prefix, r);
+          foreach (var result in r.Results) {
+            InsertNew(run.Results, prefix, result.Key, result.Value);
+          }
+          foreach (var parameter in r.Parameters) {
+            InsertNew(run.Parameters, prefix, parameter.Key, parameter.Value);
+          }
+        }
+        runs.Add(run);
+      }
+    }
+
+    private static void InsertNew(IDictionary<string, IItem> dict, string prefix, string key, IItem value) {
+      if (prefix == null)
+        prefix = "<null>";
+      var n = 0;
+      var name = string.Format("{0}.{1}", prefix, key);
+      while (dict.ContainsKey(name)) {
+        name = string.Format("{0}_{1}.{2}", prefix, ++n, key);
+      }
+      dict.Add(name, value);      
+    }
+  }
+}
Index: branches/GP.Grammar.Editor/HeuristicLab.Optimization/3.3/RunCollectionSorter.cs
===================================================================
--- branches/GP.Grammar.Editor/HeuristicLab.Optimization/3.3/RunCollectionSorter.cs	(revision 6784)
+++ branches/GP.Grammar.Editor/HeuristicLab.Optimization/3.3/RunCollectionSorter.cs	(revision 6784)
@@ -0,0 +1,108 @@
+﻿using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Data;
+using HeuristicLab.Parameters;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Optimization {
+  [Item("RunCollection Sorter", "Sorts a run collection according the specified key variable.")]
+  [StorableClass]
+  public class RunCollectionSorter : ParameterizedNamedItem, IRunCollectionModifier {
+
+    public override bool CanChangeName { get { return false; } }
+    public override bool CanChangeDescription { get { return false; } }
+
+    #region Parameters
+    public ValueParameter<StringValue> ValueParameter {
+      get { return (ValueParameter<StringValue>)Parameters["Value"]; }
+    }
+    #endregion
+
+    private string Value { get { return ValueParameter.Value.Value; } }
+
+    #region Construction & Cloning
+    [StorableConstructor]
+    protected RunCollectionSorter(bool deserializing) : base(deserializing) { }
+    protected RunCollectionSorter(RunCollectionSorter original, Cloner cloner)
+      : base(original, cloner) {
+      RegisterEvents();
+    }
+    public RunCollectionSorter() {
+      Parameters.Add(new ValueParameter<StringValue>("Value", "The variable name used as sorting key.", new StringValue("Value")));
+      RegisterEvents();
+      UpdateName();
+    }
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new RunCollectionSorter(this, cloner);
+    }
+    [StorableHook(HookType.AfterDeserialization)]
+    private void AfterDeserialization() {
+      RegisterEvents();
+    }
+    #endregion
+
+    private void RegisterEvents() {
+      ValueParameter.ToStringChanged += Parameter_NameChanged;
+    }
+
+    private void Parameter_NameChanged(object sender, EventArgs e) {
+      UpdateName();
+    }
+
+    private void UpdateName() {
+      name = string.Format("Sort by {0}", Value);
+      OnNameChanged();
+    }
+
+    private class ValueComparer : IComparer<IComparable> {
+
+      #region IComparer<IComparable> Members
+
+      public int Compare(IComparable x, IComparable y) {
+        if (x == null && y == null) return 0;
+        if (x == null) return -1;
+        if (y == null) return 1;
+        return x.CompareTo(y);
+      }
+
+      #endregion
+    }
+
+    private static readonly ValueComparer Comparer = new ValueComparer();
+
+    #region IRunCollectionModifier Members
+
+    public void Modify(List<IRun> runs) {
+      var sortedRuns = runs
+        .Select(r => new {Run = r, Key = GetValue(r)})
+        .OrderBy(r => r.Key, Comparer)
+        .Select(r => r.Run).ToList();
+      runs.Clear();
+      runs.AddRange(sortedRuns);
+    }
+
+    private IComparable GetValue(IRun run) {
+      return GetValue(run.Results) ?? GetValue(run.Parameters);
+    }
+
+    private IComparable GetValue(IDictionary<string, IItem> variables) {
+      IItem value;
+      variables.TryGetValue(Value, out value);
+      var intValue = value as IntValue;
+      if (intValue != null)
+        return intValue;
+      var doubleValue = value as DoubleValue;
+      if (doubleValue != null)
+        return doubleValue;
+      if (value != null)
+        return value.ToString();
+      return null;
+    }
+
+    #endregion
+  }
+}
